Skip to Content
DocsAccount API

Account API

This page is only for Connect with Valyd and reading a connected user’s account. These endpoints do not start any check. To run a check, use a configured workflow — see Reusable Verification and the Verification API reference.

General notes

  • Every API response carries an X-Request-Id header. Log it, and quote it when contacting support — never send API keys, tokens, or identity data.
  • All API requests must be made over HTTPS.
  • Endpoints that require authentication expect a Bearer token in the Authorization header: Authorization: Bearer YOUR_ACCESS_TOKEN.
  • If you are using the SDK, prefer the typed helpers (getAuthorizationUrl(), exchangeCode(), handleCallback(), refreshToken()) — they call these endpoints for you.
  • One API namespace: authorize, token, JWKS, UserInfo, licenses, and verifications are under https://idp.valyd.id/api/auth/oidc. Discovery is at /.well-known/openid-configuration (the /api/.well-known/... alias also works).

SDK methods (@valyd/sdk)

valyd.auth.createAuthorizationRequest({ scope, redirectUri? })

Recommended Connect entry point. Generates strong state, nonce, and an S256 PKCE verifier/challenge together. Store the returned transaction server-side and redirect to transaction.url.

valyd.auth.getAuthorizationUrl({ state, nonce, codeChallenge, scope, redirectUri? })

Low-level URL builder. state is required, and so is codeChallenge — without it the SDK (≥ 1.12) throws ValydError("pkce_required"). Prefer createAuthorizationRequest() so PKCE and nonce cannot be forgotten.

valyd.auth.exchangeCode(code, { codeVerifier, redirectUri? })

Exchanges the authorization code at POST /api/auth/oidc/token. The SDK verifies the ID token against discovery/JWKS before returning { accessToken, refreshToken, idToken, claims, expiresIn, scope, tokenType }.

valyd.auth.handleCallback(url, { transaction })

One callback call: surfaces a callback error (e.g. access_denied) as a ValydError, compares state, checks the RFC 9207 iss (issuer_mismatch), sends the PKCE verifier, exchanges the code, verifies RS256/JWKS plus issuer/audience/expiry/nonce, and fetches UserInfo.

valyd.auth.refreshToken(refreshToken)

Refreshes at POST /api/auth/oidc/token with grant_type: "refresh_token". Rotation is on — persist the returned refreshToken every time.

valyd.auth.getEndSessionUrl({ idTokenHint, postLogoutRedirectUri, state })

Builds the RP-initiated logout URL (/api/auth/oidc/logout) with your client_id. Redirect the browser to it to end the user’s Valyd session and revoke your app’s tokens.

OIDC endpoints (current — use these)

GET /api/.well-known/openid-configuration — Discovery

  • Method: GET
  • Full URL: https://idp.valyd.id/api/.well-known/openid-configuration
  • Auth: none

Standard OIDC discovery document: issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri, end_session_endpoint, supported scopes/grants/algorithms, code_challenge_methods_supported: ["S256"], response_modes_supported: ["query"], prompt_values_supported, claim_types_supported: ["normal"], authorization_response_iss_parameter_supported: true, and explicit false for request_parameter_supported, request_uri_parameter_supported, claims_parameter_supported, frontchannel_logout_supported, and backchannel_logout_supported. Point any OIDC-capable framework at this URL to auto-configure. See the OIDC integration guide for the full response.

GET /api/auth/oidc/authorize — Authorization

  • Method: GET (browser redirect) or POST (application/x-www-form-urlencoded form, OIDC Core §3.1.2.1)
  • Full URL: https://idp.valyd.id/api/auth/oidc/authorize
  • Auth: none (user authenticates interactively)

Parameters:

ParameterRequiredNotes
client_idYes
redirect_uriYesExact match with a registered redirect URI.
response_typeYescode only.
scopeYesSpace-separated, must include openid.
code_challengeYesPKCE, 43–128 base64url characters — required for every client, public and confidential.
code_challenge_methodYesS256 (plain is not supported).
stateRecommendedEchoed back verbatim — only if you sent it. No minimum length.
nonceRecommendedCopied into the id_token — only if you sent it.
promptNoSpace-separated none / login / consent / select_account (none can’t be combined). none → login_required or consent_required instead of UI; login / select_account force re-authentication; consent forces the consent screen, even for first-party apps.
max_ageNoSeconds; forces re-authentication if the last real sign-in is older. The id_token always has auth_time.
id_token_hintNoWith prompt=none, a different signed-in user → login_required.
login_hintNoPassed through to the sign-in screen.
resourceNoRFC 8707 resource indicator — becomes the access token aud.
response_modeNoquery only.
request / request_uri—Not supported (request_not_supported / request_uri_not_supported).

