v1 · REST · JSON

Verify With Vouchly.

Businesses should never need to perform KYC again if the user already has a Vouchly Identity. Three API calls. Zero document uploads. Full verified identity.

1. Request

POST /identity/request — business requests user data.

2. Preview

User enters Vouchly ID + email, sees all their data.

3. Consent

User reviews data, then approves or rejects.

4. Fetch

GET /identity/{request_id} — business receives identity package.

All endpoints

MethodPathDescription
POST/api/v1/identity/requestRequest identity access (consent)
GET/api/v1/identity/{request_id}Get verified identity package
POST/api/v1/identity/previewPreview identity data before approving
POST/api/v1/identity/approveApprove consent request
POST/api/v1/identity/rejectReject consent request
GET/api/v1/trust-score/{vouchly_id}Get trust score
GET/api/v1/kyc/level/{vouchly_id}Get verification level breakdown (0-5)
POST/api/v1/kyc/prembly-submitVerify user via Prembly (document type + number, no uploads)
POST/api/v1/kyc/submitSubmit KYC with file uploads (sandbox)
GET/api/v1/kyc/{id}/statusCheck submission status
GET/api/v1/kyc/{id}/resultFull verification result
POST/api/v1/webhooks/registerRegister a webhook
DELETE/api/v1/webhooks/{id}Remove a webhook
POST/api/v1/auth/tokenGet access token
POST/api/v1/deletion-requestSubmit data deletion request
Guide

Quickstart

Verify users without ever storing their documents. Three API calls, zero document uploads.

1

Get your API key

Generate API keys from the Vouchly dashboard. Use sk_test_* for sandbox and sk_live_* for production.

2

Request identity access

Ask the user for permission to access their verified identity. Send their Vouchly ID and the fields you need.

curl -X POST https://www.vouchly.online/api/v1/identity/request \
  -H "Authorization: Bearer sk_test_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "vouchly_id": "VOUCHLY_A3B8X9K2",
    "requested_fields": [
      "full_name",
      "dob",
      "selfie",
      "verification_status"
    ]
  }'

→ 201 Created
{
  "request_id": "req_123",
  "status": "pending_consent"
}
3

Show in-app consent screen

Build a consent screen in your own app using the app_name and requested_fields from the response. Show the user what data is being requested and let them Approve or Deny.

// Response from step 2 includes:
{
  "request_id": "req_123",
  "status": "pending_consent",
  "app_name": "Your App Name",
  "requested_fields": ["full_name", "dob", ...]
}

// Render a consent UI in your app:
┌─────────────────────────────┐
│  "Your App Name" wants:     │
│  ☑ full_name                │
│  ☑ dob                      │
│  ☑ selfie                   │
│                             │
│  [Deny]    [Approve & Share]│
└─────────────────────────────┘

// Call from your frontend (no API key needed):
POST /api/v1/identity/approve  { "request_id": "req_123" }
POST /api/v1/identity/reject   { "request_id": "req_123" }
4

Receive verified identity

Once approved, fetch the user's complete verified identity package. No more KYC ever needed.

curl -X GET https://www.vouchly.online/api/v1/identity/req_123 \
  -H "Authorization: Bearer sk_test_your_api_key"

→ 200 OK
{
  "verified": true,
  "vouchly_id": "VOUCHLY_A3B8X9K2",
  "identity": {
    "full_name": "Ada Lovelace",
    "date_of_birth": "1990-12-10"
  },
  "verification": {
    "provider": "prembly",
    "status": "verified",
    "reference": "REF_123456"
  },
  "images": {
    "selfie": "https://...",
    "document": "https://..."
  },
  "trust_score": 87,
  "verification_level": 4
}

Done. That's it — three API calls, no document uploads, full verified identity. Read the full API reference or try the API Playground.

Auth

Authentication

All API requests require authentication via a Bearer API key. Two key types are available:

sk_test_*
Sandbox

Deterministic results. No real data used. No credit deducted.

sk_live_*
Production

Real verifications. Billed per check. Credits deducted.

How to get your API key

Generate API keys from your Vouchly dashboard. Use sk_test_* keys for sandbox testing and sk_live_* keys for production.

POST

Verify With Vouchly

Request access to a user's verified identity. Creates a consent request — the user must approve before you receive data.

POST /api/v1/identity/request
Authorization: Bearer sk_test_your_api_key
Content-Type: application/json

