Skip to main content
ShieldID

Quickstart

The identity verification quickstart.

Sandbox key to first signed webhook, in order. Each step has a working example — copy it, swap in your own key and URLs, and run it.

Step by step

Seven steps, one afternoon.

  1. Get a sandbox key

    Sandbox keys (prefix vk_sand_) are issued on request while self-serve signup is being built. Ask through the contact form — most requests turn around the same business day. Sandbox sessions are never billed, so there's no reason to wait to start.

  2. Create your first session

    One call. Send a return_url (where the applicant lands when they finish) and a webhook_url (where the signed result goes). You get back a session_id and a verification_url.

    bash
    curl -X POST https://api.shieldid.ca/v1/sessions \
      -H "X-Api-Key: vk_sand_your_key" \
      -H "Content-Type: application/json" \
      -d '{
        "return_url": "https://yourapp.example/verified",
        "webhook_url": "https://yourapp.example/hooks/shieldid",
        "expected_name": "Jordan Smith",
        "locale": "en",
        "metadata": { "application_id": "app_9182" }
      }'

  3. Hand the applicant the link

    Open verification_url and you'll see the hosted page. On a desktop browser it shows a QR code — scanning it hands the identical link to a phone, which is where the actual document photo, liveness challenge and selfie happen. Try it on your own phone right now.

  4. Stand up a webhook endpoint

    Point webhook_url at a route that accepts a POST, reads the raw request body (you need it unparsed to check the signature), and returns a 2xx status. Anything else and ShieldID retries the delivery up to three times.

    javascript
    import express from "express";
    const app = express();
    
    app.post("/hooks/shieldid", express.raw({ type: "application/json" }), (req, res) => {
      // Verify the signature before you trust this body — see the next step.
      console.log("Received a webhook:", req.body.toString());
      res.status(200).send("ok");
    });
    
    app.listen(3000);

  5. Verify the signature

    Don't act on a webhook body until you've checked it's genuinely from ShieldID. Compute an HMAC-SHA256 over the raw body with your webhook secret and compare it to the X-Bio-Signature header.

    javascript
    import crypto from "node:crypto";
    
    function isValidShieldIdSignature(rawBody, signatureHeader, secret) {
      const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
      const provided = signatureHeader || "";
      const a = Buffer.from(expected, "hex");
      const b = Buffer.from(provided, "hex");
      return a.length === b.length && crypto.timingSafeEqual(a, b);
    }
    
    // rawBody must be the exact, unparsed request body
    app.post("/hooks/shieldid", express.raw({ type: "application/json" }), (req, res) => {
      const ok = isValidShieldIdSignature(req.body, req.headers["x-bio-signature"], process.env.SHIELDID_WEBHOOK_SECRET);
      if (!ok) return res.status(401).send("invalid signature");
    
      const result = JSON.parse(req.body);
      // result.session_id, result.status, result.checks, …
      res.status(200).send("ok");
    });

  6. Read the result

    The webhook payload already has everything you need. If you'd rather pull the result yourself — after a redirect, say — call the result endpoint directly.

    bash
    curl https://api.shieldid.ca/v1/sessions/ses_9f2c1a/result \
      -H "X-Api-Key: vk_sand_your_key"
    
    # → { "session_id": "ses_9f2c1a", "status": "VERIFIED", "checks": { "document": "PASS", … }, … }

  7. Handle FAILED and a retry

    A session that lands on FAILED carries a failure_reason. Show the applicant something useful. Coming soon: retrying a FAILED session to reset it to PENDING. Today, treat FAILED as terminal and create a new session if the applicant wants to try again.

Before you flip the switch

Go to production.

The request shape never changes between sandbox and production — only the key. Confirm these before you send a real applicant through a vk_live_ session.

  • Swap vk_sand_ for a vk_live_ key. Nothing else about the request changes.
  • Point webhook_url at a real, always-on endpoint that verifies the signature — not a request-bin or a laptop.
  • Handle all three terminal states in your app: VERIFIED, FAILED and EXPIRED, not just the happy path.
  • Confirm your return_url reads the session_id and status query parameters ShieldID appends on redirect.
  • Check current pricing with GET /v1/pricing/{country} or the pricing page, so production charges aren't a surprise.
  • Keep sandbox keys out of anything a real applicant could reach — a sandbox session is free because it's not real.

Questions

Quickstart FAQ.

Get started

Run the first step right now.

Request a sandbox key, paste the first cURL command into a terminal, and you're already ahead of most integrations.