Users are the people in an organization: the practitioners who offer appointments, the staff who help them, and the patients who book. These endpoints create and maintain them, and control whether a patient can sign in.

Roles

role Who Can sign in Takes a seat
doctor A practitioner who offers appointments. Always. Yes.
medical_assistant Staff who support practitioners. Always. No.
admin An administrator of the organization. Always. No.
patient A person who books appointments. Only with login_enabled: true. No.

The role is set when the user is created and cannot be changed afterwards (422, code immutable). Other roles in MeetOne, such as agents and super admins, are not part of the API, and neither are the service accounts behind API tokens: they never appear in lists.

The user object

Field Type Description
id string (uuid) MeetOne's ID of the user.
external_id string, nullable Your key for the user. Unique within the organization. See Identifiers.
email string The email address, stored in lower case.
role string (enum) doctor, medical_assistant, patient or admin.
login_enabled boolean Whether the user can sign in. See Login control.
organization_owner boolean Whether the user owns the organization. Only practitioners and admins can.
locale string, nullable The user's language, e.g. de or en.
organization object, nullable The organization, with id and external_id.
profile object The user's profile.
deleted_at string (date-time), nullable Set when the user has been deactivated.
created_at, updated_at string (date-time) Timestamps.

The profile

Every role has one profile object with the same field names. Not every role has every field: a field the role does not have is null when you read it and returns 422 (code unsupported_for_role) when you send it. A field that is not part of the profile at all returns 422 (code unknown_attribute).

Field Type doctor medical_assistant patient admin
first_name, last_name string Required Required Required Required
name_title string Yes Yes Yes No
gender male or female Yes Yes Yes No
date_of_birth date (YYYY-MM-DD) Yes Yes Yes No
mobile_number, land_line_number string Yes Yes Yes No
address_street, address_street_number, address_zipcode, address_city, address_country string Yes No Yes No
insurance_company, insurance_number string No No Yes No
insurance_type gkv, pkv, self_pay or bg No No Yes No

List and show users

GET /api/v1/users and GET /api/v1/users/{id}

Query parameter Type Description
role string Only users with this role. An unknown role returns 422.
email string Only the user with this email address.
login_enabled boolean Only users who can (true) or cannot (false) sign in.
external_id string Only the user with this external ID.
organization_id, organization_external_id string Instance tokens: only this organization's users.
updated_since string (date-time) Only users changed at or after this time. See Errors & pagination.
page, per_page integer Pagination.
curl "https://acme.meetone.io/api/v1/users?role=doctor&per_page=100" \
  -H "Authorization: Bearer $TOKEN"

Deactivated users stay in the list and keep answering GET with deleted_at set, so that a sync learns about them. Filter on deleted_at in your own code if you only want active users.

Create a user

POST /api/v1/users

Instance tokens name the organization with organization_id or organization_external_id; organization tokens create in their own organization.

Field Type Required Description
email string Yes Unique across the installation.
role string (enum) Yes See Roles.
profile object Yes At least first_name and last_name. See The profile.
external_id string No Your key for the user.
login_enabled boolean No Patients only. See Login control.
send_invitation boolean No Send the invitation email with the sign-in link. Default false.
organization_owner boolean No Make the user an owner of the organization. Practitioners and admins only; for others it returns 422 (not_allowed_for_role).
locale string No The user's language, e.g. de.
curl -X POST "https://acme.meetone.io/api/v1/users" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "mary.major@example.com",
    "role": "patient",
    "external_id": "pat-1001",
    "locale": "de",
    "profile": {
      "first_name": "Mary",
      "last_name": "Major",
      "date_of_birth": "1990-04-02",
      "address_city": "Berlin",
      "insurance_type": "pkv"
    }
  }'

A successful request returns 201 Created:

