Auvral

Web SDK

Embed the Auvral verification flow in your page: installation, callbacks, and the one rule that matters.

The Auvral web SDK mounts the hosted verification flow in an overlay on your page, tells you when it finishes, and removes itself.

It does not handle API keys, create sessions, or talk to the Auvral API. Everything it needs travels in the url your backend already obtained from POST /v1/sessions.

See it running first

Try it out opens the same SDK against a sandbox session, so you can watch the callbacks fire before you embed anything.

Install

<script src="https://sdk.dev.auvral.com/v1/verification.js"></script>

A dependency-free script that defines one global, window.AuvralVerification.

Sandbox and production

The snippet above is the sandbox URL. In production, load it from

<script src="https://sdk.auvral.com/v1/verification.js"></script>

/v1/ tracks the latest release of the current major version and is safe to depend on directly. To pin an exact release instead, name its version in place of v1: that path never changes bytes once published, so it is safe to verify with Subresource Integrity. SDK versions lists every published version with a copyable <script src integrity> snippet.

Create the session with allowedOrigins

The SDK receives its result from the verification flow, which will only send it to a page whose origin the session explicitly allows. That list comes from your backend, when you create the session:

server.js
const res = await fetch(`${process.env.AUVRAL_API_URL}/v1/sessions`, {
  method: "POST",
  headers: {
    "x-api-key": process.env.AUVRAL_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    flowId: process.env.AUVRAL_FLOW_ID,
    referenceId: user.id,
    allowedOrigins: ["https://app.example.com"], // the page that will embed the flow
  }),
});

This environment's API base URL is https://api.dev.auvral.com

Omitting allowedOrigins breaks the SDK callbacks, silently

Leave the field out and the list is empty, so no origin ever matches and nothing is sent. onResult and onClose never fire, and the loading indicator never clears.

Nothing fails on the network, the verification completes normally and your webhook arrives on schedule: only the browser side stays silent. The embedded flow logs one [auvral] warning to the browser console, and that warning is the only place this failure names itself.

It is optional in the API schema and not optional in practice. Pass the exact origin of the embedding page (scheme, host and port, no path) for every embedded integration: an entry that is not an origin answers 400 INVALID_ALLOWED_ORIGINS naming it, so the only failure left to catch by eye is a well-formed origin that is not the embedding page's.

Every origin, on purpose

allowedOrigins: ["*"] is supported and means exactly what it says: the flow posts its result to whatever page embeds it. Use it when the embedding origin cannot be known when the session is created, a tenant on its own domain, an agency running the flow for its clients, or localhost on whichever port your dev server picked.

It is all or nothing: ["*"] cannot be combined with a concrete origin, and a list that mixes them answers 400 INVALID_ALLOWED_ORIGINS naming both, because the two ask for opposite things.

The trade is that any page holding the session URL receives that session's events, so the guarantee lasts as long as the URL does. That is short: the code in it is single use, minted by your backend with your secret key, and bound to the first device that opens it. Prefer the explicit list wherever you can write one.

Use it

const auvral = new AuvralVerification();

const frame = auvral.init({
  url: sessionUrl,
  onResult: ({ status, verificationId, sessionId }) => {
    // status is "verified" or "rejected"
  },
  onClose: ({ reason }) => {},
  onError: ({ code, message }) => {},
});

init() returns a handle:

frame.close();  // unmount, idempotent
frame.isOpen;   // boolean

There is no Auvral button component. Connect your own, so it matches your design system:

<button id="verify">Verify identity</button>
<script>
  document.getElementById("verify").onclick = async () => {
    const { url } = await (await fetch("/verification/start", { method: "POST" })).json();
    new AuvralVerification().init({ url, onResult: console.log });
  };
</script>

What the callbacks mean

onResult fires as soon as there is a verdict, and does not close the overlay. Your user may still be reading the final screen. Use the gap: update the page behind them, enable a button, start whatever comes next.

onClose does not mean the verification finished. A close with no preceding onResult means your user left early. Reopening the same url picks up where they stopped, for as long as the session lives. Whether to offer that is your call.

By default the SDK draws its own close button so a user is never trapped behind a full-screen overlay. Pass dismissible: false to remove it.

The rule that matters

The postMessage is a UI signal, not a proof

Never unlock an account, approve a payment or change business state from it. The truth is the webhook or a server-side GET. It lives in the browser and the user controls their browser.

onResult arrives in your user's browser, on a page they can open devtools on. Anyone can call your callback with status: "verified" from the console. The SDK rejects messages from anywhere but the verification flow itself, which stops other code on your page forging a result. It can do nothing about the person at the keyboard.

So:

  • Use onResult for the interface. A success screen, the next step of your wizard, a spinner while your backend catches up.
  • Use the webhook for the decision. Account state, payments, limits, entitlements.

A good pattern is to have onResult poll your own backend, which reports success only once your webhook handler has run.

Before you debug anything

If the overlay does not appear or the camera never turns on, it is almost always a header on your own page. Read Browser requirements first.

Next

On this page