Build with the AgeCheck API
Create age-check sessions, route users to hosted verification, accept wallet credential presentations, and store only the minimal age-gate result your product needs.
Quickstart
Create your first age-check session
Use the API key for one environment to create a session. AgeCheckAPI returns a hosted verification URL plus the minimal result object in its current state.
export API=http://localhost:3001
export AGE_KEY=ack_test_your_key.your_secret
curl -X POST "$API/v1/age-check/sessions" \
-H "Content-Type: application/json" \
-H "X-API-Key: $AGE_KEY" \
-H "Idempotency-Key: order-123" \
-d '{
"age_policy": "over_18",
"use_case": "general_b2b",
"redirect_url": "https://merchant.example/age-result",
"external_reference": "order_123"
}'{
"session_id": "clx_session_id",
"status": "provider_redirected",
"age_policy": "over_18",
"verified": false,
"result": "unknown",
"reason_code": "provider_request_created",
"verified_at": null,
"assurance_level": "low",
"provider_reference": "mock_provider_reference",
"verification_url": "http://localhost:3000/verify?...",
"expires_at": "2026-07-03T13:15:00.000Z"
}What to do next
Send the user to verification_url, open it inside the widget, or collect a verifiable presentation from a wallet/provider bridge and submit it to the session presentation endpoint.
Authentication
API keys are scoped to environments
Session APIs use X-API-Key. Bootstrap/admin APIs use X-Admin-Token and should be protected by dashboard auth, SSO and RBAC before production.
Base URLs
Local development uses http://localhost:3001. Production clients should target https://api.agecheckapi.com when provisioned.
Idempotency
Pass Idempotency-Key when creating sessions. The backend stores a scoped hash and returns the existing session for repeated keys.
Secrets
API key secrets are returned once, then stored hashed. Webhook secrets are encrypted and used for HMAC signatures.
export API=http://localhost:3001
export ADMIN_TOKEN=replace-with-admin-bootstrap-token
curl -X POST "$API/v1/admin/organizations" \
-H "Content-Type: application/json" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-d '{"slug":"acme","display_name":"Acme Media"}'
curl -X POST "$API/v1/admin/projects" \
-H "Content-Type: application/json" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-d '{"organization_id":"ORG_ID","slug":"main","display_name":"Main","use_case":"general_b2b"}'
curl -X POST "$API/v1/admin/environments" \
-H "Content-Type: application/json" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-d '{"project_id":"PROJECT_ID","kind":"sandbox","display_name":"Sandbox"}'
curl -X POST "$API/v1/admin/api-keys" \
-H "Content-Type: application/json" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-d '{"environment_id":"ENVIRONMENT_ID"}'API surface
Implemented REST endpoints
These endpoints mirror the real NestJS controllers in the backend project. The static OpenAPI file documents the public core subset, while admin and compliance operations are listed here from the implementation.
Health
/healthService health check. This route is intentionally not prefixed with /v1.
Age-check sessions
/v1/age-check/sessionsCreate a session and receive a hosted verification URL. Requires X-API-Key.
/v1/age-check/sessions/{session_id}Read the minimal result for one session. Requires X-API-Key.
/v1/age-check/sessions/{session_id}/cancelCancel a session. Requires X-API-Key.
Verification inputs
/v1/age-check/sessions/{session_id}/presentationsVerify an Ed25519 compact JWS presentation. Public, but bound by session nonce and audience.
/v1/provider-callbacks/{provider_id}Provider callback endpoint. The built-in sandbox provider id is mock-wallet.
Admin setup
/v1/admin/organizationsBootstrap a B2B organization. Requires X-Admin-Token.
/v1/admin/projectsCreate a project under an organization. Requires X-Admin-Token.
/v1/admin/environmentsCreate a sandbox or production environment. Requires X-Admin-Token.
/v1/admin/api-keysCreate an API key. The secret is returned once and stored hashed.
/v1/admin/api-keys/{api_key_id}/rotateRevoke an API key and return a replacement secret once. Requires X-Admin-Token.
/v1/admin/webhook-endpointsCreate a webhook endpoint and encrypted signing secret. Requires X-Admin-Token.
/v1/admin/environments/{environment_id}/sessionsList the latest sessions for an environment. Requires X-Admin-Token.
/v1/admin/organizations/{organization_id}/usageList recent usage records for an organization. Requires X-Admin-Token.
Compliance and webhooks
/v1/compliance/{organization_id}/ropaExport ROPA-style processing records.
/v1/compliance/{organization_id}/dpiaExport a DPIA draft for the selected policy.
/v1/compliance/{organization_id}/dpa-annexExport a processor-first DPA annex.
/v1/compliance/{organization_id}/audit-evidenceExport append-only audit hash-chain evidence.
/v1/compliance/{organization_id}/breach-incidentsRegister a breach incident and append an audit event.
/v1/compliance/policy-reportExport the EU policy registry mapping.
/v1/webhooks/{endpoint_id}/testCreate a signed webhook test delivery.
Wallet proof
Submit a verifiable age presentation
A wallet, issuer or provider bridge can submit an Ed25519 compact JWS. AgeCheckAPI verifies the signature, trusted issuer, audience, session nonce, expiration and age evidence before computing the final pass/fail result.
curl -X POST "$API/v1/age-check/sessions/SESSION_ID/presentations" \
-H "Content-Type: application/json" \
-d '{"presentation_jws":"COMPACT_ED25519_JWS"}'{
"iss": "https://issuer.example.com",
"aud": "agecheckapi:ENVIRONMENT_ID",
"nonce": "SESSION_ID",
"vc": {
"type": ["VerifiableCredential", "AgeCredential"],
"credentialSubject": {
"age_over_18": true
}
},
"assurance_level": "substantial",
"iat": 1783079100,
"exp": 1783079400
}Nonce and audience are mandatory
The presentation aud must equal agecheckapi:ENVIRONMENT_ID, and nonce must equal the AgeCheckAPI session id. This prevents replay across sessions or relying parties.
Sandbox
Mock wallet callback scenarios
The built-in sandbox provider is mock-wallet. Use it to exercise pass, fail, consent-declined, provider-unavailable and expired-session paths without real credentials.
curl -X POST "$API/v1/provider-callbacks/mock-wallet" \
-H "Content-Type: application/json" \
-d '{
"provider_reference": "mock_reference_from_session",
"state": "SESSION_ID",
"scenario": "user_over_18"
}'Supported scenarios
user_over_18user_under_18provider_unavailableconsent_declinedexpired_sessionWebhooks
Verify signed delivery attempts
Webhook payloads are signed with an AgeCheckAPI-Signature header. Verify the timestamp and HMAC before trusting a delivery.
AgeCheckAPI-Signature: t=1783079100,v1=hex_hmac_sha256
signed_payload = timestamp + "." + raw_json_body
signature = HMAC_SHA256(webhook_secret, signed_payload)Delivery behavior
- Worker retries failed deliveries with exponential backoff.
- Payloads contain session id and payload hash, not raw identity data.
- Default event set: age_check.completed, age_check.failed, age_check.expired.
Data model
Persist only the minimal age-gate result
The public result allowlist contains only session status, policy, boolean verification result, reason code, assurance level, timestamp and a non-identifying provider reference.
{
"session_id": "SESSION_ID",
"status": "verified",
"age_policy": "over_18",
"verified": true,
"result": "pass",
"reason_code": "age_threshold_met_by_agecheckapi",
"verified_at": "2026-07-03T13:15:00.000Z",
"assurance_level": "substantial",
"provider_reference": "vp_non_identifying_reference"
}Session statuses
provider_redirectedSession is ready and the user should complete the hosted or provider flow.
verifiedAgeCheckAPI verified the proof and the age threshold passed.
failedThe proof was valid but the user did not meet the age threshold, or the provider result failed.
expiredThe session or presentation expired.
cancelledThe relying party cancelled the session or the user declined consent.
Age policies and use cases
Current behavior is policy-registry driven. For a threshold such as 21, use age_policy: "custom_min_age" with custom_min_age: 21 until a dedicated over-21 policy profile is configured.
| Policy | Minimum age | Typical use |
|---|---|---|
over_16 | 16 | Resolved by the current policy registry for social_media. |
over_18 | 18 | Resolved by the current policy registry for general_b2b, adult_content, gambling, alcohol and video_sharing. |
custom_min_age | 13-25 | Requires custom_min_age in the create-session body. |
Ready to wire this into your product?
Start with sandbox sessions, implement idempotency, verify webhook signatures, and make access decisions only from the minimal result object.
TypeScript SDK
Use the SDK when running inside Node.js
The SDK wraps session creation, lookup and verifiable presentation submission while preserving the same REST payloads.
import { AgeCheckClient } from "@agecheck/sdk";
const agecheck = new AgeCheckClient({
apiKey: process.env.AGECHECK_API_KEY,
baseUrl: "http://localhost:3001"
});
const session = await agecheck.createAgeCheckSession({
age_policy: "over_18",
use_case: "general_b2b",
redirect_url: "https://merchant.example/age-result",
external_reference: "user_or_order_reference",
idempotency_key: "order-123"
});
console.log(session.verification_url);