v1 · REST · JSON

Developer documentation.

KLYR Verify exposes a versioned REST API for identity verification, trust scoring, consent management, and compliance reporting.

Authentication

All API requests require a Bearer token or API key. Use sandbox keys during development and production keys in deployment.

curl https://api.klyr.io/v1/kyc/list \
  -H "Authorization: Bearer sk_live_••••••"
Request format

All requests and responses use JSON. Files are base64-encoded. Maximum file size is 5 MB per upload.

  • Accepted: JPG, PNG, PDF
  • Max: 5 MB per file
  • Rate limited per API key

All endpoints

MethodPathDescription
POST/v1/kyc/submitSubmit a new KYC verification
GET/v1/kyc/{id}/statusCheck submission status
GET/v1/kyc/{id}/resultFull verification result
POST/v1/kyc/{id}/resubmitResubmit after rejection
GET/v1/kyc/listPaginated list of submissions
POST/v1/webhooks/registerRegister a webhook URL
DELETE/v1/webhooks/{id}Remove a webhook
POST/v1/auth/tokenGet an access token
GET/v1/identity/{klyr_id}Get identity by KLYR ID
GET/v1/trust-score/{klyr_id}Get trust score
POST/v1/consent/requestRequest identity sharing
POST/v1/deletion-requestSubmit data deletion request
Auth

Authentication

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

sk_test_*
Sandbox

Deterministic test results. No real data used.

sk_live_*
Production

Real verifications. Live data. Billed per check.

Obtain an access token

POST /v1/auth/token
Content-Type: application/json

{
  "email": "you@company.com",
  "password": "your_password"
}

→ 200 OK
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600
}
POST

Submit KYC verification

Submit identity details, document image, and selfie. Document and selfie must be base64-encoded.

curl
curl -X POST https://api.klyr.io/v1/kyc/submit \
  -H "Authorization: Bearer sk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "full_name": "Ada Lovelace",
    "date_of_birth": "1990-12-10",
    "email": "ada@example.com",
    "phone": "+2348012345678",
    "document_type": "nin_slip",
    "document": "<base64_encoded_file>",
    "selfie": "<base64_encoded_image>",
    "liveness_action": "blink",
    "consent_given": true
  }'

Request parameters

ParameterTypeRequiredDescription
full_namestring
Required
Applicant's full name
date_of_birthstring (date)
Required
YYYY-MM-DD
emailstring (email)
Required
Email address
phonestring
Required
Phone with country code
document_typeenum
Required
passport / nin_slip / bvn_document / drivers_license
documentbase64
Required
Document image (max 5MB)
selfiebase64
Required
Selfie image (max 5MB)
liveness_actionstring
Required
blink / smile / turn_head
consent_givenboolean
Required
Must be true
countryenum
Optional
ng / ke / gh / za / ci (default: ng)

Response

→ 201 Created
{
  "id": "8b3f7a2e-1c5d-4e9f...",
  "status": "PENDING",
  "submitted_at": "2026-06-14T10:21:00Z"
}
GET

Check verification status

Poll or check the status of any submission using its ID.

GET /v1/kyc/8b3f7a2e-1c5d-4e9f.../status

→ 200 OK
{
  "id": "8b3f7a2e-...",
  "status": "APPROVED",
  "document_type": "nin_slip",
  "summary": {
    "face_match_score": 92,
    "ocr_confidence": 88,
    "liveness_passed": true
  }
}

Status values

PENDING
Awaiting processing
APPROVED
All checks passed
REJECTED
One or more checks failed
FLAGGED
Needs manual review
GET

Get full verification result

Complete verification result including AI scores, extracted data, and signed file URLs.

GET /v1/kyc/8b3f7a2e-.../result

→ 200 OK
{
  "id": "8b3f7a2e-...",
  "status": "APPROVED",
  "full_name": "Ada Lovelace",
  "ai_result": {
    "extracted": { "full_name": "Ada Lovelace", "document_number": "NIN12345678" },
    "face_match_score": 94.2,
    "liveness_passed": true
  },
  "signed_urls": {
    "document": "https://.../doc.jpg?token=...&expires=900",
    "selfie": "https://.../selfie.jpg?token=...&expires=900"
  }
}
⚠️ Signed URLs expire after 15 minutes

Download and store files on your end if you need long-term access.

POST

Resubmit verification

Allow a user to resubmit after rejection. Resets status to PENDING.

POST /v1/kyc/8b3f7a2e-.../resubmit

{
  "full_name": "Ada Lovelace",
  "email": "ada@example.com"
}

→ 200 OK { "ok": true, "status": "PENDING" }
GET

List submissions

Paginated list of KYC submissions. Supports filtering by status.

GET /v1/kyc/list?page=1&limit=20&status=FLAGGED

→ 200 OK
{
  "rows": [{ "id": "...", "full_name": "Ada Lovelace", "status": "FLAGGED" }],
  "count": 1, "page": 1, "limit": 20
}

Query parameters

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Max 100
statusstringFilter by status
v1

Identity & Trust Score API

Retrieve identity information and trust scores for verified users. Requires active consent grant.

GET
/v1/identity/{klyr_id}

Returns basic identity info (name, email, phone) for a KLYR ID — requires consent.

GET
/v1/trust-score/{klyr_id}

Returns trust score (0–100), tier, and verification history — requires consent.

POST
/v1/consent/request

Request an identity sharing consent grant from a user.

Events

Webhooks

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

Register a webhook

POST /v1/webhooks/register

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

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

Webhook payload

POST /your-webhook-url
X-Klyr-Signature: sha256=...
X-Klyr-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));
}
Compliance

Data deletion (right to deletion)

End users can request deletion of personal data. The flow requires email verification before processing begins.

Submit a deletion request

curl -X POST https://api.klyr.io/v1/deletion-request \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "full_name": "Ada Lovelace",
    "reason": "No longer use this service"
  }'
PENDING
VERIFIED
IN_PROGRESS
COMPLETED
REJECTED

Users can track their deletion request at /deletion-request.

Sandbox

Sandbox environment

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

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 code and message.

→ 400 Bad Request
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "document: File exceeds 5MB limit"
  }
}
StatusCodeDescription
400
VALIDATION_ERRORInvalid request
401
UNAUTHORIZEDMissing/invalid API key
403
FORBIDDENNo permission
404
NOT_FOUNDResource not found
409
DUPLICATEDuplicate detected
429
RATE_LIMITEDToo many requests
500
INTERNAL_ERRORServer error
Guide

Best practices

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 consent will be rejected.

Test in sandbox first

Develop against sandbox before switching to production API keys.

Validate country codes

Pass country: 'ke' for Kenya, 'gh' for Ghana, etc. Defaults to Nigeria (ng).

Integration

Embeddable widget

Embed the KYC flow directly into your app. React component, script tag, or iframe.

React component

jsx
import { KlyrWidget } from "@klyr/widget";

function VerifyPage() {
  return (
    <KlyrWidget
      apiKey="pk_live_••••••"
      environment="production"
      onComplete={(r) => console.log(r.submission_id)}
    />
  );
}

Script tag

html
<script src="https://cdn.klyr.io/widget/v1/klyr.js"></script>
<div id="klyr-widget"></div>
<script>
  KlyrWidget.init({
    apiKey: "pk_live_••••••",
    container: "#klyr-widget",
  });
</script>