8.1 KiB
BML Internet Banking API Documentation
Reverse-engineered from traffic captures of the BML Mobile Banking Android app (mv.com.bml.mib).
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:
- Web login (credentials + TOTP)
- Profile activation
- PKCE OAuth token exchange
- 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 (Xiaomi; Android 14; 22101320I)
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="{"component":"Login","props":{...}}">
To extract: find data-page="...", unescape HTML entities (" → ", & → &, ' → ', < → <, > → >), 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:
- Attempt to refresh using the stored
refresh_token→ Token Refresh - 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