Server-side Implementation
CreditChek Widget (SDK) - Backend Integration Guide​
Integrate the CreditChek Widget (SDK) to perform identity verifications (BVN, NIN, Liveness) with a frictionless user experience.
Production Base URL: https://api.creditchek.africa
To keep your business informed about transaction results outcomes (objects) through our API & SDK, beside providing some On-demand API polling Endpoints, we strongly recommend the use of Webhooks. This feature enables you to initiate additional workflows based on transaction events. Whenever an event you're subscribed to occurs, Creditchek will dispatch a POST request to the Webhook URL you specified, including information about the transaction event.
To activate webhook requests, you must enable this feature on the dashboard settings and configure your correct webhook Callback URL, which should accept POST HTTP requests.
Webhook requests include an "x-auth-signature" on the header, containing your app's LiveSecretKey, for verification purposes. You can leverage this key to authenticate and authorize your unique webhook request.
Overview​
| Step | Who | What happens | Where |
|---|---|---|---|
| 0. Configure SDK | Business (dashboard) | Creates an SDK, picks branding and the verification services. Gets a Widget ID. | CreditChek Dashboard |
| 1. Create Session | Your Backend | Creates a widget session using your secret key and Widget ID. Receives a sessionId. | POST /v1/auth/widget-session/token |
| 2. Launch Widget | Your Frontend | Opens the CreditChek widget with your public key, Widget ID and the sessionId. | CreditChek.open({ publicKey, widgetId, sessionId }) |
| 3. User Verifies | End User | Completes BVN / NIN / Liveness inside the widget. | CreditChek Widget UI |
| 4. Webhook | CreditChek API | Sends the verified profile to your webhook URL. | identity.bvn.verified, identity.nin.verified |
| 5. Confirm Session | Your Backend | Confirms the session is completed. | GET /v1/auth/widget-session/:sessionId |
| 6. Fetch Data | Your Backend | (Optional) Pulls the verified profile on demand. | GET /v1/auth/widget-session/bvn-data/:sessionIdGET /v1/auth/widget-session/nin-data/:sessionId |
Step 0: Configure the SDK on the Dashboard​
This is done once per app on the CreditChek dashboard. No API call is needed from your backend.
- Sign in to the dashboard and open the app you want to use.
- Go to SDK Setup and create a new SDK.
- Fill in the configuration:
| Option | What it controls |
|---|---|
| Name | Internal name for this SDK configuration |
| Display name | Name shown to end users in the widget |
| Logo | Logo shown in the widget header |
| Brand colour | Primary colour of the widget |
| Support email | Contact shown to users if something goes wrong |
| Environment | sandbox for testing, live for production |
| Modules | Which product modules are enabled: Identity, Credit, Income, Recova, Liveness |
| Services | Verification steps the user must complete: bvn, nin, liveness (and identity, credit, income, recova) |
| Order | The order the enabled steps are shown to the user |
| Multiple approval | Whether changes need more than one approver |
- Save. Copy the Widget ID — your backend sends it as
widgetIdwhen creating sessions, and your frontend passes it to the widget aswidgetId.
One SDK setup is allowed per app.
You also need, from the dashboard's API Keys page:
- Your app's secret key — used by your backend (Step 1). Never expose it in the frontend.
- Your app's public key — used by your frontend (Step 2).
Use keys from the same environment: live secret key with live public key, sandbox with sandbox.
Finally, register a webhook URL so you receive verification events.
Step 1: Create a Widget Session (Backend)​
Your backend creates a session server-to-server, authenticated with your app's secret key. The public key is resolved from the secret key automatically:
- Live secret key → session uses your live public key.
- Sandbox secret key → session uses your sandbox public key.
POST /v1/auth/widget-session/token
token: <your_secret_key>
Content-Type: application/json
Request body​
| Field | Type | Required | Description |
|---|---|---|---|
widgetId | string | No | Your widget ID from the dashboard (the same value you pass to the widget). Links the session to that widget's branding and services. |
The session uses the services configured on the SDK. If no widgetId is sent, it defaults to bvn only.
Example request​
curl -X POST https://api.creditchek.africa/v1/auth/widget-session/token \
-H "token: $CREDITCHEK_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "widgetId": "64b2c1f0a9e3b2c4d5e6f7a8" }'
Backend example (Node.js / Express)​
const axios = require('axios');
async function createVerificationSession(req, res) {
try {
const { data } = await axios.post(
'https://api.creditchek.africa/v1/auth/widget-session/token',
{ widgetId: process.env.CREDITCHEK_WIDGET_ID },
{ headers: { token: process.env.CREDITCHEK_SECRET_KEY } }
);
res.status(200).json({ sessionId: data.data.sessionId });
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
}
Response 200​
{
"success": true,
"message": "Widget session created successfully",
"data": {
"_id": "66dee42b918f4a187d90a12f",
"publicKey": "reh+e9i7kTu9neibSbX6NTaLgx/ZfThcmPWOsDr4tL/p6X9Qf9YmU4pUNnelkm47",
"widgetId": "64b2c1f0a9e3b2c4d5e6f7a8",
"sessionId": "412275c7-5be5-433a-873d-3dd32d6b9ff2",
"status": "active",
"services": {
"bvn": { "status": "pending" },
"liveness": { "status": "pending" }
},
"expiresAt": "2026-09-29T14:10:00.000Z",
"createdAt": "2026-09-29T13:50:00.000Z",
"updatedAt": "2026-09-29T13:50:00.000Z"
}
}
Sessions expire 20 minutes after creation. Create the session when the user is about to verify, not ahead of time.
Errors​
| Status | Message | Cause |
|---|---|---|
401 | Access token is required / Invalid App token | Missing or wrong token header |
400 | No public key configured for this app | The app has no public key for that environment |
404 | SDK setup not found | widgetId does not exist |
403 | SDK setup does not belong to this app | widgetId belongs to a different app than the secret key |
Step 2: Launch the Widget (Frontend)​
Load the widget script and call CreditChek.open with your public key, your widget ID (widgetId) and the sessionId from your backend. The widget loads the SDK's branding (logo, colour, display name) and shows the configured steps in order.
<button id="verify">Verify my identity</button>
<script src="https://securedwidget.creditchek.africa/v1/inline.js"></script>
<script>
document.getElementById("verify").addEventListener("click", () => {
CreditChek.open({
publicKey: "<your public key>",
widgetId: "<your widget configuration ID>",
sessionId: "<session created on your server>",
onResult: ({ module, status }) => console.log(`${module}: ${status}`),
onError: ({ error, reason }) => console.error("CreditChek:", error, reason),
onClose: async ({ reason, sessionId }) => {
console.log("Widget closed:", reason); // "finished" | "dismissed" | "error"
if (!sessionId) return; // the session was never created
await fetch("/api/verification/confirm", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sessionId }),
});
},
});
});
</script>
Options​
| Option | Type | Required | Description |
|---|---|---|---|
publicKey | string | Yes | Your app's public key. Must be from the same environment as the secret key used in Step 1. |
widgetId | string | Yes | Your widget ID from the dashboard (Step 0) |
sessionId | string | Yes | The session created by your backend in Step 1 |
onResult | function | No | Called each time a verification step finishes |
onError | function | No | Called when the widget hits an error |
onClose | function | No | Called when the widget closes, for any reason |
Callbacks​
| Callback | Receives | Notes |
|---|---|---|
onResult | { module, status } | module is the step, e.g. bvn, nin, liveness. status is that step's result, e.g. completed or failed. Fires once per step. |
onError | { error, reason } | error identifies the failure; reason describes it. |
onClose | { reason, sessionId } | reason is finished (all steps done), dismissed (user closed it) or error. sessionId is missing if the session was never created — skip the backend check in that case. |
Callbacks are signals only. Always confirm the session on your backend (Step 5) before granting access, regardless of what
onResultoronClosereport.
Step 3: User Completes Verification​
Inside the widget the user goes through the services on the session in the configured order, e.g. enter BVN → face capture. Each service moves from pending to completed or failed, and onResult fires as each one finishes (e.g. bvn: completed).
When the user is done, the widget closes and onClose fires with reason: "finished".
Step 4: Receive the Webhook​
When a verification finishes, CreditChek sends the verified profile to your registered webhook URL.
| Event | Sent when |
|---|---|
identity.bvn.verified | BVN verification succeeds |
identity.nin.verified | NIN / vNIN verification succeeds |
Deliveries are signed with webhook-id, webhook-timestamp and webhook-signature headers. If you didn't set a custom signing secret, your app's live secret key is the signing secret.
Step 5: Confirm the Session (Backend)​
GET /v1/auth/widget-session/:sessionId
curl -X GET https://api.creditchek.africa/v1/auth/widget-session/412275c7-5be5-433a-873d-3dd32d6b9ff2
Response 200
{
"success": true,
"message": null,
"data": {
"sessionId": "412275c7-5be5-433a-873d-3dd32d6b9ff2",
"widgetId": "64b2c1f0a9e3b2c4d5e6f7a8",
"status": "completed",
"services": {
"bvn": { "status": "completed" },
"liveness": { "status": "completed" }
},
"bvn": "22233344455",
"expiresAt": "2026-09-29T14:10:00.000Z"
}
}
| Session status | Meaning |
|---|---|
active | User hasn't finished yet |
completed | All services passed — safe to proceed |
failed | At least one service failed — prompt the user to retry |
| Status code | Meaning |
|---|---|
404 | Session not found |
410 | Session expired (older than 20 minutes) |
Step 6: Fetch Verified Data (Optional)​
The webhook already contains the verified profile. Use these endpoints if you need it on demand — e.g. to show the user's details right away, as a fallback if your webhook listener missed a delivery, or for reconciliation.
GET /v1/auth/widget-session/bvn-data/:sessionId
GET /v1/auth/widget-session/nin-data/:sessionId
bvn-datareturns the profile for the BVN on the session.nin-datareturns the profile for the NIN or vNIN on the session.- Both work with sessions created from a live or sandbox key.
| Status | Message |
|---|---|
404 | Widget session not found |
404 | Bvn not found for widget session / Nin not found for widget session — user hasn't completed that step |
404 | Bvn data not found for session / Nin data not found for session — live only; no verified record |
Testing in Sandbox​
Use your sandbox secret key in Step 1 and an SDK set to the sandbox environment. The test numbers below return fixed mock profiles and never call live providers or debit your wallet.
| Service | Test value | Result |
|---|---|---|
| BVN | 12345678901 | Verified |
| BVN | 12345678902 | Verified (watchlisted) |
| BVN | 22233344455 | Verified |
| BVN | 22113344556 | Verified |
| NIN | 12345678901 | Verified |
In sandbox(development mode), any other BVN/NIN with no stored record returns a default mock profile instead of a 404.