AI integration guide

This page exists for one purpose: giving an AI coding assistant (Cursor or anything similar) enough real, current detail about Nexiel's APIs to write correct integration code on the first try, instead of guessing at an endpoint shape from a model's training data. Everything below, the field names, the status values, the error format, matches Nexiel Verify's actual OpenAPI spec and SDK types today.

A ready-to-use prompt

Copy the block below into Cursor or any other AI coding assistant to add Nexiel Age verification to a signup flow. It names the real request/response fields from POST /v1/verify/sessions and GET /v1/verify/sessions/{sessionId} (see the Verify API reference), so an assistant given it has no reason to invent a field name, and it says explicitly which package not to install, since that is exactly the kind of detail a model is likely to get wrong by pattern-matching against a typical hosted-SaaS SDK.

Prompt: add Nexiel Age verification to a signup flow
I need to add Nexiel Age verification (an EUDI wallet age-over-18 check) to my
app's signup flow. Nexiel Age has no separate API of its own: it is Nexiel
Verify's POST /v1/verify/sessions endpoint, called with "checkType":
"age-verification".

Environment variables:
- NEXIEL_VERIFY_BASE_URL: base URL of a running Nexiel Verify instance
  (e.g. https://compliance.nexiel.io in production, or http://localhost:3001
  against a local sandbox-mode instance). This platform is self-hosted per
  deployment, not a shared SaaS endpoint - ask me for the real production
  value rather than guessing one.
- NEXIEL_VERIFY_API_KEY: optional. Omit it entirely against a sandbox-mode
  instance (no auth required there). Required in production, sent as
  "Authorization: Bearer <token>".

Do NOT run "npm install @nexiel/sdk" or import from "@nexiel/sdk". That is a
private internal package, not published to the public npm registry, and
installing it from this project will fail. Call the REST API directly with
fetch instead.

Implement:

1. A server-side route "POST /signup/age-check/start" that:
   - Calls "POST {NEXIEL_VERIFY_BASE_URL}/v1/verify/sessions" with JSON body
     { "clientId": "<our Nexiel client id>", "checkType": "age-verification" },
     header "Idempotency-Key: <a fresh UUID per attempt>", and header
     "Authorization: Bearer ${NEXIEL_VERIFY_API_KEY}" only if that env var is set.
   - The response is { sessionId, templateVersion, authorizationRequest,
     authorizationRequestUri, expiresAt }. Persist only sessionId and expiresAt
     against the in-progress signup record - never store authorizationRequest
     or authorizationRequestUri beyond this one response.
   - Return { sessionId, authorizationRequestUri, expiresAt } to the client.
     authorizationRequestUri is an "openid4vp://..." deep link: render it as a
     QR code for desktop and as a tappable link for mobile, so the user's EUDI
     wallet app can open it. Use whatever QR library the project already has,
     or add a small well-known one - that part is ordinary web development,
     nothing Nexiel-specific.

2. A poller that, once the QR code or link is shown, polls
   "GET {NEXIEL_VERIFY_BASE_URL}/v1/verify/sessions/{sessionId}" through our own
   backend (never directly from the browser, so NEXIEL_VERIFY_API_KEY never
   reaches the client) every few seconds until status is no longer "pending":
   - status "completed" and decision.booleanProofs.ageOver18 === true: let
     signup proceed.
   - status "completed" and decision.booleanProofs.ageOver18 === false: block
     signup with a clear "you must be 18 or older" message. Do not record
     anything about this attempt beyond what we already log for every signup.
   - status "expired" or "failed": show a retry action that starts a brand new
     session (step 1 again). A session in either of these states cannot be
     resumed.
   - Stop polling once expiresAt has passed even without a terminal status.

3. Data handling, do not skip this: persist only sessionId, the boolean result
   (ageOver18), and a timestamp against the signup record. Never persist
   authorizationRequest, authorizationRequestUri, or any wallet-disclosed claim
   beyond that one boolean - an age-verification check never returns a
   birthdate or name in the first place, so there is nothing else to
   accidentally store.

4. Error handling: a non-2xx response is JSON { "error": "<stable_code>",
   "message": "<human-readable>" }, with an additional "retryAfterSeconds"
   field on 429 responses. Handle at least:
   - 429: surface retryAfterSeconds and wait that long before letting the user
     retry, rather than hammering the endpoint immediately.
   - 400: this means our own request was malformed - log it as a bug, never
     show it to the end user as "verification failed".
   - Network failures: retry with backoff. Never treat a network error as a
     failed age check.

Write this using [React Server Actions / Next.js API routes / Express / our
actual stack - ask me which one if you're not sure, rather than assuming].

Why these details matter

Adapting this to a different flow

The same approach, real field names pulled from the actual reference docs, an explicit “don't install the private SDK” caveat, and an explicit persisted-fields list, works for any other Nexiel integration:

Machine-readable steering: /llms.txt

For an AI assistant that reads a site's llms.txt automatically rather than being handed a prompt by hand, Nexiel publishes one at the site root: nexiel.eu/llms.txt. It covers the same ground at a glance, which service owns which route, why Nexiel Age is not a separate API, why the SDK is a private internal package that is not npm install-able outside Nexiel's own systems, the idempotency contract, and the human-in-the-loop rule for POSSIBLE_MATCH, plus links to every quickstart and reference page on this site.