Skip to main content
Custom Integrations

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

StepResult
1. Merchant frontend calls /api/v1/oidc/createUNQVerify creates a verification session
2. UNQVerify returns a broker authorization URLFrontend redirects the browser to that URL
3. Broker flow returns to the UNQVerify callbackUNQVerify completes the verification exchange
4. UNQVerify redirects the user to your RedirectUri with jwtYour app verifies the token and updates state

Environments

EnvironmentPublic key prefixAPI baseVerification issuer
Testpk_test_https://test.api.aldersverificering.dkhttps://test.aldersverificering.dk
Productionpk_live_https://api.aldersverificering.dkhttps://aldersverificering.dk

Create Verification Session

Starts a browser verification flow and returns a redirect URL as plain text.

Endpoints

EnvironmentMethodFull URL
TestPOSThttps://test.api.aldersverificering.dk/api/v1/oidc/create
ProductionPOSThttps://api.aldersverificering.dk/api/v1/oidc/create

Headers

HeaderRequiredExampleNotes
x-public-keyYespk_test_abc123Must match the target environment
Content-TypeYesapplication/jsonRequired by the SDK request, even though no JSON body is sent

Query Parameters

ParameterTypeRequiredExampleNotes
RedirectUristringYeshttps://shop.example.com/verify-resultMust be a real callback page in your app
AgeToVerifynumberYes18Minimum required age
merchant_referencestringNoA backend-generated random nonce32–128 ASCII letters, digits, underscores or hyphens. Returned verbatim inside the signed JWT.

Request Body

BodyRequiredFormat
Request bodyNoEmpty

Success Response

PropertyValue
HTTP status200 OK
Content typePlain text
Response bodyRedirect 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=18

Response 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-05a005bb194f

That 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

PartMeaning
Host, for example pp.netseidbroker.dkThe MitID/OIDC broker endpoint
client_idBroker client configured for the selected environment
redirect_uriUNQVerify callback endpoint, not your merchant callback directly
scopeIncludes the requested age verification scope, for example age_verify:18
noncePer-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

PropertyValue
HTTP statusNon-2xx
Response bodyPlain 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

ParameterTypeRequiredExampleNotes
jwtstringYeseyJhbGciOiJSUzI1NiIs...Signed verification token

Expected Callback Behavior

StepRequiredNotes
Read jwt from the URLYesMissing jwt should be treated as TOKEN_INVALID
Verify JWT signature against JWKSYesDo not trust the token before verification
Check expYesReject expired tokens
Check aldersverificeringdk_verification_resultYesfalse means UNDER_AGE, not a token error
Remove jwt from the browser URLRecommendedPrevents reuse through copied URLs, logs, and browser history

Verification JWT Claims

ClaimTypeExampleMeaning
issstringhttps://test.aldersverificering.dkIssuer used to choose the correct JWKS endpoint
expnumber1773980000Unix timestamp for token expiry
iatnumber1773978200Token issue time when present
merchant_referencestringYour random referencePresent only when supplied at creation. Binds the signed result to your pending attempt.
aldersverificeringdk_verification_resultbooleantrueWhether the user met the age threshold
aldersverificeringdk_verification_agenumber21Verified 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 issuerJWKS URL
https://test.aldersverificering.dkhttps://test.api.aldersverificering.dk/well-known/openid-configuration/jwks
https://aldersverificering.dkhttps://api.aldersverificering.dk/well-known/openid-configuration/jwks

If you use popup flow, the callback page posts the verification result back to the opener window.

Success Message

FieldTypeRequiredExampleNotes
typestringYesUNQVERIFY_RESULTMessage discriminator
payloadobjectYesSee JWT claims table aboveDecoded verification payload

Example:

{
  "type": "UNQVERIFY_RESULT",
  "payload": {
    "iss": "https://test.aldersverificering.dk",
    "exp": 1773980000,
    "aldersverificeringdk_verification_result": true,
    "aldersverificeringdk_verification_age": 21
  }
}

Failure Message

FieldTypeRequiredExampleNotes
typestringYesUNQVERIFY_FAILUREMessage discriminator
errorstringYesToken has expiredHuman-readable error text
codestringYesTOKEN_INVALIDMachine-readable outcome code

Example:

{
  "type": "UNQVERIFY_FAILURE",
  "error": "User does not meet the age requirement",
  "code": "UNDER_AGE"
}
CodeMeaning
UNDER_AGEVerification completed but the user did not meet the age requirement
POPUP_CLOSEDThe user closed the popup before completion
USER_CANCELLEDThe user explicitly cancelled the verification flow
POPUP_BLOCKEDBrowser refused to open the popup
POPUP_TIMEOUTPopup flow exceeded your local timeout
UNTRUSTED_ORIGINPopup message came from an origin you do not trust
NETWORK_ERRORFailed to create the verification session
TOKEN_INVALIDReturned JWT failed verification or claim validation
UNKNOWN_ERRORAny other technical failure

Storage Contract Used By The SDK

If you want parity with the current SDK behavior:

FieldValue
Cookie nameunqverify_token
Cookie attributespath=/; SameSite=Lax; Secure
Minimum lifetime3600 seconds
Actual lifetimeGreater 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.

  1. Redirect Flow
  2. Popup Flow
  3. Token Validation

On this page