On success, Valyd redirects to your redirect_uri with ?code=...&state=<your state>&iss=https://idp.valyd.id (RFC 9207 — verify iss equals the issuer). Once client_id and redirect_uri are verified, every error is also redirected: ?error=...&error_description=...&state=...&iss=... (e.g. access_denied when the user presses Cancel or isn’t assigned to a private org app). Only an unknown client or an unregistered redirect_uri shows an HTML error page instead. See Errors.

POST /api/auth/oidc/token — Token (exchange + refresh)

  • Method: POST
  • Full URL: https://idp.valyd.id/api/auth/oidc/token
  • Auth: client credentials in the body (client_secret_post) or HTTP Basic (client_secret_basic, credentials form-urlencoded per RFC 6749 §2.3.1) — never both (invalid_request). Public first-party clients use none + PKCE.
  • Required headers: Content-Type: application/x-www-form-urlencoded (JSON is also accepted)

Two grants:

grant_typeBody fields
authorization_codeclient_id, client_secret, code, redirect_uri (exact match), code_verifier (required)
refresh_tokenclient_id, client_secret, refresh_token, optional scope (narrow only — widening → invalid_scope)

Returns a standard top-level token JSON (no data wrapper):

{ "access_token": "eyJhbGciOi...", "refresh_token": "rfrsh_abc123...", "id_token": "eyJhbGciOiJSUzI1NiIs...", "token_type": "Bearer", "expires_in": 900, "scope": "openid profile verifications" }

Errors are RFC 6749 §5.2 JSON with Cache-Control: no-store — error is a string:

{ "error": "invalid_grant", "error_description": "Invalid or expired authorization code" }

invalid_request / invalid_grant / invalid_scope / unsupported_grant_type → 400; invalid_client → 401 (with WWW-Authenticate: Basic when you used HTTP Basic).

Notes:

  • Authorization codes are single-use and client-bound — exchange immediately. Replaying a code is rejected and revokes every token it already produced.
  • The id_token is an RS256 JWT; validate it against the JWKS below and check its nonce claim (present only if you sent one).
  • The access_token is an RFC 9068 JWT (typ: at+jwt) whose sub is the user’s valyd_… id — the same as the ID token and UserInfo.
  • Refresh rotation is on for every refresh: the refresh_token you sent is revoked and a new one is returned — always persist the new value. Replaying a rotated-away token revokes every refresh token for that user and client.
  • The returned access_token works on all resource endpoints below (/userinfo, /licenses, /verifications).
  • What each of the three tokens is for, with decoded examples: Tokens.

GET /api/auth/oidc/logout — RP-initiated logout

  • Method: GET (browser redirect) or POST (form)
  • Full URL: https://idp.valyd.id/api/auth/oidc/logout
  • Auth: none (identity proven by id_token_hint)

Parameters: id_token_hint (recommended — the id_token you received at login; an expired one is accepted, its signature still proves the user/client), client_id (optional), post_logout_redirect_uri (must be registered for your client — first-party clients use their post-logout URI list; developer-portal apps may, transitionally until 2026-12-01, use one of their registered redirect URIs; an unregistered value shows an error page), state (optional, appended to the post-logout redirect).

Ends the user’s Valyd session in that browser (Valyd cookies cleared, refresh token revoked, IdP web app storage cleared) and revokes the user’s refresh and access tokens for your client, then redirects. Without a valid id_token_hint the user first sees a “Sign out of Valyd?” confirmation page. Advertised in discovery as end_session_endpoint.

GET /api/auth/oidc/jwks.json — Signing keys

  • Method: GET
  • Full URL: https://idp.valyd.id/api/auth/oidc/jwks.json
  • Auth: none

Public RSA keys (JWK set) for validating id_token signatures (RS256).

GET /api/auth/oidc/userinfo — Standard OIDC userinfo

  • Method: GET or POST
  • Full URL: https://idp.valyd.id/api/auth/oidc/userinfo
  • Auth: Authorization: Bearer YOUR_ACCESS_TOKEN, or (POST only) a form-body access_token — not both (invalid_request)

Returns top-level standard OIDC claims such as sub, valyd_id, preferred_username, email, name, and id_verified according to the granted scopes. email_verified is always false — Valyd does not verify email ownership; identity verification is the separate id_verified claim.

