API mocks

On this page

Lean Technologies

A mock compatible with the Lean open banking API (UAE and Saudi Arabia). Your app creates customers, connects their bank accounts, reads identity, accounts, balances and transactions, and takes payments from a bank account, as it does with Lean's sandbox, with Lean's response envelope and webhooks. The banks and their data are invented: no bank account is accessed and no money moves. Never enter real bank credentials on the mock's pages.

Testlie is not affiliated with, endorsed by or sponsored by Lean Technologies. The name is a trademark of its owner, used here only to say which API this mock is compatible with.

Connect

Create a Lean Technologies mock under API mocks → Banking. Use its base URL in place of https://sandbox.leantech.me and its credentials:

Credential Use
App id, client secret POST /oauth2/token with grant_type=client_credentials (form-encoded) returns { access_token, token_type: "bearer", expires_in: 3599, scope }; send Authorization: Bearer <token>
App token The legacy lean-app-token header, accepted instead of a bearer token
Payment destination id The mock's one payment destination

A missing or expired token answers 401 { "status": "UNAUTHORIZED" }.

The data API answers in Lean's envelope, { "status": "OK", "message", "timestamp", ... }, and every error uses it with an error status (CUSTOMER_NOT_FOUND, ENTITY_NOT_FOUND, INVALID_PARAMETERS, ...). The token, customer, entity, payment intent and payment destination endpoints return their objects without the envelope.

Customers

Endpoint
POST /customers/v1 { "app_user_id" } returns { customer_id, app_user_id }; an app user id used before answers 409 DUPLICATE_APP_USER_ID
GET /customers/v1/{customer_id} One customer
GET /customers/v1/app-user-id/{app_user_id} Look up by your own user id
GET /customers/v1/{customer_id}/entities The customer's bank connections
DELETE /customers/v1/{customer_id}/entities/{entity_id}?reason= Disconnect a bank; sends entity.deleted

In production the LinkSDK runs in the customer's browser. The mock replaces it with a hosted page: send the browser to

<base URL>/testlie/link?method=CONNECT&customer_id=<id>&permissions=identity,accounts,balance,transactions&redirect_url=<your URL>

The page lets the tester pick a bank and the result. It then redirects to redirect_url with what the LinkSDK callback would report:

Result Query parameters
Connected method=CONNECT&status=SUCCESS&entity_id=…&bank_identifier=LEANMB1 and an entity.created webhook
Bank login fails status=ERROR&secondary_status=BANK_LOGIN_FAILED
Cancelled status=CANCELLED

Banks: LEANMB1 (Lean Mock Bank) works normally; LEANMB2 connects but its data calls answer RECONNECT_REQUIRED, to test your reconnect flow; LEANMB3 works normally. /testlie/link is a Testlie extension, not a Lean endpoint.

Data API

POST /data/v1/identity, /data/v1/accounts, /data/v1/balance and /data/v1/transactions take { "entity_id", "account_id"? } and return { status: "OK", payload, results_id, message, timestamp, meta }. The data is invented but stable per entity: one AED current account with an IBAN, a balance and five transactions.

  • A permission the customer did not grant answers 403 PERMISSION_NOT_GRANTED.
  • An unknown account_id answers 404 ACCOUNT_NOT_FOUND.
  • With "async": true the call answers status: "PENDING" and a results.ready webhook follows; fetch the data from GET /data/v1/results/{results_id}.

Payments

Endpoint
POST /payments/v1/intents { amount, currency: "AED" | "SAR", customer_id, description?, payment_destination_id? } returns the intent with payment_intent_id
GET /payments/v1/intents/{id} The intent with its payments
GET /payments/v1/destinations The mock's payment destination

To pay, send the browser to <base URL>/testlie/link?method=PAY&payment_intent_id=<id>&redirect_url=<your URL>. Approving creates a payment in PENDING_WITH_BANK (and a payment.created webhook) and redirects with status=SUCCESS&payment_id=…&payment_intent_id=…. After the mock's Asynchronous steps delay, the next read of the intent shows it ACCEPTED_BY_BANK with a bank_transaction_reference and sends payment.updated. Bank rejects the payment, or the everything is rejected scenario, creates a FAILED payment with the scenario's code (REJECTED_BY_BANK, INSUFFICIENT_FUNDS) and redirects with status=ERROR.

Webhooks

Events: entity.created, entity.deleted, results.ready, payment.created, payment.updated, posted to the mock's webhook URL in Lean's envelope:

json
{ "type": "payment.updated", "payload": { "id": "…", "intent_id": "…", "status": "ACCEPTED_BY_BANK", "amount": 150.25, "currency": "AED" }, "message": "A payment's status changed.", "timestamp": "2026-10-18T12:00:00.000Z", "event_id": "…" }

The mock does not sign Lean webhooks. Treat them as notifications and read the object back from the API before acting on it.

Not supported

Lean's other products (account verification, payouts and disbursements, payment sources, the Saudi Arabia-specific flows, consent and reauthorization APIs) and mTLS are not mocked.