Embed guide

Drop-in KYC in four lines.

Embed a KarVerifi workflow inline in your app. No redirects, full control over layout, and a clean postMessage contract for completion events.

Four steps

  1. 1. Publish a workflow

    In your dashboard, order your checks and click Publish. You'll get a slug like wf_ng_merchant.

  2. 2. Configure allowed_origins

    Under Settings → Embed, add the origins you'll embed from — e.g. https://app.yourbrand.com. We refuse to render for origins outside this allowlist.

  3. 3. Drop the snippet

    Paste this anywhere in your HTML. The script self-mounts onto any element with a data-karrverify-workflow attribute.

    <script src="https://embed.karverifi.com/embed.js" defer></script>
    <div data-karrverify-workflow="wf_ng_merchant"></div>
  4. 4. Handle callbacks

    Listen for postMessage events on window to react to completion, cancellation, or errors — see the event contract below.

Programmatic mount

Need to render the widget inside a modal, tab, or SPA route change? Call Karrverify.mount() directly.

Karrverify.mount({
  container: '#kyc-slot',        // CSS selector or Element
  workflow:  'wf_ng_merchant',   // published workflow slug
  customer:  {                   // pre-fill anything you already know
    email: 'ada@example.com',
    phone: '+2348012345678',
  },
  metadata:  { order_id: 'ord_9f2a' },
  session_token: 'tok_…',        // optional: server-created session
  environment: 'sandbox',        // 'sandbox' | 'live' — defaults to 'live'
  theme:  'auto',                // 'auto' | 'light' | 'dark'
  locale: 'en-NG',
  onReady:    ()      => {},
  onComplete: (event) => {},
  onError:    (event) => {},
});

For the best UX, create the session server-side with POST /v1/verifications and pass the resulting session token here — that way the widget never has to prompt the user for the information you already have.

Event contract

The widget communicates via window.postMessage. Every event is a JSON object with a type field. Always verify event.origin before trusting the payload.

karrverify:readyfired once, when the iframe has finished loading
{
  "type": "karrverify:ready",
  "session_id": "ses_…"
}
karrverify:completefired once, on final verification decision
{
  "type": "karrverify:complete",
  "verification_id": "ver_…",
  "status": "passed" | "failed" | "manual_review",
  "steps": [
    { "kind": "bvn_verify",   "status": "passed" },
    { "kind": "phone_verify", "status": "passed" }
  ]
}
karrverify:errorfired any time the widget hits an unrecoverable error
{
  "type": "karrverify:error",
  "code": "session_expired" | "origin_not_allowed" | "network_error",
  "message": "Session token has expired."
}

Styling

The widget renders in a sandboxed iframe, so your host page CSS never bleeds in. Control it from the outside.

  • Constrain width

    Wrap the host element and set max-width. The iframe fills 100% of its parent and grows vertically as steps unfold.

  • Dark mode

    theme: 'auto' respects the end-user's prefers-color-scheme. Override with theme: 'dark' or 'light' to force one.

  • Responsive

    On mobile viewports the widget renders full-bleed with a bottom sheet for controls. No extra media queries needed on your side.

Troubleshooting

origin_not_allowed

The origin embedding the widget isn't in your allowlist. Add it under Settings → Embed. Origin = scheme + host + port, no path.

session_expired

Sessions are valid for 24 hours. Create a fresh one with POST /v1/verifications and re-mount.

Blank iframe / silent failure

Usually a missing allowed_origins entry. Open DevTools → console; we log the exact refusal reason.

CSP frame-ancestors

If your host page sets a strict Content-Security-Policy, add frame-src https://embed.karverifi.com and connect-src https://api.karverifi.com.

Prefer server-side flows? Skip to the API reference and skip the widget entirely.