Skip to content
Lira APILira API

Changelog

All notable changes to the Lira API are documented here. Breaking changes are marked explicitly. Dates follow ISO 8601 (YYYY-MM-DD).

2026-09-16

Changed — breaking

  • Ghana Card facial verification now returns a single error code for every failed check. Previously a failed facial verification could return one of several codes — FACIAL_LIVENESS_FAILED, FACE_NOT_DETECTED, FACE_QUALITY_LOW, MULTIPLE_FACES_DETECTED, FACIAL_VERIFICATION_FAILED, or FACE_MATCH_FAILED — even though the remedy was the same in every case: submit a new selfie. All of these now return FACE_MATCH_FAILED, with the specific reason carried in error.message:

    error.messageReason
    Facial verification did not matchThe face did not match the cardholder record
    Liveness check failed, please retake the photoThe image was not recognized as a live photo
    No face detected, please retake the photoNo face found in the image
    Photo quality too low, please retakeFace too small, too close, covered, or low-resolution
    Multiple faces detected, submit a single faceMore than one face in the image
    Facial verification failedThe check could not be completed

    If your client branches on the retired codes, update it to branch on FACE_MATCH_FAILED. The retired codes are no longer returned. IDENTIFIER_NOT_FOUND (Ghana Card number not found) is unchanged — it indicates a card-number problem, not a selfie problem. error.message is display text, not a contract: show it to your user or map it to your own copy, but branch only on error.code.

    The sandbox fixtures reflect the new behaviour — GHA-000000003-3 and GHA-000000004-4 now return FACE_MATCH_FAILED with their scenario-specific messages. See test numbers.

2026-09-08

Changed

  • Ethiopian bank account verification now tells you when an account does not exist. Previously, an Ethiopian account that the bank could not resolve came back as INCONCLUSIVE — the same response you get when verification is genuinely unavailable. There was no way to tell "this account number is wrong" from "try again in a few minutes", so the only sensible client behaviour was to retry a lookup that would never succeed.

    Those outcomes are now separated:

    ResponseWhat happenedCharged
    failed + ACCOUNT_NOT_FOUNDThe bank answered: no such accountYes
    failed + ACCOUNT_INACTIVEThe bank answered: the account exists but is closed, dormant or blockedYes
    INCONCLUSIVEThe bank did not answer — a timeout or outageNo — reversed automatically

    If you branch on error.code for Ethiopia, handle ACCOUNT_NOT_FOUND and ACCOUNT_INACTIVE separately. They are not the same outcome: ACCOUNT_NOT_FOUND means the number is wrong, so ask your user to re-check it; ACCOUNT_INACTIVE means the number is right but the account is unusable, so they need to supply a different one. Both are definitive — retrying will not change either. INCONCLUSIVE remains the retryable case, and remains uncharged.

    The ACCOUNT_INACTIVE message text also changed outside Ethiopia. Any corridor that can return ACCOUNT_INACTIVE now sends "Account found but is inactive, dormant or blocked" in place of "Account is closed, dormant or blocked". Only the wording changed — the error.code is unchanged, and results recorded before this date keep their original text, so the same code may show either message depending on when the verification ran. Branch on error.code, never on error.message.

    Verifications already recorded under the old behaviour are relabelled to match, so historical Ethiopian figures in the dashboard no longer count these as provider outages. Nothing is charged retroactively — calls that were reversed at the time stay reversed.

    Ethiopian accounts that were previously reported as INVALID_ACCOUNT now report ACCOUNT_NOT_FOUND, matching every other country and the sandbox. Test both new outcomes in sandbox with 1000000000002 (not found) and 1000000000004 (inactive) — see test numbers.

2026-08-06

Changed

  • Dashboard: your success rate no longer counts our provider outages against you. This affects the usage figures shown in the Lira dashboard. No API endpoint or response changed — your integration is unaffected.

    A verification has three endings, and we were only distinguishing two of them:

    OutcomeWhat happenedCharged
    SuccessThe provider responded and the record matchedYes
    FailedThe provider responded and found no matching recordYes
    ErrorThe provider did not respond — a timeout or outage on our sideNo — reversed automatically

    Your success rate previously divided successes by every attempt, so errors counted against it — even though you were never charged for them. Verifications still in flight had the same effect while a queue drained. A provider incident on our side therefore lowered a number you report internally, for calls you did not pay for.

    Success rate is now successes divided by the verifications a provider actually answered. Provider availability is reported separately as its own error rate, and a new failure rate shows how often a provider answered but found no record — a signal about the identifiers you submit, which is what that number was always meant to tell you.

    Expect your success rate to step up on this date. Nothing about your integration changed and no verification changed its outcome — only the denominator. The underlying counts are unchanged, so historical figures can still be reproduced from them.

    Per-country success rates changed the same way, and are now shown to two decimal places for consistency.