{
  "data": {
    "id": "e41c8a0f-6b9d-4d27-a3f5-90c2b7d1e8a4",
    "external_id": "pat-1001",
    "email": "mary.major@example.com",
    "role": "patient",
    "login_enabled": false,
    "organization_owner": false,
    "locale": "de",
    "organization": { "id": "3f6c1a52-8d4e-4b7a-9e21-5c0d7a4b1e90", "external_id": "crm-4711" },
    "profile": {
      "name_title": null,
      "first_name": "Mary",
      "last_name": "Major",
      "gender": null,
      "date_of_birth": "1990-04-02",
      "mobile_number": null,
      "land_line_number": null,
      "address_street": null,
      "address_street_number": null,
      "address_zipcode": null,
      "address_city": "Berlin",
      "address_country": null,
      "insurance_company": null,
      "insurance_number": null,
      "insurance_type": "pkv"
    },
    "deleted_at": null,
    "created_at": "2026-10-08T10:15:00+02:00",
    "updated_at": "2026-10-08T10:15:00+02:00"
  }
}

Accounts have no password. A user who can sign in does so with a link sent to their email address. Patients get their message thread where messaging is enabled for the organization.

Email uniqueness

An email address is unique across the whole installation, not only within one organization. If it is already in use, the response is 422 with the code taken and the attribute email, and nothing about the existing account:

{
  "errors": [
    { "code": "taken", "attribute": "email", "message": "Email has already been taken" }
  ]
}

Seats

Practitioners (doctor) take a seat of the organization. Once all seats of a paid plan are in use, creating another practitioner returns 422 with the code seats_limit_reached and the attribute organization. Organizations in a trial can add practitioners without a limit. Medical assistants, admins and patients never take a seat.

Login control

login_enabled decides whether a user can sign in.

Role login_enabled
patient Optional. Defaults to false: a patient created through the API cannot sign in. Set true to allow it.
doctor, medical_assistant, admin Always true. Sending false returns 422 with the code required_for_role.

A patient without a login is a normal patient in every other respect. They appear in patient lists, can be booked, and still receive the emails about their appointments, including the link to join the video call. They just cannot sign in.

To enable the login later, send PATCH with login_enabled: true. To disable it, send login_enabled: false: the user is signed out everywhere at once and can no longer sign in, but still receives appointment emails.

Invitations

With send_invitation: true, a user who can sign in receives the invitation email with a sign-in link. It works on POST (for a patient together with login_enabled: true) and on PATCH, for example to switch a patient's login on and invite them in one request. A PATCH with send_invitation: true also sends the invitation again to a user whose login was already enabled:

curl -X PATCH "https://acme.meetone.io/api/v1/users/e41c8a0f-6b9d-4d27-a3f5-90c2b7d1e8a4" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "login_enabled": true, "send_invitation": true }'

send_invitation is not stored and is not part of the user object. It has no effect for a user who cannot sign in: no email is sent and no error is returned.

Update a user

PATCH /api/v1/users/{id}

Send only the fields you want to change. profile fields are merged into the existing profile. The role cannot change. When a patient's email changes, the appointments of that organization follow the new address (appointments the same address has in other organizations keep theirs), and the previous address receives an email-change notification. A deactivated user cannot be changed and returns 404.

curl -X PATCH "https://acme.meetone.io/api/v1/users/e41c8a0f-6b9d-4d27-a3f5-90c2b7d1e8a4" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "profile": { "mobile_number": "+49 30 1234567" } }'

The response is 200 OK with the updated user.

Deactivate a user

DELETE /api/v1/users/{id}

Deactivates the account: the user can no longer sign in and disappears from the app's lists. The response is 204 No Content. The data of the account is erased after the retention period; for patients that is the statutory retention of clinical documentation.

A user with confirmed appointments still ahead, as practitioner or as patient, returns 409 Conflict with the code future_appointments. Cancel those appointments first (see Appointments endpoints); the API does not decide for you whether the patient hears about it.

{
  "errors": [
    { "code": "future_appointments", "attribute": null, "message": "Confirmed appointments are still ahead. Cancel them first." }
  ]
}

A deactivated user stays readable with deleted_at set, but cannot be changed (404) or used for new bookings and availabilities.

Questions? Contact us at support@meetone.io.