Authentication
The API uses an OAuth2-style scheme. Applications authenticate with their
clientId/clientSecret to obtain access tokens, which are sent as Bearer
tokens on each /api/v1 request.
Partner authentication is completely separate from end-user session authentication. A partner credential can never be used as an end-user session, and vice versa.
Flows
Authorization Code (PHI)
Use this whenever your app needs to read a user’s health data. A user must approve the requested scopes before any token bound to that user is issued.
1. POST /oauth/authorize (client_id, redirect_uri, scope, state)
→ creates a pending approval; the user is sent to the in-app approval screen
2. User approves in the DPerspective app (>= 1 scope)
→ you receive: redirect_uri?code=<single-use code>&state=<state>
3. POST /oauth/token (grant_type=authorization_code, code,
client_id, client_secret, redirect_uri)
→ { access_token, refresh_token, expires_in, scope }The authorization code is single-use and expires no later than 600
seconds after issuance. The redirect_uri at token exchange must match the
one used at authorize. If the user denies, or approves zero scopes, no code
or token is issued.
Token lifecycle
| Credential | Max lifetime | Notes |
|---|---|---|
| Authorization code | 600 seconds | Single-use; bound to one client + user + scopes + redirect URI. |
| Access token | 3600 seconds (1 hour) | Bound to exactly one clientId, at most one userId, and the granted scope set. |
| Refresh token | 30 days | Only usable while the underlying consent grant is active and unexpired. |
Using an access token
GET /api/v1/medications
Authorization: Bearer <access_token>If the token is expired, the request is rejected with TOKEN_EXPIRED (HTTP
401). Any other authentication failure returns a uniform UNAUTHORIZED (HTTP
401) with an identical body and headers, so client existence is never
disclosed.
Refreshing a token
POST /oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"refresh_token": "<refresh_token>",
"client_id": "<clientId>",
"client_secret": "<clientSecret>"
}A refresh is rejected if the underlying consent grant has been revoked or expired — even if the refresh token itself has not yet reached its own expiry.
Revoking a token
POST /oauth/revoke
Content-Type: application/json
{ "token": "<access_or_refresh_token>", "client_id": "<clientId>", "client_secret": "<clientSecret>" }When a token (or its grant) is revoked, all derived tokens are invalidated within 60 seconds.
Secret handling
- Client secrets and tokens are persisted only as SHA-256 hashes, never in plaintext.
- A secret is shown in plaintext exactly once at issuance; afterward only the last 4 characters are shown.
- Rotate with
POST /developer/apps/:clientId/rotate-secret(owner-authenticated). Rotation immediately invalidates the previous secret.