An API key identifies a backend service. It does not identify the provider who uses your platform.
Practice and platform access
A practice key with catalog:read can list its available pharmacies, medication catalog and
item-specific shipping options. The key selects the practice. If you also supply practiceId, it
must identify that practice. Catalog prices use the practice’s published price book and customer
segment.
A platform key can select an authorized practice with practiceId when it requests catalog
prices. The practice price book takes precedence over the platform’s price book. Access and
pricing remain separate from the clinician review and signing required to send an order.
Send the API key
Use the Bearer authentication scheme for new integrations.
Authorization: Bearer <api-key>The API also accepts the service key header.
x-affinity-api-key: <api-key>Send one authentication header. Sending both returns 400 invalid_request.
Select an organization
An API key uses its own organization by default.
Use acct_... for a platform, prac_... for a practice, or pharm_... for a pharmacy.
These are the same public IDs used in dashboard URLs. Private database IDs and retired org_... aliases are rejected with 400.
For delegated webhook management, a platform sends X-Affinity-Organization-Id with the owner’s public organization ID.
The practice or pharmacy must first grant that platform webhook access in the same mode.
See Delegate endpoint management.
This header does not grant permissions or change the API key’s mode. Ordinary service keys cannot use it to switch organizations on other API resources. Practice-specific routes continue to identify the practice in their URL.
Personal credentials can select an explicitly granted organization with the same header. Without the header, they use their anchor organization if it remains granted.
Attribute platform requests
Affinity attributes requests to the authenticated service account as a system actor by default. You do not need to send actor headers for autonomous work.
If a person performs a protected patient or order action, send the person’s stable user ID:
Affinity-Actor-Id: prescriber-user-id
Affinity-Actor-Type: userThe actor identifies the user in your application. The actor does not identify the API client.
If several workers share one service account and you need to distinguish them in audit records,
send both system actor headers. Do not use system when a person initiated the action.
Affinity-Actor-Id: patient-sync-worker
Affinity-Actor-Type: systemSigning and submission require orders:sign. Select the clinician with prescriber: { npi },
prescriber: { id }, or prescriber: { externalId }, or inherit the draft’s prescriber.
Actor headers are optional audit metadata for this flow. Your platform still authenticates the clinician and collects explicit signing intent.
Legacy requests using userId require the clinician’s matching externalId in user actor headers.
Do not send an email address, API key ID, signing credential, or patient information in these headers.
Actor IDs containing @ return 400 invalid_request. Use a stable opaque identifier from your application.
Pin the API version
Send the dated version in each direct HTTP request.
Affinity-Version: 2026-09-28The TypeScript SDK sends its supported version automatically.
An unsupported version returns 400 UNSUPPORTED_API_VERSION.
Without a version header, authenticated API-key requests use the version stored on the key’s service account.
Keys belonging to the same service account share that default. New service accounts default to 2026-09-28.
Existing accounts retain their stored version.
An explicit header overrides the default for that request only. It does not update the key or service account.
Two clients can use different supported versions with the same key, including concurrent requests.
The response header X-Api-Version identifies the selected contract.
The SDK sends its own version header, so its default takes precedence over the service-account default. Webhook payload versions are separate from HTTP request versions.
Handle rate limits and failures
Each API key allows 300 requests per minute. Authenticated responses include these headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests allowed in the current window. |
X-RateLimit-Remaining |
Requests remaining in the current window. |
X-RateLimit-Reset |
Window reset time as Unix seconds. |
When a request exceeds the limit, the API returns 429 with Retry-After in seconds. Wait for that interval before retrying.
Preserve the request body and Idempotency-Key when retrying an order creation. Reusing the key with different input returns 409.
Errors use Problem Details JSON with a stable code. Keep the X-Request-Id response header when reporting a failed request.
Input validation can return 400 before credential checks. This does not authenticate the caller or
run the operation. Requests with valid input and a missing or invalid key return 401.
Store the key
- Keep the key in server-side secret storage.
- Use a separate key for each service.
- Grant only the scopes that the service needs.
- Revoke a key immediately after an exposure.
Affinity shows the complete key one time. Do not copy the key into a support message.
Choose an expiration
When creating a Test or Live key, select Never expires to keep the key active until you revoke
it or its account loses access. You can also select 30 days, 90 days, or a date.
Live expiration dates must be within 90 days. Creating a key through the API with expiresAt: null
has the same effect as Never expires.
Rotate a key
Open API keys in the dashboard for the organization and mode that own the key. Select an active key, then select Rotate key.
Save the replacement secret and update your service. The dashboard allows up to 60 minutes of overlap with the previous key. An earlier expiration still applies. Revoke the previous key after you confirm requests use the replacement.
Rotation preserves the key’s permissions, IP restrictions, mode, and expiration. It does not extend access to another practice or mode.
Keep modes separate
Patient, order, and webhook data are scoped to the API key’s mode. Team membership and practice locations are shared between Test and Live for the same practice.
Do not replace a Test key with a Live key in the same running process. Restart the service with the Live configuration.