Developers

Integrate age verification in under 10 minutes

Secret keys on your server. Public keys in the browser. Domain allowlisting built in. First signed webhook in under 10 minutes from signup.

Integration pattern: your backend calls POST https://api.ageverifyeu.com/v1/verification-sessions with a secret key, receives a verification_url, redirects (or opens a popup) to the hosted UI, and your webhook endpoint receives a signed verification.completed event with age_over_threshold: true|false. Create a free sandbox account to get API keys.

1. Create a session (server-side)

Call the sessions endpoint from your backend with your secret key. Never use a secret key in browser code.

curl -X POST https://api.ageverifyeu.com/v1/verification-sessions \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "country_code": "DE",
    "language": "de",
    "age_threshold": 18
  }'

# Response:
# {
#   "session_id": "avs_2nLz7X5",
#   "verification_url": "https://verify.ageverifyeu.com/session/avs_2nLz7X5",
#   "status": "created",
#   "expires_at": "2026-06-18T14:00:00Z"
# }
const res = await fetch(
  "https://api.ageverifyeu.com/v1/verification-sessions",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.AGEVERIFY_SECRET_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      country_code: "DE",
      language: "de",
      age_threshold: 18,
    }),
  }
);
const { session_id, verification_url } = await res.json();
// Redirect user to verification_url
import httpx, os

resp = httpx.post(
    "https://api.ageverifyeu.com/v1/verification-sessions",
    headers={
        "Authorization": f"Bearer {os.environ['AGEVERIFY_SECRET_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "country_code": "DE",
        "language": "de",
        "age_threshold": 18,
    },
)
data = resp.json()
session_id = data["session_id"]
verification_url = data["verification_url"]
# Redirect user to verification_url

2. Launch the hosted UI

Redirect the user to the verification_url, or use the JavaScript SDK to launch in popup, embed, or wallet mode. The SDK validates your domain allowlist on init.

// Simplest integration: full-page redirect
// verification_url returned by POST /v1/verification-sessions
window.location.href = verification_url;
import { AgeVerify } from "@ageverify/sdk";

// Init once per page (validates domain allowlist)
await AgeVerify.init({ publicKey: "pk_live_YOUR_KEY" });

// Launch popup when user triggers age-gated action
await AgeVerify.start({
  sessionId: "avs_2nLz7X5",
  mode: "popup",
  language: "de",
  onComplete: (result) => {
    // result.verified — wait for webhook for authoritative result
    console.log("Flow complete:", result);
  },
});
// EU Wallet deep-link mode — opens wallet app directly
await AgeVerify.start({
  sessionId: "avs_2nLz7X5",
  mode: "wallet",
  // Platform detects wallet availability automatically
});

3. Receive and verify the webhook

Configure your webhook endpoint in the dashboard. Every event is signed — always verify before processing.

Webhook payload

{
  "event_type": "verification.completed",
  "session_id": "avs_2nLz7X5",
  "status": "verified",
  "age_over_threshold": true,
  "age_threshold": 18,
  "country_code": "DE",
  "method": "eudi_wallet",
  "assurance_level": "high",
  "policy_version": "DE-AGE-2026-001",
  "completed_at": "2026-06-18T12:34:56Z"
}

Signature verification (Node.js)

import crypto from "crypto";

app.post("/webhook", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.headers["x-ageverify-signature"];
  const ts  = req.headers["x-ageverify-timestamp"];

  // Reject replays older than 5 minutes
  if (Date.now() / 1000 - parseInt(ts) > 300) {
    return res.status(400).send("Timestamp too old");
  }

  const expected = crypto
    .createHmac("sha256", process.env.WEBHOOK_SIGNING_SECRET)
    .update(`${ts}.${req.body.toString()}`)
    .digest("hex");

  if (!crypto.timingSafeEqual(
    Buffer.from(sig), Buffer.from(expected)
  )) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(req.body);
  if (event.age_over_threshold) {
    // Grant access
  }
  res.sendStatus(200);
});
Data minimization

We request only what is strictly necessary

AgeVerify EU requests only boolean age attributes from wallets and eID providers. Identity fields are never requested, transmitted, or stored.

Data field Requested by AgeVerify EU Never requested
age_over_18 / age_over_16 / age_over_21 ✓ Requested
Issuer trust metadata (for validation) ✓ Validated
given_name, family_name ✗ Never requested
birth_date, birth_place ✗ Never requested
Document images (passport, ID card scans) ✗ Never requested or stored
Address, phone number, email ✗ Never requested
Sandbox

Test every outcome without real identity documents

Sandbox mode simulates the full verification flow. Use it to test your webhook handling, error paths, and UI states before going live.

test_over_18

Simulates a successful verification where the user is over the configured age threshold. age_over_threshold: true.

🚫

test_under_18

Simulates a successful verification where the user is under the threshold. age_over_threshold: false.

test_failed

Simulates a verification failure (e.g., provider error, user abort). Status: failed. Tests your error handling path.

⏱️

test_expired

Simulates a session that expires before the user completes verification. Status: expired. Tests your timeout handling.

Programmatic sandbox simulation: POST to /v1/provider-callbacks/sandbox with {"session_id": "…", "method": "test_over_18"} to trigger an outcome without opening the hosted UI — useful for automated integration tests and CI pipelines.

Developer FAQ

What API authentication does AgeVerify EU use?

Secret keys (sk_test_… / sk_live_…) are used for server-side API calls via the Authorization: Bearer header. They must never appear in browser code. Public keys (pk_test_… / pk_live_…) are used for JavaScript SDK initialization and are validated against a per-app domain allowlist. Both key types can be rotated in the developer dashboard without downtime.

How do I verify webhook signatures?

Each webhook includes X-AgeVerify-Signature (HMAC-SHA256 hex of timestamp + "." + raw request body, signed with your webhook signing secret) and X-AgeVerify-Timestamp (Unix epoch seconds). Always verify both and reject payloads with timestamps older than 5 minutes to prevent replay attacks. Use crypto.timingSafeEqual (or equivalent) to prevent timing attacks.

Should I use webhooks or poll the session endpoint?

Webhooks are strongly recommended for production. They receive the result immediately after verification without polling delay, and include the HMAC signature for security. Polling GET /v1/verification-sessions/{session_id} is supported for environments where incoming webhooks are not possible. In sandbox, use the programmatic callback endpoint to trigger results instantly.

How do I test without real wallets or identity documents?

Create an app in sandbox mode and generate test API keys (sk_test_…). The hosted verify UI shows sandbox methods (test_over_18, test_under_18, test_failed, test_expired) that simulate each outcome without connecting to real identity providers. For automated testing, POST directly to /v1/provider-callbacks/sandbox with the desired method.

What SDK launch modes are available?

Four modes: redirect — full-page redirect (no SDK needed); popup — centred popup window; embed — iframe within your page; wallet — deep link to the user's EUDI Wallet app. All modes deliver results via the same signed webhook.

What HTTP status codes should I handle?

201 Created — session created. 400 Bad Request — invalid parameters. 401 Unauthorized — missing or invalid API key. 402 Payment Required — monthly quota exceeded. 422 Unprocessable Entity — validation error. 429 Too Many Requests — rate limit exceeded. Non-2xx responses from your webhook endpoint trigger automatic retries with exponential back-off.

Ready to integrate?

Free sandbox account. No credit card. Full API access from day one.

Create free account Try sandbox demo