Skip to content

ATMs - API Guide v2.1

The ATM API exposes a single endpoint — GET /atms — that returns the details of all ATMs managed by the LFI.

Prerequisites

Before calling the ATM API, ensure the following requirements are met:

  • Registered Application The application must be created within the Trust Framework and assigned the BDSP role as defined in Roles.

  • Valid Transport Certificate An active transport certificate must be issued and registered in the Trust Framework to establish secure mTLS communication with the LFI.

  • Valid Signing Certificate An active signing certificate must be issued and registered in the Trust Framework for client authentication.

  • Understanding of Tokens & Assertions You should understand how client authentication works with private_key_jwt before calling the token endpoint.

API Sequence Flow

Click to expand

Step 1 — Build a Client Assertion

The ATM API uses the OAuth 2.0 client credentials grant with scope=atm.

Use the signJWT() helper to build a client assertion proving your application's identity:

typescript
import crypto from 'node:crypto'
import { signJWT } from './sign-jwt'

const CLIENT_ID = process.env.CLIENT_ID!
const ISSUER    = process.env.LFI_ISSUER!   // from the LFI's .well-known/openid-configuration

const clientAssertion = await signJWT({
  iss: CLIENT_ID,
  sub: CLIENT_ID,
  aud: ISSUER,
  jti: crypto.randomUUID(),
})
python
import os, uuid
from sign_jwt import sign_jwt

CLIENT_ID = os.environ["CLIENT_ID"]
ISSUER    = os.environ["LFI_ISSUER"]   # from the LFI's .well-known/openid-configuration

client_assertion = sign_jwt({
    "iss": CLIENT_ID,
    "sub": CLIENT_ID,
    "aud": ISSUER,
    "jti": str(uuid.uuid4()),
})

See Client Assertion for the full claims reference.

Step 2 — Token Request

POST to the LFI's token endpoint with scope=atm:

typescript
const TOKEN_ENDPOINT = process.env.LFI_TOKEN_ENDPOINT!

const params = new URLSearchParams({
  grant_type:            'client_credentials',
  scope:                 'atm',
  client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
  client_assertion:      clientAssertion,
})

const tokenResponse = await fetch(TOKEN_ENDPOINT, {
  method:  'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body:    params.toString(),
  // agent: new https.Agent({ cert: transportCert, key: transportKey }),
})

const { access_token } = await tokenResponse.json()
python
import httpx, os

token_endpoint = os.environ["LFI_TOKEN_ENDPOINT"]

token_response = httpx.post(
    token_endpoint,
    data={
        "grant_type":            "client_credentials",
        "scope":                 "atm",
        "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
        "client_assertion":      client_assertion,
    },
    # cert=("transport.crt", "transport.key"),
)

access_token = token_response.json()["access_token"]

Step 3 — GET /atms

Call the endpoint with the access token. Include x-fapi-interaction-id on every request. See Request Headers.

x-fapi-customer-ip-address is not required for ATMs — the data is static and public, so no PSU is involved in the call.

LFI_API_BASE is the LFI's API Hub resource server — https://rs1.<lfiCode>.apihub.openfinance.ae (production) or https://rs1.<lfiCode>.sandbox.apihub.openfinance.ae (sandbox). Resolve the <lfiCode> from the Trust Framework Directory. See API Resources for the full endpoint format.

typescript
import crypto from 'node:crypto'

const API_BASE = process.env.LFI_API_BASE!

const response = await fetch(`${API_BASE}/open-finance/atm/v2.1/atms`, {
  method: 'GET',
  headers: {
    'Authorization':         `Bearer ${access_token}`,
    'x-fapi-interaction-id': crypto.randomUUID(),
  },
  // agent: new https.Agent({ cert: transportCert, key: transportKey }),
})

const { Data, Meta } = await response.json()
// Data — array of ATM records
// Meta.TotalRecords — total count
// Meta.LastUpdatedDateTime — when the data was last refreshed
python
import httpx, uuid, os

api_base = os.environ["LFI_API_BASE"]

response = httpx.get(
    f"{api_base}/open-finance/atm/v2.1/atms",
    headers={
        "Authorization":         f"Bearer {access_token}",
        "x-fapi-interaction-id": str(uuid.uuid4()),
    },
    # cert=("transport.crt", "transport.key"),
)

payload      = response.json()
atms         = payload["Data"]
total        = payload["Meta"]["TotalRecords"]
last_updated = payload["Meta"]["LastUpdatedDateTime"]

Response structure

json
{
  "Data": [
    {
      "ATMId": "ATM-001",
      "LFIId": "ADCB",
      "LFIBrandId": "ADCB",
      "SupportedCurrencies": ["AED"],
      "SupportedLanguages": ["en", "ar"],
      "Services": ["CashWithdrawal", "Balance", "MiniStatement", "PINChange"],
      "Accessibility": ["WheelchairAccess", "AudioCashMachine"],
      "IsAccess24Hour": true,
      "Availability": {
        "Status": "Available"
      },
      "MinimumPossibleAmount": { "Amount": "20.00", "Currency": "AED" },
      "MaximumPossibleAmount": { "Amount": "5000.00", "Currency": "AED" },
      "Location": {
        "LocationCategory": ["BranchExternal"],
        "PostalAddress": {
          "StreetName": "Corniche Road",
          "TownName": "Abu Dhabi",
          "CountrySubDivision": "AbuDhabi",
          "Country": "AE"
        },
        "GeoLocation": {
          "Latitude": "24.4539",
          "Longitude": "54.3773"
        }
      }
    }
  ],
  "Meta": {
    "TotalRecords": 1,
    "LastUpdatedDateTime": "2025-03-21T08:00:00Z"
  }
}

See the GET /atms API reference for the full response schema.