Authentication / Authorization (OAuth 2.0)

The DigAí Public API uses the OAuth 2.0 — Client Credentials Grant
(RFC 6749, section 4.4) for machine-to-machine authentication.

Instead of sending a static credential on every request, your application exchanges a
client_id + client_secret for a short-lived access token (a signed JWT) at our identity
provider, and uses that token to call the API. When the token expires, your application requests
a new one.


How it works

┌────────────┐   1. client_id + client_secret + resource    ┌─────────────────────┐
│  Your       │ ───────────────────────────────────────────▶ │  Identity provider  │
│  application│ ◀─────────── 2. access_token (JWT) ────────── │  (OAuth)            │
└────────────┘                                                └─────────────────────┘
      │
      │ 3. Authorization: Bearer <access_token>
      ▼
┌─────────────────────┐
│  DigAí Public API   │  4. validates the token's signature, issuer and audience, then responds
└─────────────────────┘
  1. Your application authenticates at the identity provider with its credentials.
  2. It receives a short-lived access_token (JWT).
  3. It sends the token in the Authorization header of every API call.
  4. The API validates the token and processes the request.

Step by step

1. Request your credentials

Before integrating, request access to the DigAí Public API through your DigAí point of contact
(or the support channel), stating which environment(s) you need (staging and/or production).

DigAí will provision a dedicated application for you and send back:

  • your client_id and client_secret;
  • the environment parameters — token endpoint (AUTH_BASE_URL), API identifier (RESOURCE)
    and API base (API_BASE_URL). See Environment parameters.

Store the client_secret in a secret manager — treat it as a long-lived secret and never expose
it in client-side code or version control.

2. Get an access token

Make a POST request to the identity provider's token endpoint:

curl --request POST \
  --url "https://<domain>/oidc/token" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "grant_type=client_credentials" \
  --data "client_id=YOUR_CLIENT_ID" \
  --data "client_secret=YOUR_CLIENT_SECRET" \
  --data "resource=https://<domain>"

Response:

{
  "access_token": "eyJhbGciOiJFUzM4NCJ9.eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": ""
}
  • access_token — the JWT to use on API calls.
  • expires_in — lifetime in seconds. Use this value to decide when to refresh; do not assume
    a fixed value.

3. Call the API

Send the token in the Authorization header:

curl --request GET \
  --url "https://api-screening.digai.ai/api/v1/public/screenings" \
  --header "Authorization: Bearer YOUR_ACCESS_TOKEN"

4. Refresh the token

The access token is short-lived. Your application should:

  • Reuse the same token while it is valid (do not request a new one on every call).
  • Request a new token shortly before it expires (e.g. 30–60s before expires_in) or when it
    receives a 401 Unauthorized.

No refresh_token is needed in the Client Credentials flow — just repeat step 2.

Environment parameters

The values below are provided by DigAí during onboarding, per environment (staging and
production):

ParameterDescription
AUTH_BASE_URLIdentity provider base (token endpoint)
RESOURCEPublic API identifier (audience)
API_BASE_URLPublic API base
client_idYour application identifier
client_secretYour application secret
🚧

Production URLs are provided separately. Never commit the client_secret to

repositories or plain-text configuration files.


Scopes

Scopes let a credential be limited to a subset of operations (least privilege). By default a
client is granted full access to its own resources; scoped access can be enabled per client on
request.

Example scope catalog:

ScopeGrants
screenings:readList and read screenings
screenings:writeCreate, update and delete screenings
questions:readRead screening questions
questions:writeCreate and update screening questions
candidates:readRead candidates and their results
candidates:writeCreate and update candidates
workspaces:readRead workspaces

When scoped access is enabled for your client, request the scopes at the token endpoint (space-
separated) and they are echoed back in the token response and enforced by the API:

curl --request POST \
  --url "https://<domain>/oidc/token" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "grant_type=client_credentials" \
  --data "client_id=YOUR_CLIENT_ID" \
  --data "client_secret=YOUR_CLIENT_SECRET" \
  --data "resource=https://<domain>" \
  --data "scope=screenings:read candidates:read"

A request to an operation outside the granted scopes returns 403 Forbidden.


Security properties (for AppSec teams)

Technical reference for the integration's security assessment:

PropertyDetail
ProtocolOAuth 2.0 Client Credentials Grant (RFC 6749 §4.4)
Token formatSigned JWT (JWS). Algorithms: ES384 / RS256
Signature verificationPublic keys exposed via JWKS at AUTH_BASE_URL/oidc/jwks
Server-side validationSignature, issuer (iss), audience (aud = Public API identifier) and expiry (exp)
Token lifetimeShort (reported in expires_in); refreshed automatically by the client
Secret exposureThe client_secret is sent only to the token endpoint, over TLS — it is never sent to the resource API
TransportHTTPS/TLS required on all endpoints
Rotation / revocationCentralized at the identity provider; credentials can be revoked or rotated with no changes to the API
IsolationEach client has its own application (dedicated client_id); credentials are not shared between partners

How this differs from a static API Key: with an API Key, the long-lived credential itself
travels on every request to the API. With OAuth 2.0, what travels to the API is an ephemeral
token; the long-lived credential (client_secret) only ever touches the token endpoint. If the
in-transit token is captured, it expires within minutes.


Error handling

StatusMeaningRecommended action
401 UnauthorizedMissing, expired or invalid tokenGet a new token and retry the request
403 ForbiddenValid token, but no access to the requested itemCheck that the resource belongs to your account
400 at token endpointInvalid client_id/client_secret/resourceReview the credentials and the resource value

Migrating from the API Key

The legacy API Key stays valid during the transition, so migration can be done with no
downtime
:

  1. Ask DigAí to create your OAuth 2.0 credentials (client_id / client_secret).
  2. Implement the access-token request (step 1) and start sending Authorization: Bearer <token>.
  3. Validate in staging.
  4. Switch authentication in production. Since the header is the same (Authorization: Bearer ...),
    the change is limited to how the token value is obtained.
  5. After migrating, ask DigAí to revoke the old API Key.