API Reference
Public endpoint and webhook contract for CAIRL hosted verification integrations.
API Reference
This page is the human-readable companion to the machine-readable OpenAPI contract.
Use the reference when you need exact endpoint names, request bodies, response shapes, webhook event schemas, or auth requirements. Use the quickstart when you want a copy-paste path to a working OAuth + PKCE integration.
Machine-readable contract
- OpenAPI JSON:
/openapi.json - Production server:
https://cairl.app - Staging server:
https://staging.cairl.app
The contract covers these public integration surfaces:
| Area | Endpoint or surface | Purpose |
|---|---|---|
| Hosted verification | GET /verify/start | Redirect the user into a fresh CAIRL verification for your site, with PKCE. |
| OAuth provider | POST /api/oauth/token | Exchange an authorization code for a bearer access token. |
| OAuth provider | GET /api/oauth/userinfo | Read the issuance-time verified claims snapshot. |
| OAuth provider | POST /api/oauth/revoke | Revoke an access token under RFC 7009 semantics. |
| Verification sessions | GET /api/verify/hvf-session/{session_id} | Poll hosted verification session status. |
| Shopify integration | POST /api/integrations/shopify/verify/exchange | Exchange a Shopify-hosted verification code for claims. |
| Webhooks | Your configured HTTPS callback | Receive signed outbound lifecycle and billing events. |
Also available, not yet described in the OpenAPI file:
| Endpoint | Purpose |
|---|---|
GET /oauth/authorize | Conventional OAuth authorization endpoint, in verification mode: same parameters as /verify/start plus the required response_type=code. Unlike /verify/start, it honours an existing verification that meets the key's depth and falls inside its time-based freshness window (daily, 3day, weekly, biweekly, monthly) and goes straight to consent. The event-driven policies, session and transaction, are not enforced here. This is not the sign-in flow — see the OIDC row below. |
GET /oauth/authorize | The same endpoint in OIDC sign-in mode: the openid scope alone, on a login-enabled credential, returns an ID token. Verification scopes and openid are different contracts — mixing them up is the most common integration mistake. See Log in with CAIRL. |
GET /.well-known/jwks.json | Public signing keys for the access-token JWT. |
Not published: refresh tokens. ID tokens are issued only on the OIDC
sign-in path above (openid alone, login-enabled credential) — never on a
verification-scope exchange. Configure endpoint URLs explicitly in your OAuth
library.
Authentication summary
| Surface | Auth model |
|---|---|
/verify/start | Public redirect with registered client_id, redirect_uri, state, and PKCE challenge. |
/api/oauth/token | Server-to-server client_id plus client_secret; grant_type=authorization_code only, with code, redirect_uri, and PKCE code_verifier required. |
/api/oauth/userinfo | Authorization: Bearer ACCESS_TOKEN. |
/api/oauth/revoke | Server-to-server client_id plus client_secret. |
/api/verify/hvf-session/{session_id} | Authorization: Bearer YOUR_API_KEY. |
| Shopify exchange | HMAC headers: X-CAIRL-Integration-Id, X-CAIRL-Timestamp, and X-CAIRL-Signature. |
| Webhooks | X-CAIRL-Signature: sha256=<hex> over the raw body. |
Userinfo response shape
GET /api/oauth/userinfo returns a snapshot of the claims evaluated when the
token was issued. It does not re-evaluate claims live on every request.
{
"sub": "pws_v1_<pairwise>",
"evaluated_at": "2026-05-03T10:00:00.000Z",
"mode": "live",
"claims": {
"age_18_plus": true,
"identity_verified": true
},
"meta": {
"claims_requested": ["age_18_plus", "identity_verified"],
"claims_resolved": ["age_18_plus", "identity_verified"],
"claims_null": [],
"claims_ignored": []
}
}Read the claim value from response.claims.age_18_plus, not from a top-level
age_18_plus field. The scope that produces it is age:18+; scopes and claim
names are spelled differently by design (see the
claims reference).
Webhook event schemas
The OpenAPI webhooks.cairlEvents section defines the signed callback payloads
CAIRL can send to your endpoint:
verification.session.completedverification.session.failedenrollment.createdvae.resolved
All events include an event_id for idempotency. Store seen event IDs and
acknowledge quickly with a 2xx response before doing slow work.
Compatibility rules
- Unknown response fields are additive and should be ignored by clients.
- Unknown webhook event types should be ignored and logged.
- New required request fields are compatibility-sensitive and will be recorded in the versioning changelog.
- CAIRL returns verified claims. It does not return raw identity documents or default raw PII through the public OAuth userinfo endpoint.