Authentication
The Batch API uses OAuth 2.0 client credentials. Exchange your client credentials for an access token, then send the token on every request.
Authorization: Bearer eyJ...
There are no API keys and no refresh tokens. Request a new token when the current one expires.
Get a Token
curl -s -u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials" \
-d "scope=batches:write batches:read" \
https://api.amorphous.health/oauth/token
Send the client ID and secret with HTTP Basic authentication (client_secret_basic). The body is application/x-www-form-urlencoded.
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | client_credentials |
scope | No | Space-delimited scopes. Defaults to every scope on the client |
client_assertion_type | Private key JWT only | urn:ietf:params:oauth:client-assertion-type:jwt-bearer |
client_assertion | Private key JWT only | Signed JWT. See Private Key JWT |
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "batches:write batches:read"
}
| Field | Description |
|---|---|
access_token | Send as Authorization: Bearer |
token_type | Always Bearer |
expires_in | Seconds until expiry. 900 by default, configurable from 300 to 3600 per client |
scope | Scopes granted |
Cache the token and request a new one about 60 seconds before it expires. The token endpoint allows 30 requests per minute per client.
Scopes
| Scope | Grants |
|---|---|
batches:write | Create, add, upload, submit, cancel, and delete |
batches:read | Batches, documents, entities, results, and document Bundles |
system/*.rs or system/*.read | Every /fhir/R4 read (SMART v2 or v1) |
system/{Type}.rs or system/{Type}.read | Read and search one resource type, such as system/Condition.rs |
Batch routes do not accept SMART scopes, and /fhir/R4 does not accept batches:read. Patient/{ID}/$everything returns every type, so it needs system/*.rs or system/*.read.
Private Key JWT
Use private_key_jwt instead of a shared secret. Register a public key with Amorphous, as an inline JWKS or an HTTPS jwks_uri, then sign an assertion for each token request.
curl -s -X POST https://api.amorphous.health/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "scope=batches:write batches:read" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-d "client_assertion=$ASSERTION"
| Claim | Value |
|---|---|
iss, sub | Your client ID |
aud | https://api.amorphous.health/oauth/token |
exp | No more than 5 minutes after iat |
jti | Unique per assertion. A reused jti is rejected |
Supported algorithms are RS256, RS384, ES256, and ES384. SMART Backend Services clients should use RS384 or ES384.
Access Token Claims
Access tokens are JWTs signed by https://api.amorphous.health. You do not need to read them, but the claims are stable.
| Claim | Description |
|---|---|
sub | Your client ID |
organization_id | Your organization. Requests never take an organization parameter |
mode | test or live |
scope | Scopes granted |
exp | Expiry time |
Rotate Credentials
A client secret is shown once, when it is created. Store it in a secret manager. Amorphous keeps only a keyed hash and cannot recover it.
- Ask for a second secret, or publish a new key in your JWKS.
- Deploy the new credential.
- Revoke the old secret once traffic has moved.
A client can have two active secrets. Disabling a client takes effect immediately, including for tokens already issued.
Discovery
| Document | Path |
|---|---|
| Authorization server metadata | GET /.well-known/oauth-authorization-server |
| Signing keys | GET /.well-known/jwks.json |
| SMART configuration | GET /fhir/R4/.well-known/smart-configuration |