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 keep | Blend provides |
|---|---|
| Question bank & UI | Identity verification (Didit) |
| Scoring of answers | Environment / room scan |
| Candidate login to your app | Live AI attention flags (never auto-fail) |
| Most of your branding | Lockdown / SEB when level is full |
| Pass/fail policy | Evidence 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 │
└─────────────────┘- Never put
sk_…in the browser, mobile binary, or URL. - Your server creates the session with the API key.
- Your client only receives
ses_…and mounts the iframe. - When the candidate finishes your exam, end Integrity, then GET the report and/or handle webhooks.
3. Get started checklist
- Sign up at
/login— workspace + Free credits are created automatically. - Dashboard → Developers → API keys → create
sk_test_…. - Optional: set webhooks and store the signing secret.
- Create an Integrity session from your backend (§5).
- Mount
Blendwithmode: "integrity"(§6). - End the session and fetch the report (§7–§8).
- Production:
sk_live_…, live identity, paid credits / contact sales for volume.
4. Authentication
API key
Authorization: Bearer sk_test_xxxxxxxx
| Prefix | Use |
|---|---|
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
| Header | Required | Notes |
|---|---|---|
| Authorization | Yes | Bearer sk_… |
| Idempotency-Key | Yes | Unique per create |
| Content-Type | Yes | application/json |
| Body field | Type | Default | Description |
|---|---|---|---|
| candidate_ref | string | candidate | Your user / roll id |
| external_ref | string | null | Your attempt / mock id |
| proctoring_level | enum | lockdown-lite | none | lockdown-lite | full |
| duration_minutes | number | exam default | Monitoring 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>| Option | Required | Description |
|---|---|---|
| sessionToken | Yes | ses_… from your backend |
| baseUrl | Yes* | Blend origin (*from script src if omitted) |
| mode | For Integrity | "integrity" → /integrity/… |
| height | No | CSS height (default 100%) |
| className | No | Applied to iframe |
| theme | No | Colors + partnerName |
| onEvent | No | postMessage from iframe |
Host events
Messages use source: "blend" (also examengine) and matching sessionToken.
| type | When |
|---|---|
| integrity:ready | Shell ready / monitoring can begin |
| exam:started | Precheck done; monitoring live |
| exam:flag | Soft integrity signal (never auto-fail) |
| exam:submitted | Integrity 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/endNo 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
| Level | Credits (approx.) | Behavior |
|---|---|---|
| none | 10 | Minimal / no live proctoring path |
| lockdown-lite | 50 | Browser lockdown + webcam + env scan + live AI flags |
| full | 100 | SEB + 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 type | Meaning |
|---|---|
| session.completed | Session finished |
| report.ready | Report available via GET |
| integrity.flag_raised | Elevated risk / flag |
| webhook.test | Manual test |
Headers
X-Blend-Signature— HMAC-SHA256 hex of raw bodyX-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 key | Query param |
|---|---|
| primary | themePrimary |
| background | themeBackground |
| foreground | themeForeground |
| muted | themeMuted |
| border | themeBorder |
| radius | themeRadius |
| partnerName | partnerName |
The Powered by Blend watermark cannot be removed.
12. Common errors
| Status | error | Cause |
|---|---|---|
| 401 | unauthorized | Missing/invalid API key |
| 400 | bad_request | Invalid JSON / missing Idempotency-Key |
| 402 | insufficient_credits | Not enough Blend Credits |
| 404 | not_found | Session not in your org |
| 429 | rate_limit_exceeded | Slow 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 policy14. Legacy Assessment API (optional)
Only if you want Blend-hosted questions via API. Prefer Platform UI for that; prefer Integrity when you own content.
| Method | Path | Notes |
|---|---|---|
| POST | /api/v1/exams | Create exam (Idempotency-Key) |
| POST | /api/v1/exams/{id}/questions | Add 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