---
title: "Team and prescriber access"
description: "Invite people, manage practice access, and maintain prescriber profiles and licenses."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.affinityrx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Team and prescriber access

A practice key manages its practice. A platform key manages its connected practices.
Use `team:read` for discovery and `team:write` for changes.

Team has one overview and three collections. All collection endpoints support cursor pagination.

| Endpoint                                          | Purpose                                                            |
| ------------------------------------------------- | ------------------------------------------------------------------ |
| `GET /v1/practices/{practiceId}/team`             | Counts of members, invitations, and prescribers                    |
| `GET /v1/practices/{practiceId}/team/members`     | Account roster, roles, location access, and prescriber connections |
| `GET /v1/practices/{practiceId}/team/prescribers` | Clinical profiles and licenses, with state and review filters      |
| `GET /v1/practices/{practiceId}/team/invitations` | Invitations and your integration's onboarding state                |

Memberships and clinician credentials are shared between Test and Live. A Test key can change the same Team records as a Live key.
Use synthetic practices and people when testing Team changes.
Your integration's external identities remain separate for each mode.

## Invite a person

Send [Invite team member](/api/invitePracticeTeamPerson/) from your backend.

```json
{
  "externalId": "provider_48291",
  "email": "jamie@example.test",
  "name": "Avery All-States Test",
  "role": "prescriber",
  "npi": "1234567893"
}
```

This example uses synthetic Test identity data. Replace it with the intended person for a Live invitation.

Choose `owner`, `administrator`, `prescriber`, or `tester` directly. You do not need to discover a role ID first.
The reference includes optional profile and license fields.

Store `person.id` and `person.invitation.id` from the response.
Reuse `externalId` and the same person details when retrying creation.
An accepted invitation does not change existing access.

The recipient accepts with their verified Affinity account. An NPI identifies a clinician; it does not authenticate an account.

## Track invitations

Use [List invitations](/api/listPracticeTeamInvitations/) with `externalId=provider_48291` to find your integration's invitation.
You can also filter by exact `email` or `status`: `pending`, `expired`, `accepted`, or `revoked`.
Use [Get invitation](/api/getPracticeTeamInvitation/) to follow one invitation and its `person.nextActions`.

- [Resend invitation](/api/resendPracticeTeamInvitation/) keeps the same invitation ID and replaces the previous link.
- [Revoke invitation](/api/revokePracticeTeamInvitation/) prevents acceptance and preserves history.
- An accepted invitation requires a member access update instead of revocation.

Resend supports pending and expired invitations. The new link expires in seven days.
If delivery returns `502`, the invitation remains saved. Retry resend after resolving the delivery error.

The directory includes invitations sent from Clinic.
Their `userId`, `externalId`, and `person` are null unless they belong to your integration and mode.

## Manage locations

Use [List locations](/api/listPracticeLocations/) to find each practice site's name and `loc_*` identifier.
Use `status=active` when selecting locations for a new Team invitation.

[Create location](/api/createPracticeLocation/) adds a site with a unique name. Timezone is optional; omit it unless an integration needs an explicit override.
[Read location](/api/getPracticeLocation/) returns one site's current details.
[Update location](/api/updatePracticeLocation/) changes supplied fields and preserves omitted fields.
[Archive location](/api/archivePracticeLocation/) retains the site and its historical associations.

Reads require `locations:read`. Writes require `locations:write` and `Idempotency-Key`.
Add these scopes to a new practice or platform key in the dashboard.
Existing keys do not gain these permissions automatically.
Locations are shared between Test and Live for the same practice.

## Manage member access

Use [List members](/api/listPracticeTeamMembers/) to search names or email addresses with `search`.
Filter with `role` and `status=active` or `status=disabled`.
[Get member](/api/getPracticeTeamMember/) returns one member's current access and account connection.

Send [Update member access](/api/updatePracticeTeamMember/) with the fields you want to change:

