AnybanQIDDeveloper Docs

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

bash
curl https://id.anybanq.lk/.well-known/openid-configuration

2 · Configure your OIDC client

ts
// 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'],
});
PKCE S256 requiredES256 tokensAccess 10m · Refresh 30dacr: loa1 / loa2 / loa3

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.

Authorization Code flow
Your AppAnybanQIDPhoneGET /oauth2/authorize (code_challenge)GET /oauth2/authorize (code_challenge)push login orderpush login orderapprove (biometric + signature)approve (biometric + signature)302 redirect ?code=302 redirect ?code=POST /oauth2/token (code_verifier)POST /oauth2/token (code_verifier)access + id + refresh tokenaccess + id + refresh token
ts
// 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.

Transaction approval
Your APIAuth SvcPhonePOST /auth/orders (TRANSACTION)POST /auth/orders (TRANSACTION)push {type, orderId}push {type, orderId}GET /orders/:id/status (poll)GET /orders/:id/status (poll)approve + sign displayDataapprove + sign displayDatastatus: APPROVEDstatus: APPROVED
bash
# 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/status

CIBA — 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.

CIBA poll mode
Your APIAnybanQIDPhonePOST /oauth2/bc-authorize (login_hint)POST /oauth2/bc-authorize (login_hint)auth_req_id, intervalauth_req_id, intervalpush approval orderpush approval orderPOST /oauth2/token (grant=ciba) pollPOST /oauth2/token (grant=ciba) pollauthorization_pending…authorization_pending…approve (biometric)approve (biometric)tokenstokens
bash
# 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 → tokens

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

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

Hosted verification
Merchant APIVerifiaCitizenPOST /sessions (workflowId) → linkPOST /sessions (workflowId) → linkshare link (SMS / WhatsApp / email)share link (SMS / WhatsApp / email)open link · fill fieldsopen link · fill fieldsPOST /sessions/:token/artifacts (selfie, ID)POST /sessions/:token/artifacts (selfie, ID)submit → steps run (NIC, FACE, AML…)submit → steps run (NIC, FACE, AML…)OTP prompt when a step needs itOTP prompt when a step needs itwebhook: run PASSED / REVIEW / FAILEDwebhook: run PASSED / REVIEW / FAILED

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.

Bill issue → payment → reconciliation
Biller ERPVerifiaCitizenBankPOST /bills (x-api-key) → ocr, payLinkPOST /bills (x-api-key) → ocr, payLinkdeliver pay-link (SMS / WhatsApp)deliver pay-link (SMS / WhatsApp)open /pay/<token>open /pay/<token>link account once (bank OTP at link time)link account once (bank OTP at link time)pay (saved account → AnyBanQ SMS OTP)pay (saved account → AnyBanQ SMS OTP)debit via JustPay tokendebit via JustPay tokenwebhook bill.paid {ocr}webhook bill.paid {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.

First-time link vs token reuse
Your AppVerifiaBankCitizenstart bank verify (bankCode, account)start bank verify (bankCode, account)register account (JustPay)register account (JustPay)SMS OTP to bank-registered numberSMS OTP to bank-registered numberenter OTP → account token storedenter OTP → account token storedlater: re-verify same customerlater: re-verify same customerAnyBanQ SMS OTP (token reused, no bank call)AnyBanQ SMS OTP (token reused, no bank call)verified (accountMask, token kept server-side)verified (accountMask, token kept server-side)

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):

  1. Upload the PDF (unprotected — remove any open/permission password first).
  2. Add signers in order; each gets a name, an invite channel (email/SMS) and a method (AnybanQID or draw/type). Optionally enter their NIC.
  3. Place fields — drag Signature, Name, NIC, Date or Text onto the page and assign each to a signer.
  4. 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):

Signing with AnybanQID
Signer linkVerifiaAnybanQID appopen /sign/<token>, fill required fieldsopen /sign/<token>, fill required fieldstap “Sign with AnybanQID”tap “Sign with AnybanQID”push SIGNING order (doc name + hash)push SIGNING order (doc name + hash)approve with Face ID → device key signs the hashapprove with Face ID → device key signs the hashnext signer, or certified PDF + verify codenext signer, or certified PDF + verify code

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.

Coming soon on theApp StoreComing soon onGoogle Play

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-001Challenge expiredOrder/challenge TTL elapsed. Create a new order.
AnybanQID-AUTH-002Signature invalidDevice signature failed verification.
AnybanQID-AUTH-003Replay detectedChallenge already consumed.
authorization_pendingCIBA pendingUser has not approved yet — keep polling.
invalid_grantInvalid grantCode/refresh token expired, reused, or revoked.
slow_downPoll too fastIncrease the CIBA poll interval.