Skip to main content

Overview

All API endpoints require authentication using an access token provided as a Bearer token in the Authorization header.

Obtaining an Access Token

Submit your clientId and clientSecret to the IAM endpoint using a POST request with the client_credentials grant type.
curl -X POST "https://test.didxtech.com/iam/realms/product-hub/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=your_client_id&client_secret=your_client_secret"

Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIsI...",
  "expires_in": 300,
  "refresh_expires_in": 0,
  "token_type": "Bearer",
  "not-before-policy": 0,
  "scope": "email profile"
}

Using the Access Token

Include the token in the Authorization header for all subsequent API requests:
-H "Authorization: Bearer <access_token>"

Token Expiry and Caching

Access tokens expire after 300 seconds (5 minutes). There is no refresh token — you must request a new token when the current one expires.
This short expiry is unusual and will catch you if you don’t plan for it. Here’s the recommended pattern:

Token Caching Pattern

Cache the token and refresh it proactively before it expires. Don’t request a new token for every API call.
let cachedToken = null;
let tokenExpiresAt = 0;

async function getAccessToken() {
  const now = Date.now();

  // Refresh 30 seconds before expiry to avoid race conditions
  if (cachedToken && now < tokenExpiresAt - 30000) {
    return cachedToken;
  }

  const response = await fetch(
    "https://test.didxtech.com/iam/realms/product-hub/protocol/openid-connect/token",
    {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        grant_type: "client_credentials",
        client_id: process.env.YOID_CLIENT_ID,
        client_secret: process.env.YOID_CLIENT_SECRET,
      }),
    }
  );

  const data = await response.json();
  cachedToken = data.access_token;
  tokenExpiresAt = now + data.expires_in * 1000;
  return cachedToken;
}
import time
import requests

_cached_token = None
_token_expires_at = 0

def get_access_token():
    global _cached_token, _token_expires_at

    now = time.time()

    # Refresh 30 seconds before expiry
    if _cached_token and now < _token_expires_at - 30:
        return _cached_token

    response = requests.post(
        "https://test.didxtech.com/iam/realms/product-hub/protocol/openid-connect/token",
        data={
            "grant_type": "client_credentials",
            "client_id": YOID_CLIENT_ID,
            "client_secret": YOID_CLIENT_SECRET,
        },
    )

    data = response.json()
    _cached_token = data["access_token"]
    _token_expires_at = now + data["expires_in"]
    return _cached_token

Key Rules

RuleDetail
Cache tokensDon’t request a new token for every API call
Refresh proactivelyRequest a new token ~30 seconds before expiry
No refresh tokensrefresh_expires_in: 0 means you must do a full client_credentials exchange each time
Handle 401sIf you get a 401, your token has expired — request a new one and retry

Token Lifecycle

FieldValueDescription
expires_in300Token validity in seconds (5 minutes)
refresh_expires_in0No refresh token support
token_typeBearerUse as a Bearer token in the Authorization header

Next Steps

With authentication working, choose your integration path:

Issue Credentials

For learning and impact partners who need to credential youth

Verify Credentials

For employers and opportunity providers who need to verify youth