Web

The Web SDK is loaded via the Link Loader script, which exposes a global Lean object for every method.

📘

Prerequisites

Before calling any SDK method you need to have completed the backend setup in the Getting Started section:

This page covers only the client-side SDK integration.

Installation

Add the Link Loader script to your page:

<script src="https://cdn.leantech.me/link/loader/prod/ae/latest/lean-link-loader.min.js"></script>

Load the script at app initialisation / page load — not when the user initiates a flow. Loading it early means the loader and its iframe are warm by the time you call a method.

The loader exposes a global Lean object. Every method lives on it directly: Lean.connect(...), Lean.pay(...), etc.

Available methods

Every method takes a single config object. Each example below lists required fields explicitly and shows the most common optional ones. Every method also accepts these shared optional fields: access_token, sandbox, language, customization, callback.

.connect()

Link a bank account for data access (and optionally payments).

Lean.connect({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "CUSTOMER_ID",
  permissions: ["identity", "accounts", "transactions", "balance"],
  // Optional:
  bank_identifier: "<BANK_IDENTIFIER>",
  account_type: "PERSONAL", // or "BUSINESS"
  access_from: "2025-01-01",
  access_to: "2025-12-31",
  show_consent_explanation: true,
  access_token: "<CUSTOMER_SCOPED_JWT>",
  sandbox: true,
  callback: ({ status }) => console.log("Status:", status),
});

.pay()

Initiate a bank-to-bank payment.

Required: app_token + one of payment_intent_id or bulk_payment_intent_id.

Lean.pay({
  app_token: "<YOUR_APP_TOKEN>",
  payment_intent_id: "PAYMENT_INTENT_ID",
  // OR for bulk payments: bulk_payment_intent_id: "BULK_PAYMENT_INTENT_ID",
  // Optional:
  account_id: "<ACCOUNT_ID>",
  bank_identifier: "<BANK_IDENTIFIER>",
  risk_details: { /* fraud signals */ },
  access_token: "<CUSTOMER_SCOPED_JWT>",
  sandbox: true,
  callback: ({ status }) => console.log("Status:", status),
});

.reconnect()

Restore a broken bank connection.

Required: app_token, reconnect_id.

Lean.reconnect({
  app_token: "<YOUR_APP_TOKEN>",
  reconnect_id: "RECONNECT_ID",
  access_token: "<CUSTOMER_SCOPED_JWT>",
  sandbox: true,
  callback: ({ status }) => console.log("Status:", status),
});

.updatePaymentSource()

Add a beneficiary to an existing payment source.

Required: app_token, customer_id, payment_destination_id, and one of payment_source_id or entity_id.

Lean.updatePaymentSource({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "CUSTOMER_ID",
  payment_destination_id: "PAYMENT_DESTINATION_ID",
  payment_source_id: "PAYMENT_SOURCE_ID",
  // OR to upgrade a data-only customer: entity_id: "ENTITY_ID",
  access_token: "<CUSTOMER_SCOPED_JWT>",
  sandbox: true,
  callback: ({ status }) => console.log("Status:", status),
});

.checkout()

Open Finance guest payment — the customer authenticates at the bank and is redirected back to your success_redirect_url or fail_redirect_url with status query parameters that you feed into captureRedirect.

Required: app_token, payment_intent_id, success_redirect_url, fail_redirect_url.

Lean.checkout({
  app_token: "<YOUR_APP_TOKEN>",
  payment_intent_id: "PAYMENT_INTENT_ID",
  success_redirect_url: "https://yourapp.com/payment-success",
  fail_redirect_url: "https://yourapp.com/payment-failure",
  // Optional:
  customer_name: "Jane Doe",
  bank_identifier: "<BANK_IDENTIFIER>",
  risk_details: { /* fraud signals */ },
  callback: ({ status }) => console.log("Status:", status),
});

.authorizeConsent()

Grant a long-lived Open Finance consent. Redirects to the bank and returns to your success_redirect_url / fail_redirect_url.

Required: app_token, customer_id, consent_id, success_redirect_url, fail_redirect_url.

Lean.authorizeConsent({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "CUSTOMER_ID",
  consent_id: "CONSENT_ID",
  success_redirect_url: "https://yourapp.com/consent-success",
  fail_redirect_url: "https://yourapp.com/consent-failure",
  // Optional:
  risk_details: { /* fraud signals */ },
  access_token: "<CUSTOMER_SCOPED_JWT>",
  sandbox: true,
  callback: ({ status }) => console.log("Status:", status),
});

.manageConsents()

Show a customer-facing consent management screen where users can view and revoke their active consents.

Required: app_token, customer_id.

Lean.manageConsents({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "CUSTOMER_ID",
  access_token: "<CUSTOMER_SCOPED_JWT>",
  sandbox: true,
  callback: ({ status }) => console.log("Status:", status),
});

.captureRedirect()

Show the outcome screen after an Open Finance redirect. Extract the status query parameters Lean appends to your redirect URL (consent_attempt_id, granular_status_code, status_additional_info) and pass them in.

Required: app_token, customer_id.

const params = new URLSearchParams(window.location.search);

Lean.captureRedirect({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "<CUSTOMER_ID>",
  consent_attempt_id: params.get("consent_attempt_id") ?? undefined,
  granular_status_code: params.get("granular_status_code") ?? undefined,
  status_additional_info: params.get("status_additional_info") ?? undefined,
  access_token: "<CUSTOMER_SCOPED_JWT>",
  callback: ({ status }) => console.log("Status:", status),
});

.verifyAddress()

Proof-of-address check. The SDK automatically sets permissions: ["identity"].

Required: app_token, customer_id, customer_name.