```json
{
  "role": "administrator",
  "locationIds": [],
  "status": "active"
}
```

A supplied role replaces existing role assignments. Omitted fields stay unchanged.
An empty `locationIds` array grants all practice locations.
Use [List locations](/api/listPracticeLocations/) to find location names and IDs.
Supply active location IDs to restrict access to selected practice locations.

Set `status` to `disabled` to revoke practice access while preserving clinical records.
Set it to `active` to restore access.
Ownership changes require an active owner's personal API key or a Clinic session.
An owner must stay active with access to all locations. Promote another active owner before demoting the final owner.

Sign-in email, passwords, and account security remain in the person's Affinity account settings.

## Find prescribers

Use [List prescribers](/api/listPracticeTeamPrescribers/) with these filters:

| Filter   | Values                                      |
| -------- | ------------------------------------------- |
| `search` | Part of the display name or legal name      |
| `npi`    | Exact NPI                                   |
| `state`  | Two-letter license state, such as `TX`      |
| `status` | Practice connection: `active` or `inactive` |

[Get prescriber](/api/getPracticeTeamPrescriber/) returns the clinical profile and all submitted licenses, including their IDs.
License records are optional clinician profile information. Signing requires an active account connection, Live practice access, and prescription eligibility; missing license records do not block headless or Clinic signing. The practice or EMR remains responsible for clinician credential verification.

## Update a profile or license

Profile and license changes require an active, accepted prescriber account connection and active membership in the practice.
A pending invitation alone cannot authorize edits to the clinician's shared records.

Use [Update prescriber](/api/updatePracticeTeamPrescriber/) for `displayName`, `legalName`, `credentials`, `phone`, or `address`.
Omitted fields stay unchanged. NPI is immutable.

Use [Add license](/api/createPracticeTeamLicense/) to submit a state, license number, and optional future expiration.
An exact repeat returns the existing license.

Use [Update license](/api/updatePracticeTeamLicense/) with the license ID to correct details or renew its expiration:

```json
{
  "expiresAt": "2030-12-31T23:59:59.000Z"
}
```

This updates only that license. Other licenses stay unchanged.

Clinical profiles and licenses are shared across every practice connected to that clinician.
A change affects those practices in both Test and Live.

## Use the correct identifier

| Identifier                                              | Use                                                                    |
| ------------------------------------------------------- | ---------------------------------------------------------------------- |
| Invitation response `person.id`, or invitation `userId` | Your integration's mode-scoped person, used by orders after acceptance |
| Member `id`, or invitation `memberId`                   | Manage access to this practice                                         |
| Prescriber `id`                                         | Read or update the clinician's clinical profile                        |
| License `id`                                            | Update one submitted license                                           |

A member ID or prescriber ID cannot substitute for the integration user ID.
Each clinician needs a registered or accepted identity and current practice access.

## Register without an invitation

Use `POST /v1/practices/{practiceId}/users` with `team:write`, `Idempotency-Key`, and `identityAttestation: true`.
Supply a stable `externalId`, name, email, and role. Prescribers require an NPI.
The returned `id` is the integration `userId`; `memberId` manages practice access.
Registration preserves existing memberships and never restores disabled access.

A pending invitation by itself does not authorize API signing. The identity must have either
an accepted invitation or a separate API registration, plus active practice and prescriber access.
A clinician registered through the API can sign headlessly while a separate dashboard invitation
is still pending. Invitation acceptance is not an additional gate for that registered identity.

Test registrations require synthetic `.test` emails and Affinity Test NPIs. Live requires approved integration and practice access.
The practice is responsible for the NPI and license information it submits.

## Sign an order

Your platform authenticates the clinician and collects their attestation to the complete order.
Use the [headless signing workflow](/guides/choose-an-integration/#sign-from-your-backend) with `orders:sign`.
No Affinity login is required. Affinity checks current access, prescribing authority, and exact versions.

Source: https://docs.affinityrx.com/guides/provider-access/index.mdx
