A sandbox where you pick the outcome.
Send an identifier ending 2222 and the upstream hangs, then fails — every time, on every machine. Not found, malformed and declined work the same way, so the paths that break in production are the paths you can put in a test suite. A test key cannot move money or message a customer through bills, payment links, subscriptions, payouts or remittance, and its one-time codes are returned to you instead of being sent. Other verification checks are not yet isolated by key: read the note under the table before you send one.
Authentication
One key, two environments
Every request carries your API key as a bearer token. The older x-api-key header is still accepted and will not be removed — existing integrations do not need to change.
divp_test_…
Test key. Works before your account is approved. Bills, payment links, subscription plans, payout batches and remittance orders made with it are test records: nobody is messaged, nothing is charged, and they are kept apart from live ones. A one-time code is returned in the response, not sent. Other verification checks are not yet isolated by key.
divp_live_…
Production. Real registries, real banks, real money, metered against your plan.
curl -X POST https://verifia.anybanq.lk/v1/verifications/nic \
-H "Authorization: Bearer divp_test_…" \
-H "Content-Type: application/json" \
-d '{
"nicNumber": "199012340000",
"fullName": "Nimal Perera",
"consentGiven": true
}'Amounts are in minor units wherever a field says so, and every amount carries an explicit currency. Nothing is assumed to be LKR.
Sandbox
The last four digits choose the outcome
The scenario is keyed on the last four digits of an NIC or account number, not a magic prefix — a Sri Lankan NIC carries a birth year up front and an account number carries a branch, so a prefix scheme would force you to use structurally invalid identifiers. Punctuation and a trailing letter are ignored; only digits count. Anything that is not one of these codes succeeds, so your own realistic test data gives you a working flow.
| Send | You get | What it exercises |
|---|---|---|
| …0000 | Verified | The registry or bank returns a matching record. |
| …1111 | Not found | Well-formed input, nothing upstream to match it against. |
| …2222 | Timeout | The upstream hangs for a full 5 seconds, then fails. Test your own timeout. |
| …3333 | Validation error | The upstream rejects the request as malformed. |
| 999.99 | Declined | Any payment for this amount is refused by the issuing bank. |
Not applied to verification calls yet. A one-time code requested with a test key is never sent and never metered: it comes back in the response as devCode. Every other verification sent with a test key is answered by whichever provider is connected for that check, exactly as with a live key, and is metered. Until that changes, test those checks with your own details.
A test key works while your account is still onboarding or awaiting review, so you can build the integration before the paperwork is finished. It stops working only if the account is suspended. What it makes in bills, payment links, subscription plans and payout batches is a test record, marked test: true: sent to nobody, paid by simulation only, and never mixed with live records. Its remittance orders are always sandbox orders. The full list is in the pay guide.
Core banking
Your core pushes; we never reach in
No bank opens its core to a third party, and none should have to. Your integration layer posts accounts and the transactions on them, so you decide what leaves the bank — and an outage on our side can never stall a transaction on yours.
Send the customer's declared income and occupation. It is the field that turns a number into a finding: a LKR 2.4m deposit is a deposit, until it lands in the account of a teacher who told you they earn 120,000 a month. Account numbers and NICs are hashed on arrival; what stays readable is only what an analyst must read to act.
# 1 — register the account, with what the customer told you they earn
curl -X POST https://verifia.anybanq.lk/v1/core-banking/accounts \
-H "Authorization: Bearer $VERIFIA_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalRef": "CORE-ACC-88213",
"accountNumber": "0010223348219",
"bankCode": "7083",
"subjectKey": "199812345678",
"customerName": "Nimal Perera",
"customerNic": "199812345678",
"occupation": "Teacher",
"declaredMonthlyIncome": 120000,
"signatories": [{ "nic": "197655443321", "role": "JOINT" }]
}'
# 2 — post the transactions on it, up to 1,000 a request
curl -X POST https://verifia.anybanq.lk/v1/core-banking/transactions \
-H "Authorization: Bearer $VERIFIA_KEY" \
-H "Content-Type: application/json" \
-d '{ "transactions": [{
"externalRef": "CORE-ACC-88213",
"occurredAt": "2026-09-24T09:14:00Z",
"amount": 2400000,
"direction": "IN",
"counterparty": "LANKA TRADERS PVT",
"channel": "RTGS",
"balanceAfter": 2584300
}] }'
# { "accepted": 1, "skipped": 0, "ruleAlerts": 1, "accountFindings": 2 }Rules you wrote
Every transaction runs through your compliance team's own rules, built in the console and backtested over history before they went live.
Judgement per account
Behaviour against the account's own history, income against the declared profile, dormant accounts waking, balances parked and drained. Anything serious opens a case with the arithmetic written out.
Backfill six to twelve months before going live, or every account looks new and the behaviour checks stay quiet until they have a normal to compare against. Narrow the key to the corebank scope and it can do this and nothing else.
Webhooks
Signed, retried, de-duplicable
Results are posted to your endpoint as JSON. A failed delivery is retried with exponential backoff — 10s, 30s, 2m, 10m, 30m, then 2h — for six attempts by default, after which it sits in the dead-letter list for you to replay from the dashboard.
POST https://your-app.lk/hooks/verifia
x-divp-event: verification.completed
x-divp-signature: sha256=<hex hmac of the raw body>
x-divp-delivery-id: 0f2c…
x-divp-attempt: 1
{
"event": "verification.completed",
"data": {
"verificationId": "…",
"type": "NIC",
"status": "APPROVED",
"outcome": "PASS",
"score": 100
},
"sentAt": "2026-09-16T04:21:08.114Z"
}const expected = crypto
.createHmac("sha256", process.env.VERIFIA_WEBHOOK_SECRET)
.update(rawBody) // the exact bytes we sent, not a re-serialised object
.digest("hex");
// Retries resend an identical body, so the signature never changes.
// Dedupe on x-divp-delivery-id.Getting keys
Sandbox first, live after approval
- 1
Register
Create the account at /register. We review it — you are told when sign-in opens.
- 2
Take the sandbox key
A divp_test_ key comes back on the registration screen itself, before any review, so you can call the sandbox the same minute. It is shown once; we store only an Argon2id hash of it and cannot recover it for you. Lost it, or signed up earlier? Settings → API keys mints another.
- 3
Finish onboarding
Business registration number, BR certificate, at least one director and a settlement account. A platform officer reviews every application and approves it, returns it for changes, or declines with a reason.
- 4
Request live access
Once it is granted, live keys unlock. Ask for one before then and the API says so plainly: this account is not approved for live keys yet.
Keys can be scoped and revoked individually, and every live key can be revoked at once if you ever need to. Each key records when it was last used.
A key restricted to scopes reaches only what they cover, on every route: bills for bills, payment links and subscription plans, remittance for remittance orders, verifications for checks, and so on. A key with no scopes is not narrowed. No key, scoped or not, can manage API keys, webhooks, your team or your organisation's settings: those are done by a person signed in to the console.
What we deliberately do not publish
There is no public OpenAPI document.
Stop looking for one — it is not hidden behind a URL you have yet to guess. A complete route inventory tells an attacker exactly which doors exist and what shape the keys are, so the spec is served only to signed-in merchants, and /docs and /openapi.json are refused at the edge on every host.
The same content is in the dashboard the moment you can sign in: the full reference, per workflow, with your own keys already filled in. Everything on this page is true of the sandbox before you have an account at all.