Lean.verifyAddress({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "CUSTOMER_ID",
  customer_name: "Jane Doe",
  access_token: "<CUSTOMER_SCOPED_JWT>",
  sandbox: true,
  callback: ({ status }) => console.log("Status:", status),
});

Deprecated methods

.createBeneficiary()

⚠️

Deprecated — use updatePaymentSource.

Alias for updatePaymentSource. Accepts the same configuration and behaves identically.

.createPaymentSource()

⚠️

Deprecated — use connect with permissions: ["payments"] instead.

Alias for connect that forces permissions: ["payments"]. Any permissions you pass is ignored.

Callback

Every method accepts a callback that fires on SDK close with a CallbackData object. Status codes documented at LinkSDK statuses.

The CallbackData shape:

FieldTypeNotes
statusstringSUCCESS | ERROR | CANCELLED | REDIRECT | LINK_CLOSED_PROGRAMMATICALLY
messagestringHuman-readable description of the outcome.
bank{ bank_identifier, is_supported }Bank the user selected; is_supported is false when the user taps "My bank is not listed".
exit_pointstringLast screen the user saw.
secondary_statusstringAdditional detail on failures (e.g. INVALID_CREDENTIALS) — see LinkSDK statuses.
last_api_responsestringLast status returned by the Lean API during the flow.
lean_correlation_idstringUnique correlation ID — include in support requests.
open_banking_redirect_urlstringPopulated on REDIRECT status (Open Finance flows).
Lean.connect({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "CUSTOMER_ID",
  permissions: ["identity", "accounts"],
  callback: (data) => {
    switch (data.status) {
      case "SUCCESS":
        // Account linked. Webhook will follow — treat webhook as authoritative.
        break;
      case "CANCELLED":
        // User closed the SDK.
        break;
      case "ERROR":
        // Inspect data.message and data.secondary_status.
        break;
      case "REDIRECT":
        // Open Finance flow redirected — handle at your redirect URL.
        break;
    }
  },
});

Bank-list and account-selection shortcuts

Pass bank_identifier on connect, pay, or checkout to skip the bank-selection screen. Pass account_id on pay to skip the payment-source-selection screen. Both accept values from the Lean Banks API — see Create your own bank list.

Customisation

Pass a customization object to any method to override the SDK's appearance. Full field reference in Customisation.

Lean.connect({
  app_token: "<YOUR_APP_TOKEN>",
  customer_id: "CUSTOMER_ID",
  permissions: ["identity", "accounts"],
  customization: {
    theme_color: "#0080ff",
    button_text_color: "#ffffff",
    button_border_radius: "8",
  },
});

Language

Pass language: "ar" to run the SDK in Arabic with a full right-to-left layout. Omit or pass "en" for English (default).

Content Security Policy

If your site uses CSP, these hosts must be allowed or the SDK will fail to load. The safest option is to wildcard *.leantech.me and add the third-party analytics hosts separately.

Strict directives

<meta
  http-equiv="Content-Security-Policy"
  content="
    default-src 'self' https://cdn.leantech.me data: blob:;
    script-src 'self' https://cdn.leantech.me https://cdn.segment.com http://cdn.mxpnl.com;
    style-src 'self' https://cdn.leantech.me https://fonts.googleapis.com https://cdn.segment.com;
    connect-src 'self' blob:
      https://apm.ae01.leantech.me https://apm.sa01.leantech.me
      https://link.sandbox.leantech.me https://link.leantech.me
      https://link.sandbox.sa.leantech.me https://link.sa.leantech.me
      https://graphql.contentful.com
      https://api.segment.io https://cdn.segment.com
      https://api-js.mixpanel.com
      https://cdn.growthbook.io;
    img-src 'self' data:
      https://cdn.leantech.me https://images.leantech.me
      https://images.sandbox.leantech.me https://images.sa.leantech.me
      https://images.sandbox.sa.leantech.me
      https://images.ctfassets.net http://images.contentful.com
      https://graphql.contentful.com;
    font-src 'self' https://fonts.gstatic.com;
    frame-src 'self' https://cdn.leantech.me data: blob:;
  "
/>

Relaxed directives (wildcard)

<meta
  http-equiv="Content-Security-Policy"
  content="
    default-src 'self' https://*.leantech.me data: blob:;
    script-src 'self' https://*.leantech.me https://cdn.segment.com http://cdn.mxpnl.com;
    style-src 'self' https://*.leantech.me https://fonts.googleapis.com https://cdn.segment.com;
    connect-src 'self' blob: https://*.leantech.me https://graphql.contentful.com https://api.segment.io https://cdn.segment.com https://api-js.mixpanel.com https://cdn.growthbook.io;
    img-src 'self' data: https://*.leantech.me https://images.ctfassets.net http://images.contentful.com https://graphql.contentful.com;
    font-src 'self' https://fonts.gstatic.com;
    frame-src 'self' https://*.leantech.me data: blob:;
  "
/>

Resources the SDK loads

// Lean
https://*.leantech.me
https://apm.ae01.leantech.me
https://apm.sa01.leantech.me
https://cdn.leantech.me
https://images.leantech.me
https://images.sa.leantech.me
https://images.sandbox.leantech.me
https://images.sandbox.sa.leantech.me
https://link.leantech.me
https://link.sa.leantech.me
https://link.sandbox.leantech.me
https://link.sandbox.sa.leantech.me

// Third-party analytics and content
http://cdn.mxpnl.com
https://api-js.mixpanel.com
https://api.segment.io
https://cdn.segment.com
https://cdn.growthbook.io
https://fonts.googleapis.com
https://fonts.gstatic.com
https://graphql.contentful.com
http://images.contentful.com
https://images.ctfassets.net

The list may change without notice — wildcarding *.leantech.me is the simplest way to stay compatible.

Related