Getting Started
YeboVerify verifies that a person is who they claim to be, from an ID document and a selfie. This page takes you from nothing to a working verification.
Every command below is real and runnable, and every response shown is actual output — not an illustration.
Using an AI coding assistant?
Install the MCP server and let your agent do the integration:
claude mcp add yeboverify -- npx -y github:omegathesecond/yeboverify-mcpNo API key needed to start. Then ask it to "set up YeboVerify in this project" — it calls start_here, gets the full briefing, and can create your account for you. See AI Agents & MCP. There is also a machine-readable index of these docs at llms.txt.
Step 1 — Get your API keys
Sign up in the browser at yeboverify.com/signup, or do it from the terminal — no account or key required to make this call:
curl -X POST https://api.yeboverify.com/v1/signup \
-H "Content-Type: application/json" \
-d '{"name":"Acme Financial Services","email":"you@company.com"}'You get back two keys:
{
"success": true,
"data": {
"businessId": "cmu5wlqba0f28s6010nudz0n1",
"name": "Acme Financial Services",
"email": "you@company.com",
"plan": "free",
"apiKey": "yv_live_6tb4xK…",
"apiKeyPrefix": "yv_live_6tb4",
"testApiKey": "yv_test_E9TfCL…",
"testApiKeyPrefix": "yv_test_E9Tf",
"passwordSet": false
}
}| Key | What it does |
|---|---|
yv_test_… | Sandbox. Never bills, never calls a real AI provider, returns deterministic results. Start here. |
yv_live_… | Production. Billable, runs the real checks on real documents. |
Both keys are shown exactly once
They are not retrievable later — save them now (in .env, never in source control). If you lose the live key, POST /v1/account/recover-api-key emails you a reset link.
Store the sandbox key and use it for everything until you go live:
export YEBOVERIFY_API_KEY=yv_test_E9TfCL…Step 2 — Your first verification
Submit an ID photo and a selfie. With a yv_test_ key this costs nothing and never touches a real provider:
curl -X POST https://api.yeboverify.com/v1/verify \
-H "X-API-Key: $YEBOVERIFY_API_KEY" \
-F "idFront=@id-front.jpg" \
-F "selfie=@selfie.jpg" \
-F "externalRef=user_123"{
"success": true,
"data": {
"verificationId": "vrf_tpmd6kfkuc1of0ci",
"status": "pending",
"message": "Verification submitted successfully. Results will be sent via webhook."
}
}externalRef is your own user or order ID. It comes back on every result, so you never have to store YeboVerify's IDs to know who a result belongs to.
Fetch the result a few seconds later:
curl https://api.yeboverify.com/v1/verifications/vrf_tpmd6kfkuc1of0ci \
-H "X-API-Key: $YEBOVERIFY_API_KEY"{
"verificationId": "vrf_tpmd6kfkuc1of0ci",
"externalRef": "user_123",
"status": "COMPLETED",
"decision": "APPROVED",
"decisionReason": "[TEST MODE] Simulated approval (simulate=approved)",
"confidence": "high",
"faceScore": 98,
"ocrConfidence": 95,
"livenessDecision": "live",
"extractedData": {
"surname": "Testperson",
"names": "Sandbox Jane",
"dateOfBirth": "1990-01-01",
"sex": "F",
"idNumber": "TESTMODE0000",
"nationality": "ZZ",
"documentType": "NATIONAL_ID",
"issueDate": "2020-01-01",
"expiryDate": "2030-01-01",
"issuingCountry": "ZZ"
}
}That's a full verification. decision is the answer you act on: APPROVED, REJECTED, or NEEDS_REVIEW.
Test the paths that aren't the happy one
Approval is the easy case. Add simulate to force any other outcome — sandbox keys only:
curl -X POST https://api.yeboverify.com/v1/verify \
-H "X-API-Key: $YEBOVERIFY_API_KEY" \
-F "idFront=@id-front.jpg" -F "selfie=@selfie.jpg" \
-F "simulate=rejected"{
"decision": "REJECTED",
"decisionReason": "[TEST MODE] Simulated rejection — face match below threshold (simulate=rejected)"
}simulate | Result |
|---|---|
approved | Approved (the default when you omit it) |
rejected | Rejected — face match below threshold |
needs_review | Routed to manual review |
liveness_spoofed | Rejected — anti-spoofing caught a presentation attack |
expired_document | Rejected — the ID has expired |
Build the rejected and needs_review branches now. They are the ones that break in production. Passing simulate with a live key is rejected with 400, so this can never silently affect real traffic.
Step 3 — Choose your integration path
| Path | Use it when | You build |
|---|---|---|
| Hosted flow | You want the shortest route to production | Nothing — we host the capture UI |
| Direct upload | You already have the images | Your own capture UI |
| KYB | You're verifying a company, not a person | See KYB |
Hosted flow (recommended)
Create a session, and send the user to the URL it returns:
curl -X POST https://api.yeboverify.com/v1/sessions/create \
-H "X-API-Key: $YEBOVERIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId":"user_123"}'{
"success": true,
"session": "eyJ1c2VySWQiOiJ1c2VyXzEyMyIs…",
"verifyUrl": "https://verify.yeboverify.com#session=eyJ1c2VySWQiOiJ1c2VyXzEyMyIs…",
"expiresIn": 1800
}The user opens verifyUrl, captures their ID and selfie on verify.yeboverify.com, and the result arrives on your webhook. You build no camera UI, no upload handling, no liveness challenge.
Note the shape of that response — see Response format below, because it is the one endpoint family that differs.
You deliver the link. YeboVerify does not.
verifyUrl is returned to you, not sent anywhere. YeboVerify never contacts your user — no SMS, no email, no verification code, ever. Redirect their browser to it, render it as a button, or send it over whatever channel you already use.
Keep the #. The token is in the URL fragment so it never reaches server logs, analytics, or the Referer header of the next page. Rewriting it to ?session= leaks a live credential.
The link expires in expiresIn seconds (30 minutes) — create it when the user is ready to verify, not hours ahead.
Direct upload
POST /v1/verify is what you already used in Step 2. Two narrower variants exist when you don't need the full check: /v1/verify/selfie-match (face comparison only) and /v1/verify/id-document.
Step 4 — Receive the result
Register a webhook once:
curl -X PUT https://api.yeboverify.com/v1/account/webhook \
-H "X-API-Key: $YEBOVERIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://yourapp.com/webhooks/yeboverify",
"webhookSecret": "whsec_a_long_random_string"
}'The hosted flow delivers two events: verification.submitted the moment the user finishes uploading, then verification.completed with the decision (~5–10s later). Flip your own KYC state to "in progress" on submitted — never on session creation, since the user might abandon — and decide on completed.
Two rules for the handler:
- Verify the signature on every delivery (
X-YeboVerify-Signature-V2, HMAC-SHA256). An unverified webhook endpoint is an open door. - De-duplicate on
eventId. Retries are expected, so your handler must be idempotent.
Both are covered with code in Webhooks.
Step 5 — Go live
- Swap
yv_test_…foryv_live_…. - Set a spend limit —
PUT /v1/account/spend-limit— so a bug can't run up a bill. - Point the webhook at production and confirm it's reachable from the internet.
- Re-test
rejectedandneeds_reviewagainst your real UI.
Reference
Base URL
https://api.yeboverify.comhttps://dev-api.yeboverify.com is a separate pre-production deployment. It is not related to which key you use — both key types work against either host.
Authentication
Every request carries your key in a header:
X-API-Key: yv_test_…Response format
Almost every endpoint wraps its payload in data:
{ "success": true, "data": { } }Errors are uniform:
{
"success": false,
"error": { "code": "ERROR_CODE", "message": "Human-readable error message" }
}One exception: /v1/sessions/* responds flat
The session endpoints put their fields next to success, with no data wrapper:
{ "success": true, "session": "…", "verifyUrl": "…", "expiresIn": 1800 }So response.data.verifyUrl is undefined — read response.verifyUrl. This one difference is the most common cause of a broken hosted-flow integration; it broke our own Node SDK. Every other endpoint in these docs is wrapped.
See Error Codes for the full list.
Next steps
- SDKs — Node, React, React Native, Flutter, Kotlin, Swift
- AI Agents & MCP — integrate with Claude, Cursor, or any MCP client
- Webhooks — signature verification, retries, event catalogue
- API Reference — every endpoint
- KYB — company verification
- White Label — put your own brand on the hosted flow