Skip to main content

Invite a team member

Adds a practitioner to your project and, if they have no account yet, emails them a link to set a password. Use it to bring in a colleague. It does not share any patient's record.

MethodPathWho
POST/v1/projects/me/membersA project admin

What the project needs​

| Requirement | Detail | | --- | --- | --- | | Practitioner invitations on | PRACTITIONER_INVITATION_ENABLED must not be false. It is on when unset. | | A practitioner app URL | The link needs one: PRACTITIONER_APP_URL on the project, then the parent, then older settings, then the request's Origin if it is on the allowed list, then the platform default. If nothing resolves the call answers 409 and creates nothing. | | A default practitioner AccessPolicy, only if you send access | DEFAULT_PRACTITIONER_ACCESS_POLICY. | | The INVITE_TO_PROJECT email template | Mapped to your template, with a provider that is ready. |

Request​

curl --request POST \
--url 'https://api.sandbox.ovok.com/v1/projects/me/members' \
--header "Authorization: Bearer ${OVOK_TOKEN}" \
--header 'Content-Type: application/json' \
--data '{
"email": "alex@example.com",
"firstName": "Alex",
"lastName": "Lee",
"admin": false
}'
Body fieldRequiredDescription
emailYesA valid address. Ovok trims and lowercases it.
firstNameYes, but may be nullGiven name. null becomes New.
lastNameYes, but may be nullFamily name. null becomes Member.
adminYes, but may be nulltrue makes the member a project admin. null becomes false.
accessNoOne to 100 entries that limit what the member can do. See limit what the member can do.

The three "may be null" fields are still required keys. Leaving them out answers 422 with Required for each. Send null to take the default.

Limit what the member can do​

Without access, the new member has no AccessPolicy, and can reach the whole project. To limit them, send access:

{
"email": "alex@example.com",
"firstName": "Alex",
"lastName": "Lee",
"admin": false,
"access": [
{ "policy": { "reference": "AccessPolicy/care-team-policy" } }
]
}

With access, the member's base AccessPolicy is the project's default practitioner AccessPolicy, and each entry adds to it. Every entry must name an AccessPolicy of this project, written AccessPolicy/<id>. An entry can also carry parameter values (name plus either valueReference or valueString) for policies that take parameters.

Response​

The answer is 200 with the new member:

FieldExampleMeaning
id…Membership id.
emailalex@example.comNormalized email address.
displayNameAlex LeeDisplay name for the new member.
firstNameAlexGiven name.
lastNameLeeFamily name.
adminfalseWhether the member is a project admin.
profileTypePractitionerProfile resource type.
profileId…Id of the practitioner's profile.
pendingtrueRemains true until the member sets a password through the emailed link.
invitedAt2026-10-06T08:15:00.000ZInvitation timestamp.
emailVerifiedfalseBecomes true after an emailed set-password link is used; null when it could not be read.
existingAccountfalseTrue when the address could already sign in, so no email was sent.

What can go wrong​

Checks run in this order.

StatusMessage or errorCause
401The token is missing or invalid.
403Forbidden resourceThe caller is not a project admin.
422A list of path and messageA field is missing or invalid, including the three required keys above.
403Practitioner invitations are disabled for this project.PRACTITIONER_INVITATION_ENABLED is false.
404The project in the caller's token cannot be read.
409A member with this email already exists on this project.The address is already a practitioner member here.
409error invitation_not_configuredaccess was sent, but there is no valid default practitioner AccessPolicy.
400Every access entry must name an AccessPolicy of this project.An access entry names another project's policy, or one that does not exist.
409code app_url_not_configuredNo practitioner app URL resolves. Nothing was created.
409Another write holds the account. Retry.
500Failed to send invite email. Please add the user manually if the issue persists.The email could not be sent. The new account was removed.

Gotchas​

  • No access means whole-project access. Treat access as required unless you mean it. The other two invitation routes refuse a member who has no AccessPolicy, because a share would narrow their access.
  • An existing account gets no email. An address that already has a password, or belongs to another project, receives the membership silently, and existingAccount is true. Tell them yourself.
  • The placeholders are real. null names become New and Member, and those appear in the email greeting. Send real names.
  • Only an existing member of this project is a conflict. The same address in another project is fine.
  • A browser call can borrow its own Origin. When no app URL is configured, a call made from a browser app on the allowed list builds the link on that origin; a call from your server does not. Set PRACTITIONER_APP_URL so the link never depends on the caller.
  • admin: true is a full admin. Admins can invite and remove members and change settings.
  • Invitation is undone with the email. A failed send leaves no account behind, so it is safe to retry.

Manage members​

The same path family lists and changes members. All of these need a project admin except the list, which any practitioner may read.

MethodPathWhat it does
GET/v1/projects/me/membersList practitioner members. Query accessPolicyId, profileId, userId, page (from 0) and count (1 to 100, default 20).
GET/v1/projects/me/members/:membershipIdRead one member. 404 for an id in another project.
PATCH/v1/projects/me/members/:membershipIdChange admin, or replace access. 409 if it would demote the last admin or the member changed since you read it.
DELETE/v1/projects/me/members/:membershipIdRemove a member. 409 for the last admin.

These routes handle practitioner memberships only; another type answers 400.