Errors follow RFC 6750: 401 {"error":"invalid_token","error_description":"..."} with WWW-Authenticate: Bearer realm="valyd", error="invalid_token", ..., or 403 insufficient_scope when the token lacks openid.


Resource API — user data

🔑 Auth: Bearer access token (from login) · 👤 User login: required · 📄 Scope-gated — these READ the account; they never run a new check

These canonical /api/auth/oidc/* endpoints accept access tokens minted by the OIDC token endpoint. The Account API never runs a check — it reads what previous checks already proved. To run a new check, see Verification.

Raw identity data (DOB, document number, address …) is never returned by these endpoints — it requires the user’s explicit approval via the consent flow, and comes back end-to-end encrypted.

GET /userinfo — Get User Profile

  • Method: GET
  • Full URL: https://idp.valyd.id/api/auth/oidc/userinfo
  • Base URL: https://idp.valyd.id/api/auth/oidc
  • Path: /userinfo
  • Auth / required scope: Bearer access token required; required scope: profile
  • Required headers:
    • Accept: application/json
    • Authorization: Bearer YOUR_ACCESS_TOKEN

Retrieve the authenticated user’s profile information including name, email, and verification status.

YOUR_ACCESS_TOKEN: the access_token returned by POST /api/auth/oidc/token (code exchange or refresh grant).

Example

curl -X GET "https://idp.valyd.id/api/auth/oidc/userinfo" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response

{ "sub": "valyd_225c7f2ac450496f97bbbc57354a5898", "valyd_id": "valyd_225c7f2ac450496f97bbbc57354a5898", "preferred_username": "johndoe", "email": "user@example.com", "name": "John Doe", "id_verified": true }

GET /licenses — Get Professional Licenses

  • Method: GET
  • Full URL: https://idp.valyd.id/api/auth/oidc/licenses
  • Base URL: https://idp.valyd.id/api/auth/oidc
  • Path: /licenses
  • Auth / required scope: Bearer access token required (no specific scope declared on this endpoint in the source)
  • Required headers:
    • Accept: application/json
    • Authorization: Bearer YOUR_ACCESS_TOKEN

Returns a snapshot of the user’s professional licenses as verified by Valyd. Includes nursing licenses, CDL endorsements, CPR/BLS certifications, Food Handler permits, and more.

YOUR_ACCESS_TOKEN: the access_token returned by POST /api/auth/oidc/token (code exchange or refresh grant).

Example

curl -X GET "https://idp.valyd.id/api/auth/oidc/licenses" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response

{ "success": true, "data": { "licenses": [ { "type": "nurse_licenses", "number": "RN-123456", "status": "Active", "expires_on": "2027-06-30", "issuer": "CA Board of Nursing" }, { "type": "cpr_certification", "number": "CPR-998877", "status": "Active", "expires_on": "2026-05-15", "issuer": "American Heart Association" } ] } }

GET /verifications — Get Identity Verifications

  • Method: GET
  • Full URL: https://idp.valyd.id/api/auth/oidc/verifications
  • Base URL: https://idp.valyd.id/api/auth/oidc
  • Path: /verifications
  • Auth / required scope: Bearer access token required; required scope: verifications
  • Required headers:
    • Accept: application/json
    • Authorization: Bearer YOUR_ACCESS_TOKEN

Returns the user’s verification status: whether they passed a human (liveness) check, whether they completed identity (KYC) verification, and any professional licenses linked to their Valyd identity. Use alongside /userinfo for a complete user picture.

YOUR_ACCESS_TOKEN: the access_token returned by POST /api/auth/oidc/token (code exchange or refresh grant).

Response fields (data.verifications):

FieldTypeDescription
human_verifiedbooleanThe user passed a liveness / anti-spoof human check. Falls back to id_verified when no explicit human check is on file.
id_verifiedbooleanThe user completed identity (KYC) document verification.
licensesarrayProfessional / credential licenses linked to the user. Empty array if none.
licenses[].license_typestringThe kind of license (e.g. drivers_license, medical).
licenses[].verifiedbooleanWhether that license is currently verified.
licenses[].verified_fromstring | nullSource the license was verified against.
licenses[].expire_atstring | nullISO-8601 expiry timestamp, or null if it does not expire.

Example

curl -X GET "https://idp.valyd.id/api/auth/oidc/verifications" \ -H "Accept: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response

{ "success": true, "data": { "verifications": { "human_verified": true, "id_verified": true, "licenses": [ { "license_type": "drivers_license", "verified": true, "verified_from": "kyc", "expire_at": "2027-03-01T00:00:00+00:00" } ] } } }
Last updated on