Skip to main content
The browser SDK opens ProofAge’s verification widget on your page, in a modal or a new tab, and tells your page when the person is done. The decision itself reaches your backend by webhook.

Load it

The script defines 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 calls POST /v1/verifications with a signed request carrying your external_id (see the Quick Start) and returns the session’s url to the page:
Quick: a session created from the browser. Without a URL, the SDK creates the session itself with the public key:
Such a session cannot carry 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 after onComplete, 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 calling onComplete. 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.
  • onComplete runs in the person’s browser, where anyone can call it. Never unlock anything from it: wait for your backend to confirm the outcome.
  • external_id can 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 a pk_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.

Full example