2026-07-30

Changed (breaking)

  • Verification error codes renamed. The error.code returned on a failed or errored verification no longer describes where the failure happened. Update any client branching on the old values:

    Old codeNew code
    PROVIDER_ERRORVERIFICATION_ERROR
    PROVIDER_UNAVAILABLEVERIFICATION_UNAVAILABLE
    PROVIDER_TIMEOUTVERIFICATION_TIMEOUT
    PROVIDER_RATE_LIMITEDVERIFICATION_RATE_LIMITED
    PROVIDER_CANCELLEDVERIFICATION_CANCELLED

    See Errors for the full reference.

  • providerName removed from verification responses. The field is gone from every verification response and webhook payload. There is no replacement: how a verification is fulfilled is an implementation detail and is not part of the API contract.

  • source now reports live instead of provider. A verification answered freshly rather than from cache is now "source": "live". The cache value is unchanged.

  • Verification error messages are now standardised for six codes. For VERIFICATION_ERROR, VERIFICATION_UNAVAILABLE, VERIFICATION_TIMEOUT, VERIFICATION_RATE_LIMITED, VERIFICATION_CANCELLED, and INCONCLUSIVE, error.message is resolved from error.code and is the same for every occurrence of that code. Every other code (for example ACCOUNT_NOT_FOUND or VERIFICATION_FAILED) still returns its persisted, result-specific message, which can vary between occurrences of the same code. Branch on error.code, never on message text.

Notes for this release

  • Historical records keep their original error_code. Verifications recorded before this release still report the code they were stored with (for example PROVIDER_TIMEOUT), so past results are never relabelled. Their error.message, however, is now the standard message for that outcome rather than the text captured at the time.
  • Idempotent replays may briefly return the previous shape. A replay of a request whose Idempotency-Key was first used before the deploy returns the stored response for the remainder of the key's TTL, which may still carry the old code, message, or providerName. Use a new key to get the current shape.

2026-07-14

Added

  • Bank routing endpoints. Documented four bank routing checks, now grouped under Bank Routing in the API reference:

    • POST /verifications/bic — validate a BIC/SWIFT code (ISO 9362) and break it into its component parts.
    • POST /verifications/routing-codes — validate a US ABA routing number (length + mod-10 checksum).
    • POST /verifications/correspondents — look up the correspondent banks on file for a foreign BIC, sorted by confidence (recorded and billed; send an Idempotency-Key).
    • POST /verifications/batch — validate up to 100 mixed BIC/routing/route items in one request.

    In sandbox, these endpoints return seeded fixture data for a documented set of test identifiers (see Test Numbers → Bank Routing); any other identifier returns TEST_NUMBER_NOT_FOUND. See Bank Routing.

2026-06-26

Added

  • United Arab Emirates (AE) bank-account verification. Added IBAN bank-account verification for the UAE. A name-match check: supply the IBAN as accountNumber, the country (AE), and the accountName to match. No bankCode is required — the IBAN carries the routing — and AED accounts only. Synchronous only (mode: "async" is not supported). See Bank Account: Country Requirements.
  • Australia (AU) bank-account verification. Added bank-account verification for Australia. A name-match check: supply the account number, the 6-digit BSB as bankCode, and the accountName to match. AUD accounts only. Synchronous only (mode: "async" is not supported). See Bank Account: Country Requirements.
  • United States (US) bank-account verification. Added bank-account verification for the United States. A name-match check: supply the account number, the 9-digit ABA routing number as bankCode, and the accountName to match. USD accounts only. Synchronous only (mode: "async" is not supported). See Bank Account: Country Requirements.

2026-06-25