{
  "vouchly_id": "VOUCHLY_A3B8X9K2",
  "requested_fields": [
    "full_name",
    "dob",
    "selfie",
    "verification_status"
  ],
  "app_name": "My Fintech App"
}

→ 201 Created
{
  "request_id": "req_123",
  "status": "pending_consent",
  "app_name": "My Fintech App",
  "app_logo": null,
  "requested_fields": ["full_name", "dob", "selfie", "verification_status"]
}

Request parameters

ParameterTypeRequiredDescription
vouchly_idstring
Required
User's Vouchly ID (e.g. VOUCHLY_A3B8X9K2)
requested_fieldsstring[]
Optional
Fields to request: full_name, dob, selfie, verification_status (default: all)
app_namestring
Optional
Your app name (shown on consent screen, defaults to business name)
app_logostring (URL)
Optional
URL to your app logo (shown on consent screen)
Next: build your in-app consent UI

Use app_name and requested_fields to render a consent screen in your own app. Call POST /api/v1/identity/approve from your frontend (no API key needed) when the user approves.

POST

Preview Identity Data

Users can preview their own identity data (including selfie and document images) before approving a consent request. Call this from your frontend — it's public, no API key needed. Show the returned data in your in-app consent screen so the user can review before they approve.

POST /api/v1/identity/preview
Content-Type: application/json

{
  "vouchly_id": "VOUCHLY_A3B8X9K2",
  "email": "ada@example.com"
}

→ 200 OK
{
  "verified": true,
  "vouchly_id": "VOUCHLY_A3B8X9K2",
  "identity": {
    "full_name": "Ada Lovelace",
    "email": "ada@example.com",
    "phone": "+2348012345678",
    "date_of_birth": "1990-12-10",
    "address": "123 Main St, Lagos",
    "nin": "NIN12345678"
  },
  "document": {
    "type": "nin",
    "number": "NIN12345678"
  },
  "verification": {
    "provider": "prembly",
    "status": "verified",
    "trust_score": 87,
    "verification_level": 4
  },
  "images": {
    "selfie": "https://cdn.vouchly.online/...",
    "document": "https://cdn.vouchly.online/..."
  }
}

Request parameters

ParameterTypeRequiredDescription
vouchly_idstring
Required
User's Vouchly ID
emailstring
Required
User's email address (must match the identity)
No auth required

The email must match the identity's registered email. This prevents unauthorized access to identity data.

Show this preview in your consent UI

Call this endpoint from your frontend before the user approves. It returns the user's name, DOB, phone, NIN, address, selfie image, and document image. Show these in your in-app consent screen so the user can review what they're sharing.

GET

Get Identity Data

Receive the user's complete verified identity package. Only works after the user has approved the consent request.

GET /api/v1/identity/req_123
Authorization: Bearer sk_test_your_api_key

→ 200 OK
{
  "verified": true,
  "vouchly_id": "VOUCHLY_A3B8X9K2",
  "identity": {
    "full_name": "Ada Lovelace",
    "email": "ada@example.com",
    "phone": "+2348012345678",
    "date_of_birth": "1990-12-10",
    "address": "123 Main St, Lagos",
    "nin": "NIN12345678"
  },
  "document": {
    "type": "nin",
    "number": "NIN12345678"
  },
  "verification": {
    "provider": "prembly",
    "status": "verified",
    "reference": "REF_123456",
    "trust_score": 87,
    "verification_level": 4
  },
  "images": {
    "selfie": "https://cdn.vouchly.online/...",
    "document": "https://cdn.vouchly.online/..."
  },
  "trust_score": 87,
  "verification_level": 4
}

Response fields

ParameterTypeRequiredDescription
verifiedboolean
Optional
Whether the identity is verified
vouchly_idstring
Optional
User's Vouchly ID
identity.full_namestring
Optional
User's verified full name
identity.emailstring
Optional
User's email address
identity.phonestring
Optional
User's phone number
identity.date_of_birthstring
Optional
Date of birth from verified document
identity.addressstring
Optional
Residential address (if available)
identity.ninstring
Optional
NIN or document ID number (if available)
document.typestring
Optional
Document type: nin / bvn / passport / drivers_license
document.numberstring
Optional
Document number
verification.providerstring
Optional
Verification provider (prembly, youverify, vouchly)
verification.statusstring
Optional
Verification status: verified / pending
verification.trust_scoreinteger
Optional
Trust score (0–100)
verification.verification_levelinteger
Optional
Verification level (0–5)
images.selfiestring (URL)
Optional
Signed selfie image URL (expires in 15 min)
images.documentstring (URL)
Optional
Signed document image URL (expires in 15 min)
trust_scoreinteger
Optional
Trust score (0–100)
verification_levelinteger
Optional
Verification level (0–5)
Consent required

