Developer documentation.
KLYR Verify exposes a versioned REST API for identity verification, trust scoring, consent management, and compliance reporting.
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_••••••"
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
Authentication
All API requests require authentication via a Bearer token. Two key types are available:
Deterministic test results. No real data used.
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
}Submit KYC verification
Submit identity details, document image, and selfie. Document and selfie must be base64-encoded.
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
| Parameter | Type | Required | Description |
|---|---|---|---|
| full_name | string | Required | Applicant's full name |
| date_of_birth | string (date) | Required | YYYY-MM-DD |
| string (email) | Required | Email address | |
| phone | string | Required | Phone with country code |
| document_type | enum | Required | passport / nin_slip / bvn_document / drivers_license |
| document | base64 | Required | Document image (max 5MB) |
| selfie | base64 | Required | Selfie image (max 5MB) |
| liveness_action | string | Required | blink / smile / turn_head |
| consent_given | boolean | Required | Must be true |
| country | enum | Optional | ng / ke / gh / za / ci (default: ng) |
Response
→ 201 Created
{
"id": "8b3f7a2e-1c5d-4e9f...",
"status": "PENDING",
"submitted_at": "2026-06-14T10:21:00Z"
}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
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"
}
}Download and store files on your end if you need long-term access.
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" }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
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| limit | integer | 20 | Max 100 |
| status | string | — | Filter by status |
Identity & Trust Score API
Retrieve identity information and trust scores for verified users. Requires active consent grant.
/v1/identity/{klyr_id}Returns basic identity info (name, email, phone) for a KLYR ID — requires consent.
/v1/trust-score/{klyr_id}Returns trust score (0–100), tier, and verification history — requires consent.
/v1/consent/requestRequest an identity sharing consent grant from a user.
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)
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));
}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"
}'Users can track their deletion request at /deletion-request.
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.
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"
}
}| Status | Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | Invalid request |
401 | UNAUTHORIZED | Missing/invalid API key |
403 | FORBIDDEN | No permission |
404 | NOT_FOUND | Resource not found |
409 | DUPLICATE | Duplicate detected |
429 | RATE_LIMITED | Too many requests |
500 | INTERNAL_ERROR | Server error |
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).
Embeddable widget
Embed the KYC flow directly into your app. React component, script tag, or iframe.
React component
import { KlyrWidget } from "@klyr/widget";
function VerifyPage() {
return (
<KlyrWidget
apiKey="pk_live_••••••"
environment="production"
onComplete={(r) => console.log(r.submission_id)}
/>
);
}Script tag
<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>