Files
thijooree/docs/bmlapi
Shihaam Abdul Rahman 256f216da4
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 4s
update docs
2026-05-23 23:46:00 +05:00
..
2026-05-23 23:33:31 +05:00
2026-05-23 23:33:31 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00
2026-05-23 23:46:00 +05:00

BML Internet Banking API Documentation

Reverse-engineered from traffic captures of the BML Mobile Banking Android app (mv.com.bml.mib).

Play Store


Overview

Bank of Maldives (BML) uses a hybrid authentication model: a web session flow (cookie-based, Inertia.js frontend) handles login and profile selection, which then feeds into a PKCE OAuth 2.0 exchange to obtain a Bearer token for the REST API.

The login process is stateful and must be executed in order:

  1. Web login (credentials + TOTP)
  2. Profile activation
  3. PKCE OAuth token exchange
  4. Authenticated REST API calls using the Bearer token

Base URLs

Purpose Base URL
Web login / OAuth https://www.bankofmaldives.com.mv/internetbanking
REST API (authenticated) https://www.bankofmaldives.com.mv/internetbanking/api/mobile
Foreign limits API https://app.bankofmaldives.com.mv/api/v2

Authentication Model

Value How obtained How used
XSRF-TOKEN cookie Set by server on GET /web/login Sent as X-XSRF-TOKEN header on all web POST requests
blaze_session cookie Set by server during web flow Managed automatically by cookie jar
blaze_identity cookie Set by server after profile activation Managed automatically; identifies the active profile
access_token Returned by POST /oauth/token after PKCE exchange Sent as Authorization: Bearer <token> on all REST API calls
refresh_token Returned alongside access_token Used to obtain a new access_token without re-login

The web flow uses a standard browser cookie jar. The REST API only needs the Bearer token — no cookies required after the OAuth exchange.


OAuth 2.0 PKCE Parameters

Parameter Value
client_id 98C83590-513F-4716-B02B-EC68B7D9E7E7
redirect_uri https://app.bankofmaldives.com.mv/oauth/mobile-callback
response_type code
code_challenge_method S256

The code_verifier is a cryptographically random 72-byte value, base64url-encoded (no padding). The code_challenge is the SHA-256 hash of the verifier, also base64url-encoded.

The Device-ID is a random 8-byte hex string generated once per login session.


User-Agent Strategy

Two different User-Agent strings are used depending on the phase:

Phase User-Agent to use
Web login steps (GET/POST /web/*, /oauth/authorize) Browser UA
OAuth token endpoint (POST /oauth/token) Browser UA
All authenticated REST API calls (/api/mobile/*, /api/v2/*) App UA

Browser UA (used during the entire web session and OAuth flow):

Mozilla/5.0 (Android 14; Mobile; rv:150.0) Gecko/150.0 Firefox/150.0

App UA (used for all REST API calls after the token is obtained):

bml-mobile-banking/348 (<Build.MANUFACTURER>; Android <Build.VERSION.RELEASE>; <Build.MODEL>)

Example app UA:

bml-mobile-banking/348 ({manufacturer}; Android {version}; {model})

Inertia.js Response Format

The BML web frontend is built with Inertia.js. All web page responses embed their data as HTML-escaped JSON in the data-page attribute of the root <div>:

<div id="app" data-page="{&quot;component&quot;:&quot;Login&quot;,&quot;props&quot;:{...}}">

To extract: find data-page="...", unescape HTML entities (&quot;", &amp;&, &#39;', &lt;<, &gt;>), then parse as JSON. The useful data is inside the props key.


Login Flow

Client                                            Server
  |                                                 |
  |  GET /web/login                                 |  ← seeds XSRF-TOKEN + blaze_session cookies
  |------------------------------------------------>|
  |  Set-Cookie: XSRF-TOKEN=...; blaze_session=...  |
  |<------------------------------------------------|
  |                                                 |
  |  POST /web/login                                |  ← JSON: {username, password, code:""}
  |  X-XSRF-TOKEN: <xsrf>                          |
  |------------------------------------------------>|
  |  302 Redirect → /web/login/2fa                  |
  |<------------------------------------------------|
  |                                                 |
  |  GET /web/login/2fa                             |  ← refreshes cookies
  |------------------------------------------------>|
  |  Set-Cookie: XSRF-TOKEN=<new>                   |
  |<------------------------------------------------|
  |                                                 |
  |  POST /web/login/2fa                            |  ← JSON: {code: <TOTP>, channel: "authenticator"}
  |  X-XSRF-TOKEN: <xsrf2>                         |
  |------------------------------------------------>|
  |  302 Redirect → /web/profile                    |
  |<------------------------------------------------|
  |                                                 |
  |  GET /web/profile                               |  ← profile picker (multi) or auto-redirect (single)
  |------------------------------------------------>|
  |  200 (profile list) OR 302 (auto-activated)     |
  |<------------------------------------------------|
  |                                                 |
  |  (multi-profile) GET /web/profile/{profileId}   |  ← activate selected profile
  |------------------------------------------------>|
  |  302 → /web/redirect (personal)                 |
  |  302 → /web/profile/2fa/business (business)     |
  |<------------------------------------------------|
  |                                                 |
  |  GET /oauth/authorize?...&code_challenge=...    |  ← PKCE authorize
  |------------------------------------------------>|
  |  302 → mobile-callback?code=<auth_code>         |
  |<------------------------------------------------|
  |                                                 |
  |  POST /oauth/token                              |  ← exchange auth code for tokens
  |  {code, code_verifier, grant_type, client_id}   |
  |------------------------------------------------>|
  |  {access_token, refresh_token, expires_in}      |
  |<------------------------------------------------|
  |                                                 |
  |  GET /api/mobile/dashboard                      |  ← authenticated REST API
  |  Authorization: Bearer <access_token>           |
  |------------------------------------------------>|
  |  {success: true, payload: {dashboard: [...]}}   |
  |<------------------------------------------------|

Session Expiry

The access token expires after expires_in seconds (typically 3600). On a 401 or 419 response from any REST endpoint:

  1. Attempt to refresh using the stored refresh_tokenToken Refresh
  2. If refresh fails, re-run the full login flow

Transfer Types

Code Description
IAT Internal Account Transfer — BML account to BML account
QTR Quick Transfer — transfer via PayMV alias
DOT Domestic Outside Transfer — BML to another bank (e.g. MIB)

Documents

# File Description
1 Login Web login: credentials, TOTP, profile selection
2 Business Profile OTP SMS/email OTP for business profile activation
3 OAuth Token PKCE token exchange and token refresh
4 Dashboard Fetch all accounts (CASA, card, loan)
5 User Info User profile details and session health check
6 Account History Paginated transaction history for CASA accounts
7 Card Statement Card transaction history (prepaid, credit, debit)
8 Transfer Initiate and confirm fund transfers
9 Contacts Saved beneficiaries — list, save, delete
10 Account Validation Validate BML accounts, aliases, and MIB accounts
11 Foreign Limits USD foreign transaction limits by card and channel

 


Next → Login