Added

  • Europe (SEPA) bank-account verification. Added IBAN bank-account verification for 21 Eurozone countries: Austria (AT), Belgium (BE), Bulgaria (BG), Croatia (HR), Cyprus (CY), Estonia (EE), Finland (FI), France (FR), Germany (DE), Greece (GR), Ireland (IE), Italy (IT), Latvia (LV), Lithuania (LT), Luxembourg (LU), Malta (MT), Netherlands (NL), Portugal (PT), Slovakia (SK), Slovenia (SI), and Spain (ES). All are name-match checks: supply the IBAN as accountNumber, the country, and the accountName to match. No bankCode is required — the IBAN carries the routing — and EUR accounts only. Every SEPA corridor is synchronous only (mode: "async" is not supported). See Bank Account: Country Requirements.

2026-06-24

Added

  • LATAM bank-account corridors. Added bank-account verification for Brazil (BR), Mexico (MX), Argentina (AR), Uruguay (UY), Peru (PE), Chile (CL), Colombia (CO), and Ecuador (EC). All eight are name-match checks and synchronous (mode: "async" is not supported). Brazil, Mexico, Argentina, and Peru need no bankCode — the routing is embedded in the IBAN/CLABE/CBU/CCI; Uruguay, Chile, Colombia, and Ecuador use the bank's BIC or local clearing code. Brazil, Chile, Colombia, and Ecuador require a national/tax identifier in identifiers (CPF/CNPJ, RUT, CC/NIT, CC/RUC). See Bank Account: Country Requirements.

2026-06-22

Added

  • Asia bank-account corridors. Added bank-account verification for India (IN), Indonesia (ID), Vietnam (VN), Nepal (NP), Pakistan (PK), South Korea (KR), Bangladesh (BD), Malaysia (MY), Thailand (TH), and the Philippines (PH). India, Indonesia, Vietnam, Nepal, Pakistan, South Korea, Bangladesh, and Malaysia are name-match checks. Thailand is an account-existence check and the Philippines is a format/syntax check (neither performs a name match). All require accountName and all are synchronous (mode: "async" is not supported). India uses an 11-character IFSC in bankCode; the other nine use the bank's BIC. See Bank Account: Country Requirements.

2026-06-14

Added

  • Kenya (KE) identity verification: POST /verify/identity now supports Kenya. Select the document with the idType field:

    • national_id — Kenyan National ID number (up to 9 digits, e.g. 12345678). Returns split name fields plus date of birth.
    • drivers_license — driver's licence number (e.g. B1234567). The lookup is matched against the holder's name, so validation.firstName and validation.lastName are required.
    • passport — Kenyan passport number (6–12 alphanumeric characters, e.g. A1234567).

    Responses carry a verificationType of KE_NATIONAL_ID, KE_DRIVERS_LICENSE, or KE_PASSPORT. Passport and driver's licence records usually return a combined fullName rather than split name components, so read fullName and treat the individual parts as optional. See National ID, Driver's Licence & Passport.

  • Kenya (KE) phone number verification: POST /verify/phone now supports Kenya. Numbers are supplied in E.164 format (254700000000; the leading + is optional) and no networkCode is required — the operator is resolved automatically. A successful verification returns fullName and dateOfBirth; individual name components are not available. See Phone Number in Kenya.

2026-05-30

Added

  • United Kingdom (GB) and China (CN) bank account verification. Both are name-match corridors: you supply the account details plus the accountName you expect, and the bank confirms whether the name matches the account on record. See Bank Account in the United Kingdom and Bank Account in China.
    • United Kingdom: 8-digit account number with a 6-digit sort code supplied in bankCode. accountName is required.
    • China: select the account type with accountHolderType. INDIVIDUAL accounts use the UnionPay card number; BUSINESS accounts use the bank's BIC in bankCode plus an 18-character Uniform Credit Code supplied as a BUSINESS_REGISTRATION entry in identifiers. accountName is required.
    • Both corridors are synchronous only. mode: "async" is not supported.

Changed

  • Unified routing code field. The receiving institution's routing code is now always supplied in the single bankCode field, whatever its local form: the African bank/clearing code, the UK 6-digit sort code, or the bank's BIC/SWIFT code for China business accounts. There is no separate sortCode or bic field.
  • Typed account-holder identifiers. Corridor-specific identity codes are now supplied through an identifiers array of { "type": ..., "value": ... } entries. China business accounts carry their Uniform Credit Code as a BUSINESS_REGISTRATION identifier.
  • Name-match response shape. For GB and CN, a successful verification (verified: true) echoes back the accountName you submitted, confirmed to match the bank's record. These corridors do not disclose a separately sourced account name. When verified is false, accountName is omitted.

