Authentication
The KYC API uses the OAuth 2.0 client-credentials grant (RFC 6749 §4.4). Your backend exchanges a client_id and client_secret for a short-lived Bearer token, then presents that token on every /v1/* call.
Credentials are server-side only. Never ship a
client_secret— or an access token — to a browser or mobile app. Your customers interact with the widget, never with the API.
Request a Token
Endpoint: POST /oauth/token
Content-Type: application/x-www-form-urlencoded
curl --request POST \
--url https://{FQDN}/oauth/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data client_id=$KYC_CLIENT_ID \
--data client_secret=$KYC_CLIENT_SECRET
Credentials may also be sent as HTTP Basic authentication (client_secret_basic) instead of in the body:
curl --request POST \
--url https://{FQDN}/oauth/token \
--user "$KYC_CLIENT_ID:$KYC_CLIENT_SECRET" \
--data grant_type=client_credentials
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "sessions verifications"
}
grant_type must be client_credentials — it is the only grant this endpoint supports.
Scopes
Each endpoint requires a specific scope. Tokens are issued with the scopes granted to your client; request a narrower set with an optional scope parameter when a component of your system only needs one of them.
| Scope | Grants access to |
|---|---|
sessions | POST /v1/sessions, GET /v1/sessions/{session_id} |
verifications | POST /v1/verifications |
--data scope=sessions
Requesting an unknown scope is rejected rather than silently ignored. A token that lacks the scope an endpoint requires returns 403, not 401 — the token is valid, it just is not authorized for that call.
Using the Token
Present the token as a Bearer credential:
curl --request POST \
--url https://{FQDN}/v1/sessions \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Content-Type: application/json' \
--data '{"user_ref": "user-123"}'
The document-number format validator is the one exception — POST /v1/validators/document-number is a stateless format check and needs no token.
Token Lifetime and Refresh
Tokens are RS256-signed JWTs and expire — expires_in tells you when, in seconds. Cache the token and refresh it before expiry; do not request a new token per API call, and do not hardcode a token anywhere.
A robust client:
- Caches the token in memory with its expiry timestamp.
- Refreshes when the remaining lifetime drops below a small margin (30-60 seconds).
- Retries once on a
401, in case the token expired between the check and the call.
Error Responses
Token endpoint errors follow RFC 6749 §5.2 — an error code with a human-readable error_description:
| Status | error | Cause |
|---|---|---|
400 | unsupported_grant_type | grant_type was absent or not client_credentials |
400 | invalid_scope | The requested scope contains an unknown value |
401 | invalid_client | Missing, unknown, or incorrect client credentials. The response carries a WWW-Authenticate: Basic challenge |
{
"error": "invalid_client",
"error_description": "Missing client credentials"
}
Errors on /v1/* calls:
| Status | Cause | How to fix |
|---|---|---|
401 | No Authorization: Bearer header | Send the header; obtain a token at POST /oauth/token |
401 | Token expired | Refresh the token and retry |
401 | Token invalid or unverifiable | Confirm you are using the token verbatim, and against the matching environment |
403 | Token is valid but lacks the endpoint's scope | Request a token carrying the required scope |
The 403 response includes WWW-Authenticate: Bearer error="insufficient_scope" naming the scope that was required.
Environment Isolation
Sandbox and production issue separate credentials, and each token is valid only in the environment that issued it. Sessions are strictly scoped to the tenant that created them: a token can never read another tenant's session, and a request for one returns 404.
Rotating Your Secret
Client secrets are rotated by Inyo on request — contact your Inyo representative rather than attempting rotation through the API. Rotate immediately if a secret may have been exposed.
Next Steps
- Getting Started — run a full verification with your new token
- Verification Sessions — every field on
POST /v1/sessions
