Skip to content

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:

bash
claude mcp add yeboverify -- npx -y github:omegathesecond/yeboverify-mcp

No 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:

bash
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:

json
{
  "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
  }
}
KeyWhat 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:

bash
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:

bash
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"
json
{
  "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:

bash
curl https://api.yeboverify.com/v1/verifications/vrf_tpmd6kfkuc1of0ci \
  -H "X-API-Key: $YEBOVERIFY_API_KEY"
json
{
  "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:

bash
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"
json
{
  "decision": "REJECTED",
  "decisionReason": "[TEST MODE] Simulated rejection — face match below threshold (simulate=rejected)"
}
simulateResult
approvedApproved (the default when you omit it)
rejectedRejected — face match below threshold
needs_reviewRouted to manual review
liveness_spoofedRejected — anti-spoofing caught a presentation attack
expired_documentRejected — 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 ​

PathUse it whenYou build
Hosted flowYou want the shortest route to productionNothing — we host the capture UI
Direct uploadYou already have the imagesYour own capture UI
KYBYou're verifying a company, not a personSee KYB

Create a session, and send the user to the URL it returns:

bash
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"}'
json
{
  "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:

bash
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:

  1. Verify the signature on every delivery (X-YeboVerify-Signature-V2, HMAC-SHA256). An unverified webhook endpoint is an open door.
  2. De-duplicate on eventId. Retries are expected, so your handler must be idempotent.

Both are covered with code in Webhooks.

Step 5 — Go live ​

  1. Swap yv_test_… for yv_live_….
  2. Set a spend limit — PUT /v1/account/spend-limit — so a bug can't run up a bill.
  3. Point the webhook at production and confirm it's reachable from the internet.
  4. Re-test rejected and needs_review against your real UI.

Reference ​

Base URL ​

https://api.yeboverify.com

https://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:

json
{ "success": true, "data": { } }

Errors are uniform:

json
{
  "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:

json
{ "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

Identity Verification API for Africa