curl --request POST \
--url https://sandbox.rx.tera.health/v1/oauth/token \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data 'scope=enrollments:read enrollments:write logs:write'{
"access_token": "trx_at_example_not_a_real_token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "enrollments:read enrollments:write logs:write"
}Get an access token
Client credentials (brand servers and prescriber systems): authenticate with HTTP Basic
(client_id:client_secret) and send grant_type=client_credentials. The token lasts 1 hour and carries the
client’s scopes, or the subset you ask for in scope.
Token exchange (patients): send the brand-signed assertion as subject_token. No client secret is needed,
so the brand’s app can make this call itself with an assertion its server just signed. The result is a
15-minute patient token bound to the one enrollment the assertion names. There is no refresh token; sign a
new assertion when it expires. Patient tokens are issued for enrollments that are pending_screening,
active or paused.
An integration client cannot get tokens from the production API (unauthorized_client).
Platform partners that act for several brands name the brand with tenant_id on the client-credentials
request, and in a tenant_id claim in patient assertions. Every token is for one brand. See “Platform
partners” in the Tenants tag.
Error codes: invalid_client, client_revoked, unauthorized_client, unsupported_grant_type, invalid_scope,
tenant_required, tenant_not_permitted, token_exchange_not_allowed, assertion_signature_invalid,
assertion_expired, assertion_lifetime_too_long, assertion_replayed, assertion_audience_invalid,
assertion_issuer_unknown, assertion_invalid, patient_not_found, enrollment_not_active.
curl --request POST \
--url https://sandbox.rx.tera.health/v1/oauth/token \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data 'scope=enrollments:read enrollments:write logs:write'{
"access_token": "trx_at_example_not_a_real_token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "enrollments:read enrollments:write logs:write"
}Authorizations
HTTP Basic with your client_id and client_secret, only on POST /v1/oauth/token for the
client_credentials grant. You may send them as client_id and client_secret form fields instead.
Body
- Option 1
- Option 2
One endpoint, two grants. grant_type picks the shape.
"client_credentials"Space-separated scopes. Omit to get every scope the client holds. Asking for one it does not hold fails with invalid_scope.
"enrollments:read enrollments:write logs:write"
The brand this token is for. Required for a platform partner's client, which acts for several brands and
gets one token per brand (tenant_required without it, tenant_not_permitted for a brand it may not act for).
A brand's own client may omit it or send its own tenant id.
"a0000000-0000-4000-8000-000000000002"
Only when not using HTTP Basic.
"demo_brand_integration"
Only when not using HTTP Basic.
"example-client-secret-not-real"
Response
Token issued.
Opaque to callers. Do not parse it.
"trx_at_example_not_a_real_token"
"Bearer"Seconds. 3600 for client tokens, 900 for patient tokens.
3600
Space-separated scopes granted. Absent on patient tokens.
"enrollments:read enrollments:write logs:write"
Token exchange only (RFC 8693).
"urn:ietf:params:oauth:token-type:access_token"