2026-05-28

Address verification SDK 1.0.2

All-platform release of the Uselira address-verification SDK. iOS, Android, and React Native all bump to 1.0.2; native iOS and Android also bump from 1.0.1 since their SdkConfig signatures change.

Fixed

  • React Native iOS bridge build failure. @uselira/core@1.0.1 shipped with an incorrect import Uselira in the iOS bridge. The published pod exposes the module as UseliraCore (the Swift class is Uselira, living inside that module). Every iOS build of 1.0.1 failed with Unable to find module dependency: 'Uselira'. Fixed in 1.0.2 by changing the import to UseliraCore.

Breaking changes (all three platforms)

  • SdkConfig.environment is now REQUIRED. No default. Host apps must explicitly pick sandbox or production so a sandbox build cannot accidentally ship to production.
    • Swift: SdkConfig(... duration: 14, environment: .sandbox, ...)
    • Kotlin: SdkConfig(... duration = 14, environment = LiraEnvironment.SANDBOX, ...)
    • React Native: { ... duration: 14, environment: 'sandbox', ... }
  • Recovery paths are stricter too: iOS Uselira.restoreIfNeeded() and Android Uselira.start(context) refuse to restore from a corrupt or missing stored environment instead of silently falling back to sandbox. iOS Uselira.cancelVerification(...) returns .invalidConfig(reason:) when no environment is stored.

Documentation

  • The React Native SDK README iOS Setup section now puts the two required Podfile edits up front:
    • platform :ios, '16.0': UseliraCore requires iOS 16; the RN scaffold default min_ios_version_supported resolves to ~13.4 and fails pod install with a platform-mismatch error.
    • use_frameworks! :linkage => :dynamic: UseliraCore ships as a binary .xcframework that is not consumable under static linking; without dynamic linking the Swift import fails with No such module 'UseliraCore'.
  • This page (Address verification) was updated for the new requirements: the Configuration reference table marks environment required, the React Native example passes environment, and the iOS install section spells out the two Podfile edits with the specific failure each prevents.
  • New section: Best practice: don't start a second verification while one is in flight, covering host-side gating with the persisted jobId + Uselira.status API. Includes React Native, iOS Swift, and Android Kotlin sketches.
  • Error-handling section updated: LiraSetupError.server now carries a parsed detail (code, message, actionType, actionHint). The SDK's in-flow retry banner shows actionHint when present; hosts should branch on actionType (FIX_INPUT / CONTACT_ADMIN won't be resolved by a retry).
  • React Native: documented the two new event subscriptions: addSetupFailedListener (with the SetupFailure discriminated union) and addUploadAttemptedListener ({ success, count, error? } per batch).
  • Behaviour-guarantees section: updated the idempotency-key description to reflect that the key is now derived from (customerRef, lat, lng, consent-minute UTC) rather than (customerRef, lat, lng, UTC day). Same-minute double-taps still collapse; cross-flow retries get a fresh key and reach the validation layer instead of replaying a cached failure.

Migration

Existing call sites won't compile until environment is added to every SdkConfig(...). The compiler will flag every missed call on iOS (SdkConfig requires 'environment:') and Android (No value passed for parameter 'environment'); React Native rejects at runtime with INVALID_CONFIG.

iOS host apps additionally need the two Podfile edits described above.


2026-05-07

Added

  • Actionable error responses: All API error responses now include an optional action field with a machine-readable type and a human-readable hint. When present, action.type tells your application what the caller should do to resolve the error, for example CONTACT_ADMIN when a service is not enabled, TOP_UP_WALLET when the wallet balance is insufficient, or WAIT_AND_RETRY when a rate limit is active. See Errors → Actionable errors for the full type list and a handling example.

Changed

  • 402 Payment Required is now returned when a verification cannot be billed: either because the organization's wallet has insufficient funds (INSUFFICIENT_FUNDS), the account has been auto-suspended (WALLET_SUSPENDED), or the service has no price configured (SERVICE_PRICE_NOT_CONFIGURED). Previously these conditions returned a 500 or a generic 422. All 402 responses include an action field.
  • 403 Forbidden now covers service-not-enabled errors in addition to permission errors. If a verification service is not enabled for your organization, you receive a 403 with code: SERVICE_NOT_ENABLED and action.type: CONTACT_ADMIN.

