---
title: "Register a new operator"
method: POST
path: "/api/operators/register"
tags: ["operators"]
---

# Register a new operator

`POST /api/operators/register`

## Request

- Base URL: `https://api.corabea.it` (the document declares no server; this is the origin it was published from)
- URL: `https://api.corabea.it/api/operators/register`
- Auth: HTTP bearer

Creates the Keycloak account with a generated password and the operator role, enabled and with the email taken as verified, then the users and operators rows; the operator starts in onboarding and must change the password at first login. The password goes only to the PEC, with the blank contract: `contract_pec_sent` / `contract_pec_error` say how that went, and registration stands either way. 400 when the email is not on the company domain or the PEC is not an address; 409 when the email is already an account or its company mailbox already exists. POST /operators converts an existing user instead.

## Request body

- OperatorRegisterRequest: Create the account and the operator in one go: a Keycloak user with a generated password and the operator role, enabled, plus the users and operators rows. `user_id` and `status` are ignored — the user is new and starts in onboarding.
  - `user_id` string, uuid, nullable
  - `vat_code` string, nullable
  - `ssn_code` string, nullable
  - `pec` string, required: Where the contract and the credentials are sent
  - `tax_regime` string, nullable
  - `billing_name` string, nullable: Billing name (ragione sociale) used for invoicing; when set it replaces first/last name on Zucchetti
  - `profession` string, nullable
  - `iban` string, nullable
  - `work_place` string, nullable
  - `bio` string, nullable
  - `professional_registration_number` string, nullable
  - `professional_section` string, nullable
  - `regional_professional_order` string, nullable
  - `status` string, nullable
  - `status_reason` string, nullable
  - `professional_insurance_expires_at` string, date, nullable
  - `accepts_new_patients` boolean, nullable
  - `in_person_availability` boolean: Disponibile per pazienti in presenza: declared profile preference, independent of calendar availability
  - `default_appointment_cost` number, nullable
  - `operator_cost` number, nullable
  - `max_patients` integer, nullable
  - `call_duration` integer, nullable
  - `presentation_video` string, nullable
  - `spoki_key` string, nullable
  - `spoki_email` string, nullable
  - `slack_user_id` string, nullable
  - `email` string, required: Login email: the future company address nome.cognome@corabea.it
  - `first_name` string, required
  - `last_name` string, required
  - `phone` string, nullable

## Response `201`

Successful Response

- OperatorRegisterResponse: The new operator, and whether the contract PEC with the credentials went out.
  - `user_id` string, uuid, nullable
  - `vat_code` string, nullable
  - `ssn_code` string, nullable
  - `pec` string, nullable
  - `tax_regime` string, nullable
  - `billing_name` string, nullable: Billing name (ragione sociale) used for invoicing; when set it replaces first/last name on Zucchetti
  - `profession` string, nullable
  - `iban` string, nullable
  - `work_place` string, nullable
  - `bio` string, nullable
  - `professional_registration_number` string, nullable
  - `professional_section` string, nullable
  - `regional_professional_order` string, nullable
  - `status` string, nullable
  - `status_reason` string, nullable
  - `professional_insurance_expires_at` string, date, nullable
  - `accepts_new_patients` boolean, nullable
  - `in_person_availability` boolean: Disponibile per pazienti in presenza: declared profile preference, independent of calendar availability
  - `default_appointment_cost` number, nullable
  - `operator_cost` number, nullable
  - `max_patients` integer, nullable
  - `call_duration` integer, nullable
  - `presentation_video` string, nullable
  - `spoki_key` string, nullable
  - `spoki_email` string, nullable
  - `slack_user_id` string, nullable
  - `id` string, uuid, required
  - `created_at` string, nullable
  - `updated_at` string, nullable
  - `has_in_person_availability` boolean
  - `assignments_blocked_at` string, date-time, nullable: Set while an admin or supervisor blocks new patient assignments on this expert
  - `assignments_block_reason` string, nullable: Why new assignments are blocked; shown wherever the expert cannot be picked
  - `user` OperatorUserResponse: Response model for operator user information.
    - `id` string, uuid, nullable: User UUID
    - `first_name` string, nullable: User first name
    - `last_name` string, nullable: User last name
    - `email` string, nullable: User email
    - `roles` string[]: Keycloak realm roles
    - `tax_code` string, nullable: User tax code
    - `phone` string, nullable: User phone
    - `home_address` string, nullable: User address
    - `city` string, nullable: User city
    - `province` string, nullable: User province
    - `postal_code` string, nullable: User postal code
    - `date_of_birth` string, nullable: Date of birth (YYYY-MM-DD)
    - `place_of_birth` string, nullable: Place of birth (comune or foreign country)
    - `gender` string, nullable: User gender
    - `created_at` string, nullable: User creation timestamp ISO
  - `supervisor_names` string, nullable
  - `assigned_patients_count` integer, nullable: Patients with this operator as psychologist or nutritionist; list and profile only
  - `coordinator_names` string, nullable: Comma-separated coordinators of the operator's supervisors; with include_supervisors only
  - `handover_id` string, uuid, nullable: The operator's riassegnazione, if any. Open or closed: `handover_closed_at` is what says which.
  - `handover_due_at` string, nullable: Due time of that riassegnazione, when it has one.
  - `handover_closed_at` string, nullable: When it was closed. Null means open, so the section is still active for the operator and they cannot be deactivated yet.
  - `handover_kind` string, nullable: departure | return, for that riassegnazione.
  - `handover_ends_at` string, nullable: End of the leave, when it is a temporary replacement — 'in sostituzione fino al'.
  - `contract_pec_sent` boolean
  - `contract_pec_error` string, nullable: Why the PEC failed; the Contratti tab can send it again

## Other responses

- `422`: Validation Error

## Changes

- **2026-09-26** `f23730d7b735`: 3 breaking, 1 warning, 2 info
  - the request property `pec` became required
  - request property `pec` list-of-types was narrowed by removing types `null` from media type `application/json`
  - the `pec` request property's minLength was increased from `0` to `3`
  - removed the request property `password`
  - …2 more
- **2026-09-18** `2af59bd2c0e6`: 2 info
  - added the optional property `assignments_block_reason` to the response with the `201` status
  - added the optional property `assignments_blocked_at` to the response with the `201` status
- **2026-09-15** `ab501b8b91e7`: 2 info
  - added the new optional request property `in_person_availability`
  - added the optional property `in_person_availability` to the response with the `201` status
- **2026-09-11** `969efd5098c9`: 2 info
  - added the optional property `handover_ends_at` to the response with the `201` status
  - added the optional property `handover_kind` to the response with the `201` status
- **2026-09-11** `6afa01417695`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/corabea/apis/corabea-api/changes/api/operators/register/post.md)

---

[API](https://skmtc.dev/corabea/apis/corabea-api.md) · [All operations](https://skmtc.dev/corabea/apis/corabea-api/llms.txt) · [OpenAPI document](https://skmtc.dev/corabea/apis/corabea-api/revisions/4a8dd89893b8?raw)
