Documentation
Integrate AnybanQID authentication and Verifia verification, bills and document signing into your application.
Quickstart
AnybanQID is an OIDC provider. Your application redirects the user to AnybanQID, they approve the login on their phone with a biometric-gated device key, and you receive standard OAuth tokens. The issuer is https://id.anybanq.lk and all endpoints are discoverable at the well-known configuration URL.
1 · Discover the endpoints
curl https://id.anybanq.lk/.well-known/openid-configuration2 · Configure your OIDC client
// AnybanQID is standard OpenID Connect — use any certified OIDC client,
// e.g. openid-client:
import { Issuer } from 'openid-client';
const issuer = await Issuer.discover('https://id.anybanq.lk');
const client = new issuer.Client({
client_id: process.env.ANYBANQID_CLIENT_ID!,
client_secret: process.env.ANYBANQID_CLIENT_SECRET!,
redirect_uris: ['https://app.example.lk/auth/callback'],
response_types: ['code'],
});AnybanQID — use cases
AnybanQID is the national digital identity: a citizen proves who they are once (bank-account proofing), then approves logins, transactions and document signatures on their phone with a biometric-gated device key.
Citizen (app user)
Enroll
Register with NIC → prove a bank account (JustPay OTP) → bind this device.
Approve a login
Push arrives → review who is asking → approve with Face ID / fingerprint.
Sign a document
Review the document hash and sign with the device key (PAdES).
Pay bills
See every bill addressed to their NIC and pay via the Verifia pay-link.
Relying party (your app)
Login with AnybanQID
Standard OIDC redirect (Authorization Code + PKCE) — see the flow below.
Decoupled login
CIBA: authenticate a known NIC with no browser, e.g. call-centre or POS.
Transaction approval
Create a push order with signed display data (amount, merchant).
Request e-signature
Send a signing request; the citizen signs on-device.
Platform admin
Manage users
Suspend / reactivate / permanently delete accounts (audited).
Review verifications
Approve or reject identity evidence; watch the audit trail.
Manage RP clients
Register OAuth clients, rotate secrets, grant the CIBA grant.
Authorization Code + PKCE
The standard browser/redirect flow. All AnybanQID clients — public and confidential — must use PKCE with the S256 method.
// 1. Redirect the user to authorize
const { url, verifier } = AnybanQID.buildAuthorizationUrl({
scope: 'openid profile',
acrValues: 'loa2',
});
res.cookie('pkce', verifier, { httpOnly: true });
res.redirect(url);
// 2. Handle the callback
app.get('/auth/callback', async (req, res) => {
const tokens = await AnybanQID.exchangeCode({
code: req.query.code as string,
codeVerifier: req.cookies.pkce,
});
// tokens.accessToken, tokens.idToken, tokens.refreshToken
});Push authentication (order polling)
For flows you drive server-side (e.g. transaction approval), create an auth order directly and poll its status. The user gets a push notification and approves on their device. Display data (amount, merchant) is signed as part of the challenge.
# Create a transaction approval order
curl -X POST https://id.anybanq.lk/api/v1/auth/orders \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "TRANSACTION",
"userRef": { "nic": "199025401234" },
"displayData": {
"title": "Confirm payment",
"amount": 24500,
"currency": "LKR",
"merchant": "Keells Super"
},
"acrRequested": "loa2"
}'
# → { "orderId": "...", "expiresAt": "..." } Then poll GET /auth/orders/:id/statusCIBA — Client Initiated Backchannel Authentication
CIBA lets you authenticate a user without a browser redirect: initiate on the backchannel with a login_hint of nic:<nic>, then poll the token endpoint. AnybanQID advertises poll mode (backchannel_token_delivery_modes_supported: ["poll"]). Enable the CIBA grant on your confidential application first.
# 1. Initiate backchannel authentication
curl -X POST https://id.anybanq.lk/oauth2/bc-authorize \
-u "$ANYBANQID_CLIENT_ID:$ANYBANQID_CLIENT_SECRET" \
-d 'login_hint=nic:199025401234' \
-d 'scope=openid profile' \
-d 'binding_message=Approve login to Demo Bank' \
-d 'requested_acr=loa2'
# → { "auth_req_id": "...", "expires_in": 300, "interval": 5 }
# 2. Poll the token endpoint every {interval} seconds
curl -X POST https://id.anybanq.lk/oauth2/token \
-u "$ANYBANQID_CLIENT_ID:$ANYBANQID_CLIENT_SECRET" \
-d 'grant_type=urn:openid:params:grant-type:ciba' \
-d "auth_req_id=$AUTH_REQ_ID"
# pending → { "error": "authorization_pending" }; approved → tokensToken refresh & rotation
Refresh tokens rotate on every use with reuse detection — presenting a previously-rotated token revokes the entire family. Always persist the newest refresh token returned.
curl -X POST https://id.anybanq.lk/oauth2/token \
-u "$ANYBANQID_CLIENT_ID:$ANYBANQID_CLIENT_SECRET" \
-d grant_type=refresh_token \
-d refresh_token=$REFRESH_TOKEN
# → new access token + a NEW refresh token (store it, discard the old one)Verifia — use cases
Verifia (verifia.anybanq.lk) is the verification & payments platform for businesses: KYC workflows (NIC, document, face, bank, AML…), an OCR bill registry with pay-links, and merchant APIs — all reusing AnybanQID rails where a citizen already holds one.
Merchant / biller
Verify a customer
Compose a workflow (NIC + face + AML…), send a hosted link or embed it.
Issue bills
Register invoices by NIC; each gets an OCR reference and a pay-link.
Send & remind
Deliver by WhatsApp / SMS, track status, remind before due.
Reconcile
Match bill.paid webhooks to invoices by OCR — no manual matching.
Citizen (end customer)
Complete verification
Open the hosted link, capture selfie/ID, confirm OTP — no account needed.
Pay a bill
Open the pay-link, link a bank account once (bank OTP), pay in-window.
Pay from a saved account
Repeat payments confirmed with an AnyBanQ SMS OTP — no re-linking.
Platform staff
Operate tenants
Approve organizations, set pricing, issue bills on a tenant’s behalf.
Monitor & review
Fraud signals, transaction monitoring, manual-review queue for flagged checks.
Verifia — hosted verification workflow
The zero-code path: create a session for your workflow, hand the citizen the link, and receive the outcome by webhook. Camera captures (selfie / ID photo) are uploaded against the session token; each step (NIC, FACE, AML…) runs server-side with real providers.
A flagged face capture (occlusion, glare) or a borderline AML score never auto-passes — it lands in the manual-review queue and the final webhook fires after a reviewer decides.
Verifia — bills & pay-links workflow
Register invoices from your ERP by NIC; Verifia mints an OCR reference (Luhn-checked) and a pay-link, delivers it, and tells you by webhook the moment it is paid — you reconcile purely by OCR.
Verifia — bank account verification workflow
Proves the customer owns a bank account via HNB JustPay tokenization. The bank OTPs only at link time; once an account token exists it is reused — later confirmations use AnyBanQ's own SMS OTP instead of re-registering at the bank. Always tell the customer to have the mobile number registered with their bank.
The bank list you present must come from GET /v1/public/banks — it proxies JustPay's own merchant-bank-list, so institution codes never drift.
Document signing
Send a PDF for legally-recognised e-signature. Verifia is the envelope layer (upload, fields, routing) and AnybanQID is the signature layer — the citizen signs on their phone with a device-bound key, and the certificate that gives the signature legal weight is issued by LankaSign (LankaPay/LPPL, Sri Lanka's licensed CA). A signer with no AnybanQID can fall back to a drawn/typed signature.
Send a document (merchant, in the dashboard → Documents):
- Upload the PDF (unprotected — remove any open/permission password first).
- Add signers in order; each gets a name, an invite channel (email/SMS) and a method (AnybanQID or draw/type). Optionally enter their NIC.
- Place fields — drag Signature, Name, NIC, Date or Text onto the page and assign each to a signer.
- Send for signing — each signer receives their
/sign/<token>link; sequential routing invites the next signer only after the previous one finishes.
Sign (the citizen, from the link):
The required fields must be completed before the sign button enables. On the same device the page deep-links straight into the app's approval screen; on another phone it shows a QR to scan. When the last signer approves, Verifia appends a Certificate of Completion, stamps a public verification code, and emails the finished PDF to everyone.
Verify authenticity — anyone with the code opens /verify/document/<code>: it shows the signers, method and timestamps, and lets a third party drop their copy of the PDF to compare its SHA-256 in the browser (nothing is uploaded). Programmatic check: GET /v1/public/signing/verify/:code.
Get the apps & the CA
AnybanQID app — the citizen installs it once to sign with a device-bound key.
LankaSign (Certification Authority): LankaPay / LPPL — lankapay.net. Signing certificates are issued to the citizen's device during onboarding; the signature chains to LankaSign's CA.
Error handling
Service errors follow RFC 7807 (application/problem+json) with a code field; OAuth endpoints return standard OAuth error codes. Common codes:
| AnybanQID-AUTH-001 | Challenge expired | Order/challenge TTL elapsed. Create a new order. |
| AnybanQID-AUTH-002 | Signature invalid | Device signature failed verification. |
| AnybanQID-AUTH-003 | Replay detected | Challenge already consumed. |
| authorization_pending | CIBA pending | User has not approved yet — keep polling. |
| invalid_grant | Invalid grant | Code/refresh token expired, reused, or revoked. |
| slow_down | Poll too fast | Increase the CIBA poll interval. |