Skip to content
Last updated

Authentication

The Dentally NexGen API authenticates requests using OAuth 2.0 bearer tokens. Integrations call the API from a server (backend jobs, services, middleware) — there is no browser-based or end-user login flow for API access.

This page applies whether you are an approved partner, a practice connecting your own systems, or evaluating an integration before you apply.

Getting access

API credentials are not self-service. How you obtain them depends on who you are:

Who you areHow to get credentials
Partner (building a product for multiple practices)Apply through the Dentally partnership program. After approval and onboarding, Dentally issues a client_id and client_secret for your integration.
Practice (connecting Dentally to systems you operate)Enrol in the Practice Integration Program (PIP). Dentally issues credentials scoped to your practice as part of that process.

Store client_id and client_secret as secrets. Do not embed them in client-side code, mobile apps, or public repositories.

Partner products and subscriptions

A product is a single service or product managed by a partner. Partners may have multiple products. When a partner is onboarded, client credentials are issued which are used to access the NexGen APIs. Each product has a single set of these credentials, consisting of a client id and secret. Each Partner product can have a different set of API scopes and permissions. This is to govern data access ensuring only necessary data is shared.

Subscriptions are what authorizes a partner to access a practice's data, and are managed per practice. The practice grants permission for a partner to access their data. Access is then provisioned by the Dentally Partnerships team.

Obtaining a token

Request an access token with the OAuth 2.0 client credentials grant. POST to the token endpoint on the same API host you call for data:

POST {api_base}/v2/oauth/token

For UK sandbox, {api_base} is https://api.sandbox.dentally.co, so the token URL is https://api.sandbox.dentally.co/v2/oauth/token. Use the base URL for the environment and region you will call (see Audience).

The audience must match the API identifier for that environment and region:

Example request (UK sandbox):

curl --request POST \
  --url 'https://api.sandbox.dentally.co/v2/oauth/token' \
  --header 'Content-Type: application/json' \
  --data '{
    "grant_type": "client_credentials",
    "client_id": "<client_id>",
    "client_secret": "<client_secret>",
    "audience": "https://api.sandbox.dentally.co"
  }'
FieldNotes
grant_typeMust be client_credentials.
client_idIssued during onboarding.
client_secretIssued during onboarding.
audienceMatches the API identifier for the environment and region you will call. See Audience.

Audience

EnvironmentRegionAudience
SandboxAPAChttps://api.apac-sandbox.apac.dentally.com
SandboxUKhttps://api.sandbox.dentally.co
ProductionAPAChttps://api.apac.dentally.com
ProductionUKhttps://api.dentally.co

A token issued for one audience is not accepted by another. Call the API host that matches the audience you requested.

JWT issuer

Access tokens are JWTs. The iss (issuer) claim identifies Dentally's authorization service. The token endpoint lives into each API host, but the issuer values do not:

API environmentsIssuer (iss)
Sandbox and Productionhttps://auth.api.dentally.com

If you validate JWTs locally (signature, issuer, or audience), keep using these issuer URLs. Do not derive iss from the host you POST to for /v2/oauth/token.

Successful response

A successful response includes a short-lived JWT access token and the scopes granted to that token:

{
  "access_token": "<jwt>",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "appointment:read patient:write"
}
FieldNotes
access_tokenJWT to send as the bearer token on API requests.
token_typeAlways Bearer.
expires_inLifetime of the access token in seconds.
scopeSpace-separated list of scopes granted to this token.

Access tokens are valid for 24 hours (86,400 seconds). The expires_in field in the response reflects this lifetime. Request a new token before the current one expires — expired tokens are rejected with 401 Unauthorized (see Failure modes).

Cache the token until it expires (or until shortly before expiry) and reuse it across requests. Requesting a new token on every API call is unnecessary and may hit rate limits. See Rate limiting for API request limits and how to handle 429 responses.

Request headers

Send the access token on every API call:

Authorization: Bearer <jwt>
Dentally-API-Version: 2026-01-01
X-Practice-Id: <practice-uuid>
HeaderRequiredNotes
AuthorizationyesBearer scheme. Value is the JWT from the token endpoint.
Dentally-API-VersionrecommendedPin a dated revision. See Versioning.
X-Practice-IdyesUUID of the practice the request acts on.

Scopes

The token response includes a scope field listing the permissions granted to your credentials. Each operation in the API reference also lists the scopes required to call it. Scopes reflect what your integration is approved to do (for example appointment:read or patient:write). If your token lacks the scope required by an operation, the API responds with 403 Forbidden.

The set of scopes available to you is fixed at onboarding. If you need additional access, contact the APIs & Partnerships team.

Practice context

Dentally is multi-tenant: every API request is scoped to a single practice via X-Practice-Id. Send the UUID of the practice you are acting on. Each request targets one practice at a time.

Your integration must only call the API for practices you are authorised to access (for example practices that have subscribed to your product, or your own practice under PIP). A valid token alone is not enough — the gateway also checks that your integration may access the practice named in the header.

Failure modes

401 Unauthorized

Returned when credentials are missing, malformed, or expired — for example no Authorization header, an invalid JWT, or an expired access token.

{
  "type": "https://httpstatuses.io/401",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Missing or invalid bearer token.",
  "instance": "/v2/sites",
  "traceId": "00-84c1fd4063c38d9f3900d06e56542d48-85d1d4-00"
}

Obtain a fresh token from the token endpoint and retry.

403 Forbidden

Returned when the token is valid but the request is not permitted — for example your integration lacks the required scope, or you are not authorised to access the practice given in X-Practice-Id.

Do not retry blindly. Confirm the practice ID, your approved scopes, and that the practice has granted your integration access.

See Errors for the full list of error responses and handling guidance.