2026-04-24

Added

  • Francophone Africa coverage: Bank account and phone number verification now supported for Côte d'Ivoire (CI), Senegal (SN), Cameroon (CM), Burkina Faso (BF), Mali (ML), Benin (BJ), and Togo (TG). Tanzania (TZ) is also added. All phone verifications for the new markets require a networkCode. Contact support for the carrier code list per country.

2026-03-09

Added

  • Ghana phone number verification: POST /verify/phone now supports Ghana (GH) in addition to Nigeria. Ghana verifications require a networkCode (MTN, Vodafone, or AirtelTigo). Omitting networkCode for a GH request returns NETWORK_CODE_REQUIRED.
  • Webhook delivery history: New endpoints to inspect webhook delivery attempts:
    • GET /client/webhooks/:id/deliveries: list recent deliveries with status and response details
    • GET /client/webhooks/:id/deliveries/:deliveryId: get a specific delivery record
  • Async verification mode: All verification endpoints now accept "mode": "async". The API returns status: pending immediately and delivers the result via webhook when processing completes.
  • lastUsedAt on API keys: GET /client/api-keys responses now include a lastUsedAt timestamp showing when each key was most recently used.
  • expiresAt on API keys: API key list responses include an expiresAt field. Currently null for all keys; expiry configuration will be supported in a future release.

Changed

  • Webhook retry schedule updated: Retry delays changed from fixed 5-minute intervals to exponential backoff: immediate → 1 min → 3 min → 9 min → 27 min. Total retry window is approximately 40 minutes (previously 25 minutes). No action required for existing webhooks.

  • X-Signature header format: Webhook signature headers now use the sha256=<hex> prefix format. Previously the raw hex digest was sent without a prefix. Update your signature verification logic to strip the sha256= prefix before comparing.

    Migration: If you were comparing the header directly against a computed hex digest, update to:

    JavaScript
    const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');

Fixed

  • Resolved an issue where GET /verify with status=pending returned completed verifications in some edge cases.
  • PATCH /client/webhooks/:id no longer requires all fields to be present; partial updates work as documented.

2026-02-15

Added

  • Webhook management API: New endpoints for registering and managing webhook endpoints:
    • POST /client/webhooks: register an endpoint
    • GET /client/webhooks: list all webhooks, with optional status and event filters
    • GET /client/webhooks/:id: get a single webhook
    • PATCH /client/webhooks/:id: update URL, events, status, or signing secret
    • DELETE /client/webhooks/:id: delete a webhook
  • Verification list endpoint: GET /verify returns a paginated list of past verifications. Supports type, status, limit, and offset query parameters.
  • Verification retrieve endpoint: GET /verify/:id retrieves the result of a specific verification by ID. Useful as a polling fallback when a webhook delivery is missed.

Changed

  • API key header renamed: The authentication header for verification endpoints changed from X-Lira-Key to X-API-Key. The old header will continue to work until 2026-05-15; update before that date to avoid disruption. (Breaking change, grace period active)

2026-01-20

Added

  • Phone number verification: POST /verify/phone verifies that a mobile number is registered with a carrier and returns the subscriber's name and date of birth. Supported country: Nigeria (NG).
  • DEVELOPER role: New organization role with access to verification endpoints and webhook management. Cannot create or revoke API keys.

Changed

  • Verification response shape: Responses from POST /verify/account now include a top-level verified boolean field (in addition to status) for quick checks. verified: true is equivalent to status: "success".

2026-01-05: Initial release

Added

  • Bank account verification: POST /verify/account verifies a Nigerian bank account against official records and returns the account holder's name and address.
  • Authentication: POST /auth/login, POST /auth/refresh, POST /auth/logout for JWT-based session management.
  • API key management: POST /client/api-keys, GET /client/api-keys, DELETE /client/api-keys/:id for creating and revoking API keys scoped to sandbox or live environments.
  • Sandbox environment: All endpoints are available in sandbox with deterministic test data. No real external queries are made and no charges are incurred.
  • Supported countries: Nigeria (NG) for bank account verification.