DEV Community

Cover image for Add KYC to your product without building a KYC screen: verification links, an iframe and webhooks
Adewebs
Adewebs

Posted on

Add KYC to your product without building a KYC screen: verification links, an iframe and webhooks

Most teams that need to check who a customer is end up building the same thing: a page that asks for an ID number, a camera screen for a selfie, a progress bar, a consent notice, retries when a photo is blurry, and a back office to review the results. It is weeks of work, and it is not your product.

There is a shorter way: let a hosted flow do the screens, and keep control of the decision. Here is how that looks with the Verification Suite from IdentifyOrg (disclosure: I work on it). The ideas apply to any provider that offers hosted verification.

The three ways in

  1. No code. A business sets up a flow in a dashboard (which checks, in what order), then sends each applicant a personal link on WhatsApp.
  2. An iframe or a script on your own site.
  3. Your server creates a session through the API for each applicant and reads the result afterwards.

All three end in the same place: a session with a status and a result per step.

Create a session from your server

curl -X POST https://api.identifyorg.com/v1/verification-sessions \
  -H "X-IdentifyOrg-Key: io_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "flow_id": "vf_YOUR_FLOW_ID",
    "reference": "order-1042",
    "applicant": { "name": "Ada Obi", "phone": "08031234567" }
  }'
Enter fullscreen mode Exit fullscreen mode

You get back a url. Send it to the applicant, or give it to your page. Two details worth copying into your own designs:

  • reference is your id (an order or customer number). Sending the same reference again while that session is open returns the same session, so a retry never creates a second one.
  • Use a secret key on your server only. The flow id that goes in a public page is not a secret and cannot read anything.

Embed it

<div id="identifyorg-verify" data-flow="vf_YOUR_FLOW_ID"></div>
<script src="https://identifyorg.com/embed/verify.js" async></script>
Enter fullscreen mode Exit fullscreen mode

Or a plain iframe. The one thing people forget is the permissions attribute, without which the browser will not let the frame use the camera:

<iframe
  src="https://identifyorg.com/v/vf_YOUR_FLOW_ID?embed=1"
  title="Identity verification"
  allow="camera; geolocation"
  style="width:100%;max-width:480px;height:760px;border:0"></iframe>
Enter fullscreen mode Exit fullscreen mode

The form only renders inside websites you have allowed in the flow settings. Any other site sees a blank frame.

Listen to the frame

The embedded page talks to yours with postMessage. Always check the origin:

window.addEventListener("message", (e) => {
  if (e.origin !== "https://identifyorg.com") return;
  const d = e.data;
  if (!d || d.source !== "identifyorg-verify") return;

  if (d.type === "resize") iframe.style.height = d.height + "px";
  if (d.type === "completed") showThanks();   // they finished, not "they passed"
});
Enter fullscreen mode Exit fullscreen mode

Events you will see: ready, started, step, received, resize, completed, error.

Do not treat completed as proof of anything. It only means the person reached the end. The result belongs to your server.

Read the result with a webhook

Register an endpoint and you get verification_session.started, .completed, .failed and .awaiting_funds. Verify the signature before you trust a payload. The header looks like t=1760437440,v1=9f2c... and the signature is an HMAC-SHA256 of "{t}.{raw_body}" with your webhook secret:

import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t, sig = parts["t"], parts["v1"]
    if abs(time.time() - int(t)) > tolerance:
        return False                      # too old: could be a replay
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)
Enter fullscreen mode Exit fullscreen mode

The payload includes the status, each step's outcome, and the ID number masked (last four digits). Pictures are never in the webhook; the business reviews those in the dashboard.

Test it first

A flow in test mode returns sample results, charges nothing and makes no real checks. Build the whole integration against it, then switch the flow to live. A test key does the same for the API.

What to decide yourself

A verification result is evidence, not a verdict. Decide what you require (an ID number check only, or also a selfie and a liveness test), what a "review" outcome means for your business, and who looks at it. The tool gives you the signals; the policy is yours.

Docs: Verification Suite and embed guide.

Top comments (0)