For product & platform teams

Integrate Blend Integrity into your platform

You keep the questions and the candidate experience. Blend runs identity, lockdown, live camera signals, and the evidence report. This page is the full reference — every endpoint, header, SDK option, webhook, and error you need to ship.

Base URL: https://exam-engine-staging.vercel.app

1. Overview — what Integrity is (and is not)

You keepBlend provides
Question bank & UIIdentity verification (Didit)
Scoring of answersEnvironment / room scan
Candidate login to your appLive AI attention flags (never auto-fail)
Most of your brandingLockdown / SEB when level is full
Pass/fail policyEvidence pack, risk score, webhooks
Always-on Powered by Blend watermark

Integrity sessions never include Blend-hosted questions. Reports return kind: "integrity" and answers: []. Humans adjudicate — AI only signals.

Just need to run tests without an API? Use the Platform dashboard instead of this guide.

2. Architecture (recommended)

┌─────────────────┐     sk_… (secret)      ┌──────────────────┐
│  Your backend   │ ─────────────────────► │  Blend API       │
│                 │   create session       │  /api/v1/…       │
└────────┬────────┘                        └────────┬─────────┘
         │ returns ses_…
         ▼
┌─────────────────┐     iframe / SDK
│  Your frontend  │ ◄── Blend.mount({ mode: "integrity" })
│  + questions UI │
└─────────────────┘
  1. Never put sk_… in the browser, mobile binary, or URL.
  2. Your server creates the session with the API key.
  3. Your client only receives ses_… and mounts the iframe.
  4. When the candidate finishes your exam, end Integrity, then GET the report and/or handle webhooks.

3. Get started checklist

  1. Sign up at /login — workspace + Free credits are created automatically.
  2. Dashboard → Developers → API keys → create sk_test_….
  3. Optional: set webhooks and store the signing secret.
  4. Create an Integrity session from your backend (§5).
  5. Mount Blend with mode: "integrity" (§6).
  6. End the session and fetch the report (§7–§8).
  7. Production: sk_live_…, live identity, paid credits / contact sales for volume.

4. Authentication

API key

Authorization: Bearer sk_test_xxxxxxxx
PrefixUse
sk_test_Sandbox — mock identity available
sk_live_Production

Idempotency (required on POSTs)

Idempotency-Key: <unique-string-per-logical-request>

Required for POST /api/v1/integrity/sessions. Replays return the original body with header Idempotency-Replayed: true. Use a UUID per attempt (e.g. your assignment attempt id).

Rate limits

Per-key token bucket (default capacity 60, ~1 token/sec refill). 429 with rate_limit_exceeded; headers Retry-After, X-RateLimit-Remaining.

Credits

Each session launch spends Blend Credits (lite 50 / full 100 / none 10). Short balance → 402 insufficient_credits. Top up under Billing; coupons via /invite/CODE.

5. Create an Integrity session

POST /api/v1/integrity/sessions

HeaderRequiredNotes
AuthorizationYesBearer sk_…
Idempotency-KeyYesUnique per create
Content-TypeYesapplication/json
Body fieldTypeDefaultDescription
candidate_refstringcandidateYour user / roll id
external_refstringnullYour attempt / mock id
proctoring_levelenumlockdown-litenone | lockdown-lite | full
duration_minutesnumberexam defaultMonitoring window
curl -sS -X POST "https://exam-engine-staging.vercel.app/api/v1/integrity/sessions" \
  -H "Authorization: Bearer $KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "candidate_ref": "student-42",
    "external_ref": "gmat-mock-991",
    "proctoring_level": "lockdown-lite",
    "duration_minutes": 120
  }'

201 response

{
  "id": "uuid-session-id",
  "kind": "integrity",
  "session_token": "ses_…",
  "embed_url": "https://exam-engine-staging.vercel.app/integrity/ses_…",
  "status": "created",
  "expires_at": "2026-07-15T12:00:00.000Z",
  "candidate_ref": "student-42",
  "external_ref": "gmat-mock-991",
  "proctoring_level": "lockdown-lite",
  "mode": "test"
}

Store id for report GET. Pass only session_token to the browser — never the API key.

6. Embed the Integrity shell (SDK)

Script tag

<script src="https://exam-engine-staging.vercel.app/sdk/blend.js"></script>

ExamEngine is a compatibility alias of Blend.

Mount

<div id="blend-integrity" style="width:100%;height:100vh"></div>
<script>
  const handle = Blend.mount("blend-integrity", {
    sessionToken: "ses_…",
    baseUrl: "https://exam-engine-staging.vercel.app",
    mode: "integrity",
    height: "100%",
    theme: {
      primary: "#0f766e",
      background: "#f8fafc",
      foreground: "#0f172a",
      muted: "#64748b",
      border: "#e2e8f0",
      radius: "12",
      partnerName: "YourProduct"
    },
    onEvent: (event) => {
      console.log(event.type, event.payload);
    }
  });
  // handle.destroy();
</script>
OptionRequiredDescription
sessionTokenYesses_… from your backend
baseUrlYes*Blend origin (*from script src if omitted)
modeFor Integrity"integrity" → /integrity/…
heightNoCSS height (default 100%)
classNameNoApplied to iframe
themeNoColors + partnerName
onEventNopostMessage from iframe

Host events

Messages use source: "blend" (also examengine) and matching sessionToken.

typeWhen
integrity:readyShell ready / monitoring can begin
exam:startedPrecheck done; monitoring live
exam:flagSoft integrity signal (never auto-fail)
exam:submittedIntegrity session ended

Typical layout: your questions UI beside/under the Integrity iframe (see partner demo). Candidate answers in your app; Blend watches.

Raw iframe (no SDK)

<iframe
  src="https://exam-engine-staging.vercel.app/integrity/ses_…?partnerName=YourProduct"
  allow="camera; microphone; display-capture; fullscreen"
  allowfullscreen
  style="width:100%;height:100%;border:0"
></iframe>

7. End an Integrity session

When the candidate finishes your exam:

POST /api/exam/{sessionToken}/integrity/end

No API key — the session token is the capability. Used by the Integrity runtime; your host may call it when the attempt ends. For full proctoring, the response may include sebQuitUrl to unlock Safe Exam Browser.

8. Fetch the report

GET /api/v1/sessions/{sessionId} — use the UUID id from create, not ses_….

curl -sS "https://exam-engine-staging.vercel.app/api/v1/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $KEY"
{
  "id": "…",
  "kind": "integrity",
  "candidate_ref": "student-42",
  "external_ref": "gmat-mock-991",
  "status": "graded",
  "risk_score": 12,
  "identity_status": "verified",
  "integrity": {
    "risk_score": 12,
    "breakdown": {},
    "timeline_event_count": 40
  },
  "answers": [],
  "mode": "test"
}

Dashboard reviewers can also open the evidence pack under Review.

9. Proctoring levels

LevelCredits (approx.)Behavior
none10Minimal / no live proctoring path
lockdown-lite50Browser lockdown + webcam + env scan + live AI flags
full100SEB + phone second camera + stricter rules

Live AI flags only — never auto-fail. Environment scan runs when level ≠ none. Full mode auto-manages SEB config keys.

10. Webhooks

Configure under Dashboard → Developers → Webhooks.

Event typeMeaning
session.completedSession finished
report.readyReport available via GET
integrity.flag_raisedElevated risk / flag
webhook.testManual test

Headers

  • X-Blend-Signature — HMAC-SHA256 hex of raw body
  • X-Blend-Event — event type
  • Legacy: X-ExamEngine-Signature, X-ExamEngine-Event

Envelope

{
  "id": "evt_…",
  "type": "report.ready",
  "created": 1710000000,
  "data": {
    "session_id": "…",
    "exam_id": "…",
    "candidate_ref": "…",
    "status": "…",
    "risk_score": 12
  }
}

Verify (Node)

import { createHmac, timingSafeEqual } from "crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || "");
  return a.length === b.length && timingSafeEqual(a, b);
}

Sign the raw body bytes. Deliveries retry with backoff; dead after max attempts. Test via dashboard or POST /api/v1/sessions/{id}/webhook-test.

11. Theming

Theme keyQuery param
primarythemePrimary
backgroundthemeBackground
foregroundthemeForeground
mutedthemeMuted
borderthemeBorder
radiusthemeRadius
partnerNamepartnerName

The Powered by Blend watermark cannot be removed.

12. Common errors

StatuserrorCause
401unauthorizedMissing/invalid API key
400bad_requestInvalid JSON / missing Idempotency-Key
402insufficient_creditsNot enough Blend Credits
404not_foundSession not in your org
429rate_limit_exceededSlow down

13. End-to-end flow

1. Candidate starts mock in YourApp
2. YourApp backend:
     POST /api/v1/integrity/sessions  →  { id, session_token }
3. YourApp frontend:
     Blend.mount(..., { sessionToken, mode: "integrity", onEvent })
4. Candidate completes YourApp questions
5. YourApp (or Integrity UI):
     POST /api/exam/{sessionToken}/integrity/end
6. YourApp backend:
     GET /api/v1/sessions/{id}
     and/or handle webhooks
7. YourApp applies your pass / review policy

14. Legacy Assessment API (optional)

Only if you want Blend-hosted questions via API. Prefer Platform UI for that; prefer Integrity when you own content.

MethodPathNotes
POST/api/v1/examsCreate exam (Idempotency-Key)
POST/api/v1/exams/{id}/questionsAdd questions
POST/api/v1/exams/{id}/sessions→ /exam/ses_…
GET/api/v1/sessions/{id}kind: "assessment"

Mount with mode omitted or "exam".

15. Security checklist

  • API keys only on the server; rotate if leaked
  • HTTPS everywhere
  • Verify X-Blend-Signature on every webhook
  • Treat ses_… as a bearer capability — don’t log publicly
  • Don’t trust client-only scores for Integrity — use GET report + human review
  • Production: sk_live_, live Didit, sufficient credits

Need help integrating?

Open Billing → Developers → Contact sales for volume pricing and a scoped free integration. Try the NovaHire partner demo with a real ses_… from your test key.

Full markdown copy: docs/DEVELOPERS.md · Health: /api/health