API Reference
Exact browser-facing endpoints, parameters, response formats, callback query parameters, and popup message contracts for custom integrations.
This page documents the exact browser-facing contract used by the SDK.
Use it when you want API-style reference tables rather than implementation walkthroughs.
End-To-End Sequence
| Step | Result |
|---|---|
1. Merchant frontend calls /api/v1/oidc/create | UNQVerify creates a verification session |
| 2. UNQVerify returns a broker authorization URL | Frontend redirects the browser to that URL |
| 3. Broker flow returns to the UNQVerify callback | UNQVerify completes the verification exchange |
4. UNQVerify redirects the user to your RedirectUri with jwt | Your app verifies the token and updates state |
Environments
| Environment | Public key prefix | API base | Verification issuer |
|---|---|---|---|
| Test | pk_test_ | https://test.api.aldersverificering.dk | https://test.aldersverificering.dk |
| Production | pk_live_ | https://api.aldersverificering.dk | https://aldersverificering.dk |
Create Verification Session
Starts a browser verification flow and returns a redirect URL as plain text.
Endpoints
| Environment | Method | Full URL |
|---|---|---|
| Test | POST | https://test.api.aldersverificering.dk/api/v1/oidc/create |
| Production | POST | https://api.aldersverificering.dk/api/v1/oidc/create |
Headers
| Header | Required | Example | Notes |
|---|---|---|---|
x-public-key | Yes | pk_test_abc123 | Must match the target environment |
Content-Type | Yes | application/json | Required by the SDK request, even though no JSON body is sent |
Query Parameters
| Parameter | Type | Required | Example | Notes |
|---|---|---|---|---|
RedirectUri | string | Yes | https://shop.example.com/verify-result | Must be a real callback page in your app |
AgeToVerify | number | Yes | 18 | Minimum required age |
merchant_reference | string | No | A backend-generated random nonce | 32–128 ASCII letters, digits, underscores or hyphens. Returned verbatim inside the signed JWT. |
Request Body
| Body | Required | Format |
|---|---|---|
| Request body | No | Empty |
Success Response
| Property | Value |
|---|---|
| HTTP status | 200 OK |
| Content type | Plain text |
| Response body | Redirect URL string to the MitID/OIDC authorization flow |
Example response body:
https://aldersverificering.dk/oidc/authorize?client_id=...Real test example:
Request:
POST https://test.api.aldersverificering.dk/api/v1/oidc/create?RedirectUri=https%3A%2F%2Funqverify-app.myshopify.com%2F&AgeToVerify=18Response body:
https://pp.netseidbroker.dk/op/connect/authorize?client_id=0b5ac04e-5dbb-4bb8-a697-93152896a2f6&redirect_uri=https%3A%2F%2Ftest.api.aldersverificering.dk%2Fapi%2Fv1%2Foidc%2Fcallback%3FredirectUri%3Dhttps%253A%252F%252Funqverify-app.myshopify.com%252F%26nonce%3D17959b87-7d8a-4aac-b88c-05a005bb194f%26internalCustomerId%3D6952c411cd0a2cfed100fcc5%26internalRequestEventId%3D69bd093ceef79d9928ef28c0&response_type=code&scope=openid%20age_verify%3A18&prompt=login&nonce=17959b87-7d8a-4aac-b88c-05a005bb194fThat returned URL is the MitID broker portal entrypoint. Your frontend should treat it as an opaque redirect target and send the browser to it exactly as returned.
Response URL Semantics
| Part | Meaning |
|---|---|
Host, for example pp.netseidbroker.dk | The MitID/OIDC broker endpoint |
client_id | Broker client configured for the selected environment |
redirect_uri | UNQVerify callback endpoint, not your merchant callback directly |
scope | Includes the requested age verification scope, for example age_verify:18 |
nonce | Per-request anti-replay value generated by the backend |
You should not parse business logic out of this URL. Use it only as the next redirect target.
Error Response
| Property | Value |
|---|---|
| HTTP status | Non-2xx |
| Response body | Plain text error body |
Example error handling shape:
const response = await fetch(url, options)
if (!response.ok) {
const errorBody = await response.text()
throw new Error(`API error: ${response.status} - ${errorBody}`)
}Example Request
curl -X POST \
'https://test.api.aldersverificering.dk/api/v1/oidc/create?RedirectUri=https%3A%2F%2Fshop.example.com%2Fverify-result&AgeToVerify=18' \
-H 'Content-Type: application/json' \
-H 'x-public-key: pk_test_your_key'Redirect Callback Contract
After the user completes verification, the browser returns to your RedirectUri.
Query Parameters Returned To Your Callback Page
| Parameter | Type | Required | Example | Notes |
|---|---|---|---|---|
jwt | string | Yes | eyJhbGciOiJSUzI1NiIs... | Signed verification token |
Expected Callback Behavior
| Step | Required | Notes |
|---|---|---|
Read jwt from the URL | Yes | Missing jwt should be treated as TOKEN_INVALID |
| Verify JWT signature against JWKS | Yes | Do not trust the token before verification |
Check exp | Yes | Reject expired tokens |
Check aldersverificeringdk_verification_result | Yes | false means UNDER_AGE, not a token error |
Remove jwt from the browser URL | Recommended | Prevents reuse through copied URLs, logs, and browser history |
Verification JWT Claims
| Claim | Type | Example | Meaning |
|---|---|---|---|
iss | string | https://test.aldersverificering.dk | Issuer used to choose the correct JWKS endpoint |
exp | number | 1773980000 | Unix timestamp for token expiry |
iat | number | 1773978200 | Token issue time when present |
merchant_reference | string | Your random reference | Present only when supplied at creation. Binds the signed result to your pending attempt. |
aldersverificeringdk_verification_result | boolean | true | Whether the user met the age threshold |
aldersverificeringdk_verification_age | number | 21 | Verified age returned from the flow |
Binding a result to an account
For account approval, create a fresh reference on your backend for each verification attempt.
Use crypto.randomBytes(32).toString('base64url') in Node.js and store it with the
logged-in account, expected age threshold, environment, expiry and an unused flag.
Call the create endpoint from your backend with merchant_reference in the query
and the usual x-public-key header. Redirect the browser to the returned broker URL.
Keep the reference on your backend until the signed result arrives. The public API key allows anyone who possesses it to start a flow, so predictable references such as user IDs, email addresses or order numbers are unsuitable. Keep references out of browser state, callback URLs, analytics and request logs. The API checks the format and length, while your backend is responsible for cryptographic randomness and single use. The reference binds an attempt to an account, without establishing a person's identity or preventing someone from deliberately sharing their flow.
On return, validate the raw JWT on your backend using the configured environment's
JWKS, RS256 algorithm, expected issuer and expiration. Require a successful result,
an age threshold at least as high as your requirement, and an exact match between
the signed merchant_reference and the pending reference for the logged-in account.
Reject missing, mismatched, expired or already-used references. Consume the reference
and approve the account in one database transaction so concurrent callbacks can
approve it only once. Tokens from older flows without this claim cannot approve an
account through this integration.
UNQVerify stores the reference on the verification event and signs that stored value. Callback query parameters cannot replace it. Callback recovery uses the same stored reference and completed retries return the original JWT. The claim is also included on unsuccessful results, so always check the verification result before approval.
JWKS Endpoints
Use the token issuer to choose the correct JWKS endpoint for RS256 signature verification.
| Token issuer | JWKS URL |
|---|---|
https://test.aldersverificering.dk | https://test.api.aldersverificering.dk/well-known/openid-configuration/jwks |
https://aldersverificering.dk | https://api.aldersverificering.dk/well-known/openid-configuration/jwks |
Popup Message Contract
If you use popup flow, the callback page posts the verification result back to the opener window.
Success Message
| Field | Type | Required | Example | Notes |
|---|---|---|---|---|
type | string | Yes | UNQVERIFY_RESULT | Message discriminator |
payload | object | Yes | See JWT claims table above | Decoded verification payload |
Example:
{
"type": "UNQVERIFY_RESULT",
"payload": {
"iss": "https://test.aldersverificering.dk",
"exp": 1773980000,
"aldersverificeringdk_verification_result": true,
"aldersverificeringdk_verification_age": 21
}
}Failure Message
| Field | Type | Required | Example | Notes |
|---|---|---|---|---|
type | string | Yes | UNQVERIFY_FAILURE | Message discriminator |
error | string | Yes | Token has expired | Human-readable error text |
code | string | Yes | TOKEN_INVALID | Machine-readable outcome code |
Example:
{
"type": "UNQVERIFY_FAILURE",
"error": "User does not meet the age requirement",
"code": "UNDER_AGE"
}Recommended Outcome Codes
| Code | Meaning |
|---|---|
UNDER_AGE | Verification completed but the user did not meet the age requirement |
POPUP_CLOSED | The user closed the popup before completion |
USER_CANCELLED | The user explicitly cancelled the verification flow |
POPUP_BLOCKED | Browser refused to open the popup |
POPUP_TIMEOUT | Popup flow exceeded your local timeout |
UNTRUSTED_ORIGIN | Popup message came from an origin you do not trust |
NETWORK_ERROR | Failed to create the verification session |
TOKEN_INVALID | Returned JWT failed verification or claim validation |
UNKNOWN_ERROR | Any other technical failure |
Storage Contract Used By The SDK
If you want parity with the current SDK behavior:
| Field | Value |
|---|---|
| Cookie name | unqverify_token |
| Cookie attributes | path=/; SameSite=Lax; Secure |
| Minimum lifetime | 3600 seconds |
| Actual lifetime | Greater of JWT expiry and 1 hour |
This storage contract is browser-readable because it is set from JavaScript. It is appropriate for UI state, but backend authorization should still validate the JWT or rely on a server-issued session.