The user must approve the consent request before you can fetch their data. Pending or rejected requests return an error.

GET

Trust Score

Public endpoint to retrieve a user's trust score and verification level. No authentication required.

GET /api/v1/trust-score/VOUCHLY_A3B8X9K2

→ 200 OK
{
  "trust_score": 92,
  "tier": "trusted"
}

Score ranges

95–100
highly_trustedHighly Trusted
80–94
trustedTrusted
60–79
medium_riskMedium Risk
1–59
high_riskHigh Risk
0
unverifiedNot Yet Verified
Events

Webhooks

When a KYC status changes, VOUCHLY sends a signed POST to all registered webhooks.

Register a webhook

POST /api/v1/webhooks/register

{
  "url": "https://api.yourco.com/webhooks/VOUCHLY",
  "events": ["kyc.status_changed"]
}

→ 201 Created
{
  "id": "wh_abc123...",
  "url": "https://api.yourco.com/webhooks/VOUCHLY",
  "secret": "a1b2c3d4e5f6...",
  "is_active": true
}

Webhook payload

POST /your-webhook-url
X-vouchly-Signature: sha256=...
X-vouchly-Event: kyc.status_changed

{
  "submission_id": "8b3f7a2e...",
  "status": "APPROVED",
  "timestamp": "2026-06-14T10:21:00Z",
  "checks": {
    "face_match": { "passed": true, "score": 92 },
    "liveness": { "passed": true }
  }
}

Verify signature (Node.js)

javascript
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(payload, signature, secret) {
  const expected = createHmac("sha256", secret)
    .update(JSON.stringify(payload)).digest("hex");
  const received = signature.replace("sha256=", "");
  return timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}
Delivery

How results are delivered

When a verification completes, the result is delivered to your business in three ways:

1
Immediate API response

POST /api/v1/kyc/prembly-submit and POST /api/v1/kyc/submit return the full verification result synchronously, including the identity object and images when Prembly verification data is available.

2
Webhook callback

If you register a webhook, VOUCHLY sends a signed POST to your URL when the status changes. The webhook payload includes the submission_id — use GET /api/v1/kyc/{id}/result to fetch the full data.

3
Poll via GET /result

Fetch the complete verification result — including ai_result, prembly_response (identity data, photos, signature), and signed document URLs — at any time:

GET /api/v1/kyc/{id}/result

→ 200 OK
{
  "id": "8b3f7a2e-...",
  "status": "APPROVED",
  "full_name": "Ada Lovelace",
  "email": "ada@example.com",
  "document_type": "nin",
  "ai_result": {
    "prembly_response": { ... },
    "photo": "...",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "gender": "Female",
    "nationality": "Nigerian",
    "residentialAddress": "42 Marina Street"
  },
  "signed_urls": {
    "document": "https://...",
    "selfie": "https://..."
  }
}
Identity data fields available in responses
When verified via Prembly (NIN, BVN, Passport, Driver's License), the response includes:

Identity object

  • gender
  • marital_status
  • nationality
  • state_of_origin
  • lga_of_origin
  • lga_of_residence
  • address
  • phone_number
  • bvn / nin
  • registration_date

Images object

  • photo — government database photo
  • signature — signature image
  • selfie — user selfie (via GET /result)
  • document — ID document (via GET /result)

Verification object

  • provider
  • trust_score
  • verification_level (1-5)
Sandbox

Sandbox environment

Mirrors production with deterministic test data. Sandbox keys (sk_test_*) return predictable results without deducting credits.

Deterministic results

Use the document number to control the result: APPROVED, REJECTED, or FLAGGED.

Isolated data

Sandbox data is completely isolated from production. Create, test, and delete freely.

Errors

Error codes

All errors return a consistent JSON response with a flat error message.

→ 400 Bad Request
{
  "error": "document: File exceeds 5MB limit"
}

→ 401 Unauthorized
{
  "error": "Invalid API key"
}

→ 402 Payment Required
{
  "error": "Insufficient verification credits"
}
StatusWhen
400
Invalid request body, missing required fields, or validation error
401
Missing or invalid API key
402
Insufficient verification credits
404
Resource not found (submission, identity, webhook)
500
Internal server error — contact support if persistent
Guide

Best practices

Use consent-based identity request

Always use POST /identity/request to ask users for data. Never access identity data without explicit user approval.

Handle webhooks idempotently

Always check the submission_id before processing. Duplicate webhooks may be sent under rare circumstances.

Poll status as fallback

Use GET /status with exponential backoff if you miss a webhook. Max 10 requests/min per submission.

Use signed URLs immediately

Document URLs expire after 15 minutes. Download files immediately.

Always set consent_given: true

Submissions without explicit user consent will be rejected.

Test in sandbox first

Develop against sandbox (sk_test_) before switching to production (sk_live_) API keys.

Integration

Integration Guide

Add "Verify With Vouchly" to your app in 5 steps. No redirect. No iframe. Full in-app consent with user data preview (selfie, document images).

1

Backend: Request identity access

Your backend calls POST /api/v1/identity/request with the user's Vouchly ID. The response includes app_name and requested_fields so your frontend can build the consent UI.

2

Frontend: Fetch user data preview (public API)

From your frontend, call POST /api/v1/identity/previewno API key needed. This returns the user's full identity data including their selfie image and document image from their original KYC (Prembly). Show these in your consent UI so the user can review before approving.

3

Frontend: Show consent UI with data preview

Render a consent screen in your app showing:

  • Your app name (from step 1)
  • What data is being requested (from step 1)
  • User's personal info — name, DOB, email, phone, NIN, address (from step 2)
  • User's selfie photo and document photo (from step 2)
  • Trust score and verification level (from step 2)
  • Approve / Deny buttons
4

Frontend: Approve or reject

When the user clicks Approve, call POST /api/v1/identity/approve from your frontend. No API key needed. Same for reject.

5

Backend: Fetch the verified identity

After approval, your backend calls GET /api/v1/identity/{request_id} with your API key to receive the full verified identity package.

Complete code example (React + Node.js)

Backend (Node.js / any server)

// Step 1: Request identity access
const { request_id, app_name, requested_fields } = await fetch(
  "https://www.vouchly.online/api/v1/identity/request",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      vouchly_id: req.body.vouchly_id,
      requested_fields: ["full_name", "dob",
        "email", "phone", "selfie", "trust_score"]
    })
  }
).then(r => r.json());

// Return to frontend
res.json({ request_id, app_name, requested_fields });

// Step 5: After user approves, fetch identity
const identity = await fetch(
  "https://www.vouchly.online/api/v1/identity/" + request_id,
  {
    headers: { "Authorization": "Bearer YOUR_API_KEY" }
  }
).then(r => r.json());
// Use identity data in your app

Frontend (React / plain JS)

// Step 2: Fetch user's data preview (NO API KEY)
const preview = await fetch(
  "https://www.vouchly.online/api/v1/identity/preview",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      vouchly_id: userVouchlyId,
      email: userEmail // must match their identity
    })
  }
).then(r => r.json());

// Returns: identity fields + selfie URL + document URL

// Step 3: Render consent UI
<ConsentScreen
  appName={app_name}
  requestedFields={requested_fields}
  preview={preview} // has preview.images.selfie etc
  // Step 4: Approve
  onApprove={() => fetch(
    "https://www.vouchly.online/api/v1/identity/approve",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ request_id })
    }
  )}
  onReject={() => fetch(
    "https://www.vouchly.online/api/v1/identity/reject",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ request_id })
    }
  )}
/>

Full HTML example (plain JS)

If you're not using a framework, here's a complete HTML page that implements the full flow:

<!DOCTYPE html>
<html>
<head><title>Verify with Vouchly</title></head>
<body>
  <button onclick="verifyWithVouchly()">Verify With Vouchly</button>

  <div id="consentModal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:1000;">
    <div style="background:white; max-width:420px; margin:60px auto; padding:24px; border-radius:12px; max-height:90vh; overflow-y:auto;">
      <h3 id="consentAppName" style="font-size:18px;"></h3>
      <p style="color:#666; font-size:13px;">wants access to your verified identity.</p>

      <!-- Data preview section -->
      <div id="previewSection" style="display:none; margin:16px 0;">
        <h4 style="font-size:14px; margin-bottom:8px;">Your data</h4>
        <div id="previewFields" style="font-size:13px; color:#333;"></div>
        <div id="previewImages" style="display:flex; gap:8px; margin-top:12px;"></div>
      </div>

      <div id="idInputSection">
        <input id="vouchlyInput" placeholder="VOUCHLY ID" style="width:100%; padding:8px; margin-bottom:8px;">
        <input id="emailInput" type="email" placeholder="Your email" style="width:100%; padding:8px; margin-bottom:8px;">
        <button onclick="loadPreview()" style="width:100%; padding:10px; background:#2563eb; color:white; border:none; border-radius:8px;">
          Preview my data
        </button>
      </div>

      <ul id="consentFields" style="margin:16px 0; padding:0; list-style:none;"></ul>

      <div id="actionButtons" style="display:none; display:flex; gap:8px;">
        <button onclick="rejectConsent()" style="flex:1; padding:10px; border:1px solid #ccc; border-radius:8px; background:white;">Deny</button>
        <button onclick="approveConsent()" style="flex:1; padding:10px; background:#2563eb; color:white; border:none; border-radius:8px;">
          Approve & Share
        </button>
      </div>
    </div>
  </div>

  <script>
  let currentRequestId = null;

  async function verifyWithVouchly() {
    const vouchlyId = prompt("Enter your Vouchly ID:");
    if (!vouchlyId) return;

    // Step 1: Backend creates identity request (proxy through your server)
    const res = await fetch("/your-backend/verify-request", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ vouchly_id: vouchlyId })
    });
    const data = await res.json();
    currentRequestId = data.request_id;

    document.getElementById("consentAppName").textContent = data.app_name;
    document.getElementById("idInputSection").style.display = "block";
    document.getElementById("consentModal").style.display = "block";
  }

  // Step 2: Load user's data preview (public, no API key)
  async function loadPreview() {
    const vId = document.getElementById("vouchlyInput").value.trim().toUpperCase();
    const email = document.getElementById("emailInput").value.trim().toLowerCase();
    if (!vId || !email) return alert("Enter your Vouchly ID and email");

    const preview = await fetch("https://www.vouchly.online/api/v1/identity/preview", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ vouchly_id: vId, email })
    }).then(r => r.json());

    if (preview.error) return alert(preview.error);

    // Show user their data
    const fields = document.getElementById("previewFields");
    fields.innerHTML = '<p>Name: ' + preview.identity.full_name + '</p>' +
      '<p>DOB: ' + (preview.identity.date_of_birth || '--') + '</p>' +
      '<p>Email: ' + preview.identity.email + '</p>' +
      '<p>Phone: ' + (preview.identity.phone || '--') + '</p>' +
      '<p>Trust Score: ' + preview.verification.trust_score + '/100</p>';

    // Show selfie and document images
    const images = document.getElementById("previewImages");
    if (preview.images.selfie) {
      images.innerHTML += '<div><img src="' + preview.images.selfie +
        '" style="width:120px; height:120px; object-fit:cover; border-radius:8px;"></div>';
    }
    if (preview.images.document) {
      images.innerHTML += '<div><img src="' + preview.images.document +
        '" style="width:120px; height:120px; object-fit:cover; border-radius:8px;"></div>';
    }

    document.getElementById("previewSection").style.display = "block";
    document.getElementById("idInputSection").style.display = "none";
  }

  // Step 4: Approve or reject (no API key needed from frontend)
  async function approveConsent() {
    await fetch("https://www.vouchly.online/api/v1/identity/approve", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ request_id: currentRequestId })
    });
    document.getElementById("consentModal").style.display = "none";
    alert("Approved! Your backend can now fetch identity data.");
  }

  async function rejectConsent() {
    await fetch("https://www.vouchly.online/api/v1/identity/reject", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ request_id: currentRequestId })
    });
    document.getElementById("consentModal").style.display = "none";
    alert("Consent denied.");
  }
  </script>
</body>
</html>

Verification Levels

Every verified identity has a verification level from 0 to 5.

LevelLabelDescription
0UnverifiedNo identity or not verified
1Email VerifiedEmail address verified
2Phone VerifiedPhone number verified
3Government ID VerifiedPassport, NIN, BVN, or license verified
4Face Match VerifiedLiveness and face match completed
5Business VerifiedBusiness-level verification completed