Skip to Content
Authentication

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

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

CredentialMax lifetimeNotes
Authorization code600 secondsSingle-use; bound to one client + user + scopes + redirect URI.
Access token3600 seconds (1 hour)Bound to exactly one clientId, at most one userId, and the granted scope set.
Refresh token30 daysOnly 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.