Verifia
Developers

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.

Run an NIC check
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
  }'
Consent is not optional: attest that you captured it with consentGiven, or hand us consentChannel (SMS or EMAIL) and consentDestination and we will collect it from the data subject before the lookup runs.

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.

SendYou getWhat it exercises
…0000VerifiedThe registry or bank returns a matching record.
…1111Not foundWell-formed input, nothing upstream to match it against.
…2222TimeoutThe upstream hangs for a full 5 seconds, then fails. Test your own timeout.
…3333Validation errorThe upstream rejects the request as malformed.
999.99DeclinedAny 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.

Register an account, then feed it
# 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 }
Idempotent on your own externalRef, so a nightly customer file converges instead of duplicating. A transaction for an account we do not hold is skipped, not guessed at — without the profile there is nothing to judge it against.

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.

verification.completed
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"
}
Verify the signature
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.
The secret is shown once, when you create the webhook. We also send a webhook.test event on demand so you can prove the endpoint works before anything real depends on it.

Getting keys

Sandbox first, live after approval

  1. 1

    Register

    Create the account at /register. We review it — you are told when sign-in opens.

  2. 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. 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. 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.