Load it
window.KycService and nothing else. Load it from this URL: the SDK finds the API from the address it was loaded from. A copy you host yourself must pass apiUrl to init.
Initialise
init throws synchronously when the configuration is invalid (for example a missing apiKey).
Start
Recommended: a session your backend created. Your backend callsPOST /v1/verifications with a signed request carrying your external_id (see the Quick Start) and returns the session’s url to the page:
external_id or callback_url: the API honours those only on a signed request. Use it to try the widget, not to verify your users.
Call start() from a click handler. In new-tab mode the SDK opens the tab before anything else so that pop-up blockers let it through, which only works if nothing was awaited before start() in that handler: have your backend’s session URL ready before the click (Safari in particular blocks a tab opened after an await). In modal mode fetching the URL inside the handler is fine. If the browser blocks it anyway, the SDK shows the person a link to open the widget themselves; that tab cannot talk back to your page, so neither onComplete nor onClose fires for it.
Calling start() again, or close(), closes whatever the previous call opened, including a session it was still creating.
Callbacks
onComplete
Fires once, when the person reaches the widget’s final screen.result.verificationId is the verification’s id.
It does not tell you whether the person was approved or declined: the widget shows the same final screen either way. Ask your backend, which learns the outcome from the webhook or GET /v1/verifications/{id}.
It does not fire for:
- a session waiting in manual review: the person sees a processing screen until a decision is made;
- a session the person finished on their phone after scanning the QR code: the modal on the computer fires it when it catches up;
- expired or abandoned sessions.
onClose
Fires when the person leaves before the final screen: the close button, a click outside the modal, the Escape key, or closing the tab in new-tab mode. It does not fire afteronComplete, nor when your own code calls close().
onError
error.recoverable is false when trying again will not help.
UNSUPPORTED_BROWSER never reaches onError: in a browser without fetch or modern JavaScript, the script throws it as it loads, before init runs, and window.KycService is not defined.
Closing
window.KycService.close() removes the widget from your page (which stops the camera) or closes the tab. The session stays open: a later start() with the same verificationUrl continues where the person left off.
After the final screen
About 3 seconds after the final screen, the SDK sends your whole page to the session’s redirect address, after callingonComplete. That address is the callback_url your backend passed when creating the session, or else the workspace’s Redirect URL (console: Workspace → Integration URLs). A new workspace’s Redirect URL points at a ProofAge page. Set it to a page of yours, or clear it to keep the person on your page and handle onComplete yourself. Without a redirect address the modal stays on the final screen until the person closes it.
callback_url is a browser redirect, not a webhook address. Webhooks go to the workspace’s webhook URL.
Security
- Messages from the widget are accepted only from the widget’s own origin.
onCompleteruns in the person’s browser, where anyone can call it. Never unlock anything from it: wait for your backend to confirm the outcome.external_idcan only be set by your backend, on a signed request. See Authentication.- The page must be served over HTTPS to create a session from the browser or to open the modal. Opening a backend-created session in a new tab also works on plain HTTP.
Test mode
With apk_test_ key the submission is not analysed: it waits in review and the person sees a processing screen. Set the outcome in the console (or with the MCP tool set-test-verification-outcome); the widget then shows the final screen and onComplete fires. See Sandbox and test data.