DataCheck platform: how it works
A short guide to what the system does today, who can do what, and how money moves. Written for the owner and the team, not for developers only.
What it is
DataCheck sells identity and business checks to banks and Saccos. Organisations sign in to a dashboard, run checks, and pay for each one from a prepaid credit wallet. Resellers buy credit from DataCheck and sell checks to their own clients at their own price.
Today every check returns a sample result from the sandbox (fixed test answers). Results are marked "Sample result" on screen and stored with sample = true. No agency is called yet.
The checks
| Check | Agency | Asks for | Sample base price |
|---|---|---|---|
| KYC ID check by serial number | Citizen KYC | ID number, ID serial number | KES 20 |
| KYC ID check against phone number | Citizen KYC | ID number, phone number | KES 20 |
| BRS business check | BRS | Registration number | KES 50 |
| NTSA vehicle check | NTSA | Number plate | KES 50 |
| KRA PIN check | KRA | KRA PIN | KES 30 |
The base prices are sample prices. The super admin changes them on the Pricing page.
Lawful purpose and consent: one declaration per organisation. Checks do not ask for a purpose or consent each time. Instead an owner or admin accepts a declaration once in Settings (what the organisation runs checks for, that it has consent or another lawful basis, and that it keeps results safe). It is stored with the time, the person and the version of the text, and checks (dashboard, bulk and API) are blocked until it is accepted. Older checks keep the purpose they were run with.
ID numbers, serial numbers, phone numbers and personal names are masked in history; the full sample answer is shown once, to the person who ran the check. While a check runs, a progress bar moves from 0 to 100% through four steps (verifying access, contacting agency, matching record, preparing result), for 1.5 to 3 seconds, and finishes when the answer arrives.
Modules
| Module | Screen | What it does |
|---|---|---|
| Overview | /dashboard |
Balance and how many checks it covers, latest checks, top-ups waiting for you, low-balance clients, recent activity. DataCheck and resellers also get analytics: checks over time, revenue and margin, top requests, outcomes, top clients and resellers, busiest days and hours, filtered by period, organisation and check |
| Products | /dashboard/products/... |
A page per check: what it checks, what you enter and get back, your price, recent checks of that kind, and the form to run it |
| Bulk checks | /dashboard/bulk |
Paste or upload a list (input template provided), see the cost first, run it in the background, view results in the protected viewer |
| Checks | /dashboard/check, /dashboard/history |
Run a check (pick it from the menu); history with filters, masked details and CSV export; super admin can refund a check |
| Credit management | /dashboard/credit |
Wallet balance, statement (every ledger entry), CSV, monthly statement CSV with opening and closing balance; super admin adds or removes credit with a reason |
| Transfers | /dashboard/transfers |
Super admin sends credit to any reseller or client; a reseller sends credit to its own clients |
| Top-ups | /dashboard/topups |
Request a top-up ("I paid KES X, reference Y"); approve or reject queue; CSV receipt for each approved top-up |
| Clients | /dashboard/clients |
Super admin: every organisation, add clients and resellers, suspend, restore, make a client a reseller, custom prices. Reseller: its own clients |
| Pricing | /dashboard/pricing |
Super admin sets base prices; reseller sets selling prices; clients see their own prices |
| Pull reports | /dashboard/reports |
Checks, spend, reseller margin and DataCheck revenue by date range, organisation and check; CSV |
| User management | /dashboard/users |
Add users with a role, change roles, deactivate, reset passwords, unlock locked accounts, reset two-step sign-in; permission table |
| Settings | /dashboard/settings |
General: profile, password, appearance (System, Light or Dark theme), organisation details. Security: checklist, two-step sign-in, where you are signed in, and the organisation's security rules |
| Audit log | /dashboard/audit |
Every sensitive action, who did it and when; CSV |
| API keys | /dashboard/keys |
Keys for an organisation's own systems, with scopes; webhook address and signing secret |
| Security review | /dashboard/security |
DataCheck staff: two-step sign-in coverage per organisation, locked accounts, failed sign-ins in the last 7 days; change any organisation's rules |
| Notifications | Bell in the top bar | Top-up decisions, credit received, low credit, security events |
| Ask DataCheck | Search (Ctrl+K) and Ask (Ctrl+J) in the top bar | Search pages, clients, checks, top-ups and this guide; answer how-to questions; prepare actions for you to confirm |
Organisations
- DataCheck (platform): the super admin and DataCheck staff. Sees and manages everything.
- Reseller: buys credit from DataCheck, owns clients, sets its own selling prices.
- Client: a bank, Sacco or other business that runs checks. A client belongs to DataCheck directly or to one reseller.
The super admin can make a direct client a reseller. It keeps its wallet, users and history, and can then add clients and set prices. (A client that belongs to a reseller cannot be converted, so a reseller never loses a client without a decision about who owns it.)
Suspending an organisation signs its users out and blocks sign-in, checks and incoming credit until it is restored. Clients of a suspended reseller cannot run checks either.
Roles and permissions
Each person belongs to one organisation and has one role. What they can do is the role's permissions, limited by what their kind of organisation can do. The table lives in lib/permissions.ts and is enforced on the server for every API route; the dashboard hides what a person cannot do.
| Permission | Owner | Admin | Operator | Finance | Viewer |
|---|---|---|---|---|---|
| Run checks (clients, resellers) | yes | yes | yes | ||
| See check history | yes | yes | yes | yes | yes |
| Refund a check (DataCheck only) | yes | yes | |||
| See credit and statement | yes | yes | yes | yes | yes |
| Transfer credit (DataCheck, resellers) | yes | yes | yes | ||
| Add or remove credit by hand (DataCheck only) | yes | yes | |||
| Request a top-up (clients, resellers) | yes | yes | yes | ||
| Approve or reject top-ups (DataCheck, resellers) | yes | yes | yes | ||
| See clients (DataCheck, resellers) | yes | yes | yes | yes | |
| Add, suspend, convert clients (DataCheck, resellers) | yes | yes | |||
| See prices | yes | yes | yes | yes | yes |
| Set prices (DataCheck, resellers) | yes | yes | |||
| See users | yes | yes | yes | ||
| Manage users | yes | yes | |||
| Pull reports | yes | yes | yes | yes | |
| Read the audit log | yes | yes | yes | ||
| Edit organisation details and security rules | yes | yes | |||
| Manage API keys and webhooks (clients, resellers) | yes | yes |
Owner and Admin differ in one way: only an owner can add, change or remove another owner. Every organisation keeps at least one active owner. Nobody can change their own role or deactivate themselves.
Clients can never transfer credit. Only DataCheck (to anyone) and resellers (to their own clients) can.
What each organisation sees:
- DataCheck staff: every organisation.
- Reseller users: their own organisation and their own clients.
- Client users: their own organisation only.
Money
All amounts are Kenya shillings, stored in cents.
Wallets and the ledger. Every organisation has one wallet. Its balance only changes by writing a ledger entry, inside a database transaction that locks the wallet row. The ledger is append-only: the database refuses to edit or delete an entry, refuses a balance below zero, and refuses a balance change that does not match the latest entry. Entry types: topup, adjustment, transfer_in, transfer_out, check_charge, margin_credit, refund.
Where credit comes from. DataCheck issues credit. When a payment reaches DataCheck, the super admin approves the top-up request (or sends a transfer, or adds an adjustment with a reason). The DataCheck wallet itself is not debited: DataCheck is the issuer, and its revenue is reported from the checks, not held in a wallet.
Reseller credit. A reseller buys credit from DataCheck at face value (KES 10,000 paid gives KES 10,000 of credit). It passes credit to its clients by a transfer, or by approving a client's top-up request, which moves the amount from the reseller's wallet to the client's.
What a check costs.
| Who runs it | Charged | Reseller gets | DataCheck keeps |
|---|---|---|---|
| Direct client | Base price (or a custom price DataCheck set for that client) | All of it | |
| A reseller's client | The reseller's price for that client, else its default selling price, else the base price | Selling price minus base, as margin_credit |
The base price |
| A reseller, for itself | Base price (or a custom price DataCheck set for it) | All of it |
Example with the sample prices: Pwani Data Partners sells the KYC serial check at KES 25 (KES 23 for Mombasa Teachers Sacco). When the Sacco runs one, the Sacco's wallet goes down KES 23 and Pwani's goes up KES 3. CI checks this on every push.
A reseller cannot set a selling price below the base price. If DataCheck later raises a base price above a reseller's price, the base price is charged and the margin is zero until the reseller updates its price.
Why this model holds. The reseller paid face value for the credit it gave its client, so on each check the client's KES 23 of credit is used up: KES 20 is DataCheck's service and KES 3 returns to the reseller as margin. The reseller's margin arrives as credit in its wallet. It can use it for its own checks or pass it to clients. Paying margin out in cash is not built; if that is wanted later, it is a payout entry plus a bank or M-Pesa transfer.
Refunds. DataCheck staff can refund a check with a reason: the client gets the price back and, for a reseller's client, the reseller's margin is taken back.
Top-ups. A client or reseller requests a top-up after paying ("KES 10,000, reference QK12AB34CD"). The request goes to whoever sells them credit: DataCheck for resellers and direct clients, the reseller for its own clients. The approver checks the payment and approves (credit lands) or rejects with a reason. A reference can only be used once per organisation. Requests are stored with method = 'manual'; M-Pesa confirmation can be added later as method = 'mpesa' that approves itself when Safaricom confirms the payment.
Sign-in
- Email or phone number, plus password.
- A 6-digit code, every time. It is sent by SMS (Africa's Talking) when you sign in with your phone, by email (SMTP) when you sign in with your email, and falls back to the other if one is not set up. The code lasts 5 minutes, allows 5 tries, and can be re-sent 3 times. Only a hash of the code is stored.
People added by an owner get a temporary password, shown once to the person who added them, and choose their own at first sign-in.
Test mode (TEST_MODE=1, for the test domain): for test accounts only, the code is also shown on the sign-in screen in a marked box. Never set TEST_MODE on the live domain.
- Two-step sign-in (optional, or required by the organisation). People who turn it on in Settings, Security also enter the 6-digit code from an authenticator app (Google Authenticator, Microsoft Authenticator, Authy). Turning it on shows 10 one-time recovery codes, once. A recovery code works in place of the app code, one time each. The app secret is stored encrypted; recovery codes only as hashes. An owner or admin can require it for the whole organisation: people without it can then only open Settings, Security until they set it up. If someone loses their phone and their codes, an owner (or DataCheck) can reset it from Users and roles.
Passwords. At least 10 characters, not on the built-in list of common passwords (checked with digits and symbols at the ends removed, so "Password2026!" fails), no runs like 123456 or qwerty, and not containing the person's name, email or organisation name. No outside service is called.
Lockout. After 5 wrong passwords the account locks for 1 minute, then 2, 4, 8 and so on, up to 60 minutes per further wrong try. The right password does not work while it is locked. An owner or DataCheck can unlock it from Users and roles or the Security review page. Locks and wrong passwords go to the audit log.
Sessions. Settings, Security lists every place you are signed in (device and browser, IP address, when it started, last activity). Sign out one, or all others. Each organisation sets an idle timeout (5 minutes to 12 hours, default 1 hour); a session idle for longer ends.
Sign-in alerts. The first time someone signs in from a device DataCheck has not seen for them, it is recorded in the audit log, shown in their notifications, and sent by email or SMS when those are set up. Their very first device is only recorded.
IP allowlist (optional). An owner or admin can list the office IP addresses or ranges (like 197.248.10.0/24) the organisation may use. Sign-in, every dashboard request and every API key call from anywhere else is refused and logged. You cannot save a list that leaves out your own current address. DataCheck staff can change or clear any organisation's list from the Security review page (the override). Behind nginx, set TRUST_PROXY=1 so the real client address is used; without it the X-Forwarded-For header is ignored, so it cannot be forged.
Other protections: scrypt password hashing; a signed, httpOnly, SameSite=Lax session cookie (Secure in production) that lasts 12 hours and is ended at once by signing out, a password change, a reset or deactivation; rate limits on sign-in, codes, the assistant and API keys; changes only accepted as same-site JSON requests; parameterised SQL everywhere; server-side checks on every input.
Ask DataCheck (assistant)
Two tools in the top bar of the dashboard.
Search (Ctrl+K). A command palette. It finds pages you can open, your clients, recent checks, top-ups and sections of this guide. It works without any AI model.
Ask (Ctrl+J). A panel that:
- answers how-to questions from this guide;
- answers questions about your own figures (balance, checks and spend in the last 7 days, top-ups waiting);
- opens pages: "take me to transfers";
- prepares actions: "send 5,000 to Coast Microfinance", "approve top-up QKTEST0001", "invite Jane Achieng as operator", "set kyc serial price for Mombasa Teachers Sacco to 24".
How it stays safe:
- Nothing runs by itself. A prepared action is a card with the details and a Confirm button. Pressing Confirm calls the same API route as the normal page, so every permission and money check applies again. The audit log marks it "confirmed in Ask DataCheck".
- Your permissions only. The assistant can only look up and prepare what your role allows. A client cannot even prepare a transfer.
- No personal data to the model. Before any text is sent, email addresses, phone numbers, ID and serial numbers, KRA PINs and long digit runs are replaced with placeholders. Lookups return only organisation names, amounts and counts. Contact details for an invite are typed on the card, never sent to the model.
- Plain commands need no model. Commands like the ones above are understood by the server directly. Questions go to the language model only when one is configured.
The model is any OpenAI-compatible chat API; Groq is the default. Without a key the panel shows "Assistant not configured", and Search, commands and guide answers still work. Each question is logged in the audit log (not its text).
API and webhooks
Owners and admins of clients and resellers make API keys on the API keys page. A key is shown once; DataCheck keeps only a fingerprint. Each key has scopes: run checks, read check history, read balance. The organisation's IP allowlist applies to keys.
curl https://<site>/api/v1/balance -H "Authorization: Bearer bbx_live_..."
curl https://<site>/api/v1/checks -H "Authorization: Bearer bbx_live_..." \
-H "Content-Type: application/json" \
-d '{"product":"kra_pin","inputs":{"pin":"A012345678Z"}}'
Checks over the API are priced and charged exactly like the dashboard. Up to 120 calls a minute per key.
Webhooks. Set an https address on the API keys page. DataCheck POSTs check.completed and topup.decided events as JSON, signed with the organisation's secret: X-DataCheck-Signature: sha256=<HMAC-SHA256 of the body>. One try, 5-second timeout. Local and private addresses are refused.
Bulk checks
Paste rows or upload a CSV for one check (the input template has the column names; a header row is optional). The page checks every row before anything runs, lists the rows with problems, and shows the cost. Up to 500 rows per job.
The job runs in the background, one row at a time. Each row is charged through the same rules as a single check, in its own transaction: the client pays its price per row and a reseller gets its margin per row. If credit runs out, or the organisation is suspended, the job stops and the rows not run are not charged. Today the queue runs inside the web server; a job cut short by a restart carries on the next time anyone opens the bulk page. The architecture guide covers moving it to separate workers.
Protected results viewer. Results are for the person who ran the job only, for 24 hours. There is no download or export. Copy, select, right-click and print are turned off, printing gives a blank page, and a watermark with the viewer's name, email, organisation, time and a view id is tiled over the results. Results blur when the window loses focus, the tab is hidden, or developer tools are opened beside the page. Results are masked; revealing one row's full result is a deliberate action. Every page view and every reveal is in the audit log.
No website can stop a screenshot or a phone camera. These controls make copying hard and make any leak traceable to a person and a moment.
Notifications and alerts
The bell in the top bar lists top-up decisions, credit received, low credit and security events (new device, lockout, two-step sign-in turned off, recovery code used). In Settings, Security an owner or admin sets a low-credit level; when a payment takes the wallet below it, owners, admins and finance staff get a notification and owners an email or SMS.