Getting Started
Quickstart: Datapoint in minutes
Start verifying identities and preventing fraud with Datapoint. Get API keys, run your first check and go live in a single afternoon.
Welcome to the Datapoint Documentation. This guide is your starting point to explore and activate Datapoint’s verification and fraud-prevention capabilities. Whether you’re building a fintech app, launching an e-commerce platform, managing compliance or scaling digital operations, our tools help you integrate quickly, cut risk, and ship with confidence.
#Environments
Use the sandbox for sample data and production for live verifications. Paths in this docs site are relative to the base URL you choose (for example /subject/{id}). Do not mix sandbox keys with the production base.
Production
Live verifications. Real subjects, real settlement.
https://api.datapoint.co.ke/graphqlwss://api.datapoint.co.ke/graphqllive_sk_…Sandbox
Start hereBuild and test with seed subjects and simulated settlement. Mirrors production behaviour; no PII submitted here is retained beyond 7 days.
https://sandbox.datapoint.co.ke/graphqlwss://sandbox.datapoint.co.ke/graphqltest_sk_…Sandbox is safe to break
Submit anything you like. Sandbox data is purged after 7 days and no real settlement is triggered. Production keys reject requests to the sandbox endpoint and vice versa.
#Create API keys
Once your access is approved you’ll receive a secret and publishable key for each environment. Keep secret keys server-side, and rotate them from the client portal.
- Open the client portal and switch to the Sandbox workspace.
- Create a new API key, name it after the service that will use it.
- Copy the secret immediately — we show it once.
- Store it in your secret manager (never commit to git).
#Authentication
Datapoint uses bearer tokens on every request. Send your secret in the Authorization header. Keys are scoped per environment and rate-limited independently.
Authorization: Bearer test_sk_live_demo_000#Make your first request
The example below submits a lightweight identity verification against the sandbox. Swap in your sandbox key, run it, and inspect the response.
curl https://sandbox.datapoint.co.ke/graphql \
-H "Authorization: Bearer test_sk_live_demo_000" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation($input: VerificationRequestInput!) { requestVerification(input: $input) { success data { id status } } }",
"variables": {
"input": {
"requester": { "name": "Savanna Digital Ltd", "email": "api@savannadigital.co.ke" },
"scope": { "checks": ["IDENTITY"], "purpose": "PRE_EMPLOYMENT" },
"subject": { "firstName": "Jane", "lastName": "Mwangi", "idNumber": "12345678" }
}
}
}'Expect this response
{"data":{"requestVerification":{"success":true,"message":"Verification request accepted"}}}
#Identity Hub
A composable identity verification surface built to fit your market and use case. Whether you’re verifying individuals (KYC) or businesses (KYB), the Identity Hub supports:
- Government-issued ID verification and document analysis.
- Biometric verification (facial match, liveness detection).
- Address verification (digital and field-based).
- Phone and email ownership proofs.
#ID & Passport Checks
Submit a national ID number, alien ID, or passport number. Datapoint cross-references the relevant register (IPRS for national IDs, ECITIZEN for passports) and returns a name match, date of birth, gender, and citizenship status.
mutation InstantCheck($input: InstantCheckRequestInput!) {
instantCheckRequest(input: $input) {
success
message
data { id }
}
}
# variables — ID check
{
"input": {
"personalInfo": { "firstName": "Jane", "lastName": "Mwangi",
"phone": "254712345678", "email": "j.mwangi@gmail.com" },
"identificationInfo": { "idNumber": "12345678", "idFront": null, "idBack": null },
"selectedChecks": ["IDENTITY"],
"consent": true
}
}Sandbox ID numbers
12345678 → PASS (Jane Mwangi) | 28944075 → PASS (vehicle check included) | 99999999 → FAIL | 00000000 → ESCALATED
#Biometric Verification
Biometric verification confirms that the person presenting an ID is its genuine holder. Create a Didit session, open the session URL in an embedded web view or redirect, and poll or subscribe for the result. Datapoint stores only the session outcome — not the raw biometric data.
mutation CreateSession(
$firstName: String, $lastName: String,
$email: String, $phone: String,
$documentNumber: String
) {
createDiditSession(
firstName: $firstName, lastName: $lastName,
email: $email, phone: $phone,
documentNumber: $documentNumber
) {
diditSessionId
sessionToken
url # redirect user here
status
}
}After the user completes the flow, Datapoint fires a verification.completed webhook and attaches the liveness score and face-match confidence to the certificate.
#Document Verification
Compare an ID photograph against a live selfie to confirm the document belongs to the person presenting it. Upload both files as multipart form fields; the response includes a similarity confidence score and a pass/fail decision.
mutation CompareIdPhoto($idPhoto: Upload!, $selfie: Upload!) {
compareIdPhoto(idPhoto: $idPhoto, selfie: $selfie) {
isIdentical
confidence
faceRectangle { top left width height }
}
}Expect this response
{ "compareIdPhoto": { "isIdentical": true, "confidence": 0.962, "faceRectangle": { "top": 142, "left": 118, "width": 224, "height": 296 } } }
#Liveness Capture
Passive liveness runs during the biometric session. No explicit action is required from the user — the SDK analyses motion and depth cues automatically. After the session closes, call refreshDiditSession to retrieve the liveness score and whether the check passed.
mutation RefreshSession($diditSessionId: String!) {
refreshDiditSession(diditSessionId: $diditSessionId) {
status
terminal
outcome
livenessScore
livenessPassed
faceMatchConfidence
holderName
documentNumber
documentExpiresAt
warnings
}
}#Initiate Verification
Use requestVerification to submit a B2B verification request on behalf of an organisation. Specify the scope (which checks to run), the subject, and a requester contact. Datapoint acknowledges the request and begins processing asynchronously.
mutation RequestVerification($input: VerificationRequestInput!) {
requestVerification(input: $input) {
success
message
}
}
# variables
{
"input": {
"requester": {
"organisation": "Savanna Digital Ltd",
"contactPerson": "Wanjiku Maina",
"email": "api@savannadigital.co.ke",
"telephone": "+254712000000"
},
"verification": {
"category": "KYC",
"requiredTiming": "STANDARD",
"subjectVolume": "1-10",
"scope": {
"mode": "recommended",
"selectedChecks": ["IDENTITY", "CRIMINAL_RECORD"],
"optionalChecks": null,
"preset": null,
"customScope": null
},
"additionalDetails": null,
"decisionSupported": null
},
"authorityConfirmed": true,
"website": "https://savannadigital.co.ke"
}
}#Check Status
Poll the status of an in-progress verification by its certificate number. The status field moves through PENDING → GENERATING → READY (or FAILED). Subscribe to the certificate event instead if you want a push-based update.
query GetCertificate($certNo: String!) {
certificateById(certNo: $certNo) {
certNo
status # PENDING | GENERATING | READY | FAILED
finalOutcome # PASS | FAIL | EXPIRED
issuedAt
clientName
verificationType
}
}
# variables
{ "certNo": "DP-SANDBOX-00001" }#Verification Reports
A completed verification produces a certificate. Fetch it with certificateById to retrieve the full check breakdown. Each check carries an extras field with the raw source-API payload (IPRS, NTSA, DCI, BRS, etc.).
query VerificationReport($certNo: String!) {
certificateById(certNo: $certNo) {
certNo
clientName
clientEmail
clientPhone
verificationType
finalOutcome
issuedAt
expiresAt
status
checks {
name
result # PASS | FAIL
documentNo
extras # raw source-API payload
createdAt
}
}
}extras field contains raw third-party data
The shape of extras varies by check type: NTSA returns vehicle and owner objects; IPRS returns identity fields; DCI returns certificate metadata. Treat extras as untyped JSON in your client.
#Webhook signatures
Every webhook delivery includes an X-Datapoint-Signature header. Compute HMAC-SHA256 of the raw request body using your webhook secret and compare with constant-time equality.
import crypto from "node:crypto";
export function verifySignature(rawBody, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(signature, "hex"),
);
}#Webhook events
Datapoint fires the following events to your registered webhook URL. Each delivery includes an X-Datapoint-Signature header for verification.
| Event | Trigger |
|---|---|
| verification.completed | Check transitions to PASSED, FAILED or ESCALATED |
| verification.escalated | Discrepancy detected — field assignment or re-upload triggered |
| verification.flag_raised | A specific check is flagged for manual review |
| document.expiring | A tracked document is within 30 days of expiry |
{
"event": "verification.completed",
"verification_id": "DP-2026-08921",
"status": "PASSED",
"subject_ref": "EMP-9021",
"timestamp": "2026-10-12T09:55:00Z",
"summary": {
"iprs_check": "MATCHED",
"academic_check": "VERIFIED",
"good_conduct": "CLEAR"
},
"report_url": "https://dpaverify.com/reports/DP-2026-08921.pdf"
}#Subscriptions
Subscribe over WebSocket to receive real-time events without polling. Use the wss:// endpoint and the graphql-ws protocol. Two subscriptions are available: certificate for verification completion events and mpesaPayment for settlement confirmation.
subscription CertificateReady($verificationRequestId: String!) {
certificate(verificationRequestId: $verificationRequestId) {
status
state # AWAITING_IDENTITY | GENERATING | READY | FAILED
message
data {
fileUrl
verificationRequestId
}
}
}#AML / PEP Screening
Screen individuals and entities against global AML and PEP (Politically Exposed Persons) watchlists. Returns a risk score, match status, and the list sources that triggered a hit. Available to approved integrations — contact us to enable this capability on your account.
Beta capability
AML / PEP screening is in closed beta. The API shape is stable but subject to minor changes before general availability. Reach out to request access.
#Risk Scoring
Composite risk scores that combine identity, criminal, financial, and behavioural signals into a single decision-ready score. Risk Scoring will be available later in 2026. Sign up for early access via the client portal.
#Kenya Business Lookup
Look up a registered Kenyan business by name, registration number, or KRA PIN. Returns the company record from the Business Registration Service (BRS), including directors, shareholders, and share allocations.
mutation BusinessCheck($input: InstantCheckRequestInput!) {
instantCheckRequest(input: $input) {
success
message
data { id }
}
}
# variables — include BRS_LOOKUP in selectedChecks
{
"input": {
"personalInfo": { "firstName": "Gavin", "lastName": "Castro",
"phone": "+254722984594", "email": "romeogavin@yahoo.com" },
"identificationInfo": { "idNumber": "24958338", "idFront": null, "idBack": null },
"selectedChecks": ["IDENTITY", "BRS_LOOKUP"],
"consent": true
}
}#KRA PIN Verification
Verify a Kenya Revenue Authority PIN against the KRA register. Confirms the PIN is active and returns the registered name and taxpayer category. Include KRA_PIN in selectedChecks on any instant check request.
mutation KraCheck($input: InstantCheckRequestInput!) {
instantCheckRequest(input: $input) {
success
message
data { id }
}
}
# variables
{
"input": {
"personalInfo": { "firstName": "Jane", "lastName": "Mwangi",
"phone": "254712345678", "email": "j.mwangi@gmail.com" },
"identificationInfo": { "idNumber": "12345678", "idFront": null, "idBack": null },
"selectedChecks": ["IDENTITY", "KRA_PIN"],
"consent": true
}
}#M-Pesa STK Push
Initiate a Safaricom M-Pesa STK push to collect payment before releasing a verification. The mutation returns a merchantRequestID you can use to subscribe to the settlement event.
mutation StkPush($phoneNumber: String!) {
initiateStkPush(phoneNumber: $phoneNumber) {
success
message
data {
merchantRequestID
verificationRequestId
}
}
}
# variables
{ "phoneNumber": "254712345678" }Sandbox behaviour
STK push always returns success in the sandbox. No real money is moved. Use merchantRequestID "sandbox-mrq-00000001" in the mpesaPayment subscription to receive a simulated SUCCESS event.
#Payment Status
Payment status moves through PENDING → SUCCESS (or FAILED) after the customer completes the STK prompt. Subscribe to mpesaPayment for a real-time push, or poll certificateById and inspect the payment field.
query CertWithPayment($certNo: String!) {
certificateById(certNo: $certNo) {
certNo
status
payment {
status # PENDING | SUCCESS | FAILED
receiptNumber
amount
paidAt
}
}
}#Settlement Events
Subscribe to mpesaPayment to receive a push notification the moment M-Pesa settles. The event carries the receipt number, amount, and final status. Use the receipt number for reconciliation.
subscription Settlement($merchantRequestID: String!) {
mpesaPayment(merchantRequestID: $merchantRequestID) {
status
resultDesc
receiptNumber
amount
merchantRequestID
}
}#Rate limits
Default limits are 60 requests per minute per key in sandbox and 240 per minute in production. Every response includes X-RateLimit-Remaining and X-RateLimit-Reset headers. Burst allowances and higher caps are available on request.
#Errors
All errors follow GraphQL convention — HTTP 200 with an errors array. HTTP 4xx is only returned for transport-level issues (missing auth, malformed JSON).
| Code | HTTP | Meaning |
|---|---|---|
| UNAUTHORIZED | 401 | Missing or invalid API key |
| FORBIDDEN | 403 | Key does not have permission for this operation |
| NOT_FOUND | 200 | Resource (certificate, session) not found |
| VALIDATION_ERROR | 200 | Input failed schema validation — check extensions.fields |
| RATE_LIMITED | 429 | Request rate exceeded — retry after X-RateLimit-Reset |
| INTERNAL_ERROR | 200 | Unexpected server error — contact support with the request ID |
{
"errors": [{
"message": "Unauthorized: missing or invalid API key",
"extensions": {
"code": "UNAUTHORIZED"
}
}]
}#Changelog
Breaking changes are versioned. Non-breaking additions (new fields, new optional inputs) are released without a version bump.
2026-10-15
Sandbox GA
- Sandbox API available at /api/sandbox/graphql with Bearer auth.
- Postman collection published — download from this page.
- Scenario-based responses: ID 12345678 PASS, 99999999 FAIL, 00000000 ESCALATED.
2026-09-01
Subscriptions
- certificate and mpesaPayment WebSocket subscriptions available on wss:// endpoint.
- graphql-ws protocol replaces subscriptions-transport-ws.
2026-07-14
Instant Checks v2
- instantCheckRequest replaces clientValidation for new integrations.
- Alien ID and passport checks added to selectedChecks.
- BRS_LOOKUP and KRA_PIN added as check types.

