Identity API
Guide

Embed in your site

Run a hosted verification session inside your own page, as a modal or inline, so customers never leave your site. It's the same flow as the link: consent, ID match, card photos, selfie and liveness.

1. Allow your site

In the dashboard, open Verify → Hosted Sessions → Settings and add every origin that will embed the flow, one per line, e.g. https://acme.com and https://app.acme.com. Up to 10. Use https; http://localhost is allowed for testing.

The verification page only lets those origins frame it. On any other site the frame is blocked by the browser.

2. Create the session on your server

Same call as for a link (POST /v1/verify/sessions). Send only the sessionId to the browser; your API key must stay on your server.

JavaScript
// Your server: create the session, return only its id to the browser.
app.post("/api/start-verification", async (req, res) => {
const r = await fetch("https://api.sendexa.co/v1/verify/sessions", {
method: "POST",
headers: {
Authorization: `Basic ${process.env.SENDEXA_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
contact: req.user.phone,
contactType: "PHONE",
idDocument: "GHANA_CARD",
purpose: "opening your account",
}),
});
const { data } = await r.json();
res.json({ sessionId: data.sessionId });
});

3. Open it in the page

Load the script once, then call SendexaVerify.open. Without a container it opens as a modal (full screen on phones).

HTML
<script src="https://dash.sendexa.co/sdk/verify/v1.js"></script>
<button id="verify">Verify my identity</button>
<script>
document.getElementById("verify").addEventListener("click", async () => {
const { sessionId } = await fetch("/api/start-verification", { method: "POST" })
.then((r) => r.json());
const verification = SendexaVerify.open({
sessionId,
onComplete: () => {
verification.close();
showMessage("Thanks, we're confirming your details…");
},
onFail: ({ reason }) => showMessage("We couldn't verify you: " + (reason || "")),
onExit: () => showMessage("Verification cancelled"),
});
});
</script>

To place it inside your layout instead:

HTML
<div id="verify-box"></div>
<script>
SendexaVerify.open({
sessionId,
container: "#verify-box", // fills the element; height follows the content
onComplete: () => goToNextStep(),
});
</script>

Options and events

FieldTypeDescription
sessionIdrequiredstringFrom your server.
containerstring | ElementCSS selector or element to render into. Omit for a modal.
onReadyfunctionThe flow loaded.
onCompletefunction{ sessionId, status: "COMPLETED" }. Every step passed.
onFailfunction{ sessionId, status: "FAILED" | "EXPIRED", reason? }. Same reasons as failReason.
onReviewfunction{ sessionId, status: "NEEDS_REVIEW" }. Every step passed but a risk flag needs your decision; the customer is told you'll review it. Handle this, or such customers will look stuck.
onExitfunctionThe customer closed the modal (close button or Escape) before finishing.
onEventfunctionEvery raw message, for logging.

open returns { close() }. The modal stays on its result screen until you call close(); calling it after a result doesn't fire onExit.

If your site sends its own security headers

The flow needs the camera inside the frame. The SDK sets allow="camera" on the iframe, but a Content-Security-Policy or Permissions-Policy on your page can still block it. Allow our origin:

http
Content-Security-Policy:
script-src 'self' https://dash.sendexa.co;
frame-src https://dash.sendexa.co;
Permissions-Policy: camera=(self "https://dash.sendexa.co")

Some in-app browsers (inside social apps) don't give frames camera access at all. The flow then offers a QR code to continue on the customer's phone, and you still get the result by webhook. The plain link always works as a fallback.

Next

Get the result by webhook →