Iron Pay API

One authenticated endpoint powers everything. Send a JSON body with a cmd field to verify received payments, read live wallet balances, and send money from a connected KBZPay or Wave account.

Base https://ironpay.ironx.dev Auth Bearer API key Format JSON Fee 1% on verify
POSThttps://ironpay.ironx.dev/api/v1/exec

Alias: POST https://ironpay.ironx.dev/api/v1. The cmd field selects the operation; provider is kbzpay (default), wave, or aya.

Envelope — every response
{
  "success": true,
  "message": "<tag>",
  "data": {}
}
{
  "success": false,
  "message": "<human readable>",
  "code": "<machine_code>"
}
👤
You can connect multiple accounts per provider. Pass account_id to choose which connected account a command uses. If omitted, the default/first account is used.

Account ID format: by default it is the wallet phone number with the leading 0 and country code 95 stripped (e.g. 09xxxxxxxxx9xxxxxxxxx). Use the exact value returned by cmd: account.

Authentication

Create an API key from your dashboard at /web → API Keys → Generate new key. Send it as a Bearer token on every request. API keys are scoped to the user; use account_id on each request to select which of the user's connected wallets to use.

HTTP headers
Authorization: Bearer iron_…
Content-Type: application/json
🔑
Keep keys server-side. A leaked key can move money from connected wallets. Revoke instantly from /keys in the bot.

The exec endpoint

All operations share one route. Switch behaviour with cmd:

cmdDoesKey fields
verifyConfirm a received payment (charges fee)provider, digits
balanceLive wallet balance + service depositprovider
checkResolve a recipient's name before transferringprovider, phone
transferSend money from the connected walletprovider, phone, amount, note, pin
transfer_otpComplete a KBZPay OTP-challenged transfertx_id, otp
transfer_resendResend the transfer OTPtx_id
accountConnection state for a providerprovider
account_id optionalall commandsSelects which connected account to use. Defaults to the normalized phone of the first account. Use the exact value from cmd: account.

cmd verify

Match a received (credit) transaction by the last digits of its id and confirm it. A fee of 1% of the amount is charged from your deposit on success.

Request

cURL
# POST https://ironpay.ironx.dev/api/v1/exec
curl -X POST https://ironpay.ironx.dev/api/v1/exec \n  -H "Authorization: Bearer iron_…" \n  -H "Content-Type: application/json" \n  -d '{"cmd":"verify","provider":"kbzpay","digits":"78622","account_id":"9978114808"}'
FieldTypeNotes
cmd requiredstring"verify"
digits requiredstringLast digits of the transaction id (e.g. last 5)
provider optionalstringkbzpay (default), wave, or aya
account_id optionalstringSelects which connected account to use. Must be the exact normalized phone returned by cmd: account (e.g. 9xxxxxxxxx, not 09xxxxxxxxx).

Response 200

200 OK
{
  "success": true,
  "message": "verified",
  "data": {
    "provider": "kbzpay",
    "transaction_id": "01004158021237478622",
    "amount": 1500,
    "amount_text": "1,500.00",
    "currency": "MMK",
    "sender": "Aung Aung",
    "time": "2026-05-01T08:13:35.000Z",
    "time_unix": 1746086015000,
    "fee_charged": "15.00",
    "fee_minor": 1500,
    "fee_rate_bps": 100,
    "fee_rate": "1%",
    "deposit_remaining": "985.00",
    "deposit_remaining_minor": 98500
  }
}

Errors

402insufficient_deposit 404not_found 409ambiguous_match · account_not_connected

cmd balance

Live balance of the connected wallet, plus your service-side deposit (used to pay verify fees).

Request

cURL
# POST https://ironpay.ironx.dev/api/v1/exec
curl -X POST https://ironpay.ironx.dev/api/v1/exec \n  -H "Authorization: Bearer iron_…" \n  -H "Content-Type: application/json" \n  -d '{"cmd":"balance","provider":"wave","account_id":"9978114808"}'
FieldTypeNotes
cmd requiredstring"balance"
provider optionalstringkbzpay (default) / wave / aya
account_id optionalstringSelects which connected account to use. Must be the exact normalized phone returned by cmd: account (e.g. 9xxxxxxxxx, not 09xxxxxxxxx).

Response 200

200 OK
{
  "success": true,
  "message": "",
  "data": {
    "provider": "wave",
    "msisdn": "09790000000",
    "wallet_balance": 254300,
    "wallet_total": 254300,
    "wallet_frozen": null,  // Wave reports one balance; null. KBZPay fills this.
    "currency": "MMK",
    "deposit": "985.00",
    "deposit_minor": 98500,
    "fee_rate_bps": 100,
    "fee_rate": "1%"
  }
}

cmd check

Resolve a recipient's registered name before sending — the "who am I paying?" confirmation the wallet apps show. No fee, no PIN. Works on all three providers.

Request

cURL
# POST https://ironpay.ironx.dev/api/v1/exec
curl -X POST https://ironpay.ironx.dev/api/v1/exec \n  -H "Authorization: Bearer iron_…" \n  -H "Content-Type: application/json" \n  -d '{"cmd":"check","provider":"kbzpay","phone":"09790000000","account_id":"9978114808"}'
FieldTypeNotes
cmd requiredstring"check" (aliases: check_account, beneficiary)
phone requiredstringRecipient, 09xxxxxxxxx
provider optionalstringkbzpay (default) / wave / aya
account_id optionalstringSelects which connected account to use. Must be the exact normalized phone returned by cmd: account (e.g. 9xxxxxxxxx, not 09xxxxxxxxx).

Response 200

200 account_found
{
  "success": true,
  "message": "account_found",
  "data": {
    "provider": "kbzpay",
    "registered": true,
    "recipient": "09790000000",
    "recipient_name": "Paing Phyo Khant",
    "name": "Paing Phyo Khant",
    "status": "active",
    "currency": "MMK"
  }
}

Errors

400bad_phone 404not_registered 409account_not_connected

cmd transfer

Send money from the connected wallet. Wave completes in one call. KBZPay may return an otp_required challenge for new/untrusted devices — finish it with transfer_otp.

Request

cURL
# POST https://ironpay.ironx.dev/api/v1/exec
curl -X POST https://ironpay.ironx.dev/api/v1/exec \n  -H "Authorization: Bearer iron_…" \n  -H "Content-Type: application/json" \n  -d '{"cmd":"transfer","provider":"kbzpay","phone":"09790000000","amount":1000,"note":"Invoice 42","pin":"123456","account_id":"9978114808"}'
FieldTypeNotes
cmd requiredstring"transfer"
phone requiredstringRecipient, 09xxxxxxxxx
amount requirednumberMMK, > 0
pin requiredstringWallet PIN
note requiredstringTransfer note/reference, max 100 chars
provider optionalstringkbzpay (default) / wave / aya
account_id optionalstringSelects which connected account to use. Must be the exact normalized phone returned by cmd: account (e.g. 9xxxxxxxxx, not 09xxxxxxxxx).

Response — completed 200

200 transfer_success
{
  "success": true,
  "message": "transfer_success",
  "data": {
    "provider": "wave",
    "transaction_id": "WV2605110012345",
    "amount": 1000,
    "recipient": "09790000000",
    "recipient_name": "Mya Mya",
    "note": "Invoice 42",
    "currency": "MMK",
    "status": "success",
    "time": "2026-06-11T09:42:10.000Z",
    "message": "Transfer successful"
  }
}

Response — OTP required 200

KBZPay only, when the device is challenged. Hold the tx_id and submit the SMS code via transfer_otp within 5 minutes.

200 otp_required
{
  "success": true,
  "message": "otp_required",
  "data": {
    "otp_required": true,
    "tx_id": "9f1c4ad2e7b84a01bc33de55aa12f088",
    "sent_to": "09790000000",
    "next_cmd": "transfer_otp",
    "hint": "An SMS code was sent. Call cmd 'transfer_otp' with { tx_id, otp }."
  }
}

Errors

400bad_phone · bad_amount · no_pin · pin_failed 409account_not_connected · verify_required

cmd transfer_otp

Finish a KBZPay transfer that returned otp_required. On success the money moves and you get the same transfer_success shape.

Request

cURL
# POST https://ironpay.ironx.dev/api/v1/exec
curl -X POST https://ironpay.ironx.dev/api/v1/exec \n  -H "Authorization: Bearer iron_…" \n  -H "Content-Type: application/json" \n  -d '{"cmd":"transfer_otp","tx_id":"9f1c4ad2e7b84a01bc33de55aa12f088","otp":"456789","account_id":"9978114808"}'
FieldTypeNotes
cmd requiredstring"transfer_otp"
tx_id requiredstringOTP-challenged transfer id returned by transfer
otp requiredstringSMS OTP code
account_id optionalstringSelects which connected account to use. Must be the exact normalized phone returned by cmd: account (e.g. 9xxxxxxxxx, not 09xxxxxxxxx).

Response 200

200 transfer_success
{
  "success": true,
  "message": "transfer_success",
  "data": {
    "provider": "kbzpay",
    "transaction_id": "0100415802123747xxxx",
    "amount": 1000,
    "recipient": "09790000000",
    "note": "Invoice 42",
    "currency": "MMK",
    "status": "Success",
    "title": "Transfer Successful",
    "time": "2026-06-11T09:43:55.000Z"
  }
}

Errors

400otp_wrong · tx_expired · no_otp

cmd transfer_resend

Resend the SMS code for a parked KBZPay transfer (extends the 5-minute window).

Request

cURL
# POST https://ironpay.ironx.dev/api/v1/exec
curl -X POST https://ironpay.ironx.dev/api/v1/exec \n  -H "Authorization: Bearer iron_…" \n  -H "Content-Type: application/json" \n  -d '{"cmd":"transfer_resend","tx_id":"9f1c4ad2e7b84a01bc33de55aa12f088","account_id":"9978114808"}'
FieldTypeNotes
cmd requiredstring"transfer_resend"
tx_id requiredstringOTP-challenged transfer id returned by transfer
account_id optionalstringSelects which connected account to use. Must be the exact normalized phone returned by cmd: account (e.g. 9xxxxxxxxx, not 09xxxxxxxxx).
200 OK
{
  "success": true,
  "message": "",
  "data": {
    "sent_to": "09790000000"
  }
}

cmd account

Check whether a provider is connected for your account.

Request

cURL
# POST https://ironpay.ironx.dev/api/v1/exec
curl -X POST https://ironpay.ironx.dev/api/v1/exec \n  -H "Authorization: Bearer iron_…" \n  -H "Content-Type: application/json" \n  -d '{"cmd":"account","provider":"kbzpay","account_id":"9978114808"}'
FieldTypeNotes
cmd requiredstring"account"
provider optionalstringkbzpay (default) / wave / aya
account_id optionalstringSelects which connected account to use. Must be the exact normalized phone returned by cmd: account (e.g. 9xxxxxxxxx, not 09xxxxxxxxx).

Response 200

200 OK
{
  "success": true,
  "message": "",
  "data": {
    "provider": "kbzpay",
    "connected": true,
    "state": "connected",
    "msisdn": "09790000000"
  }
}

Errors

Errors return { success:false, message, code } with a matching HTTP status.

StatuscodeMeaning
400no_cmd / bad_cmdMissing or unknown cmd
400bad_phone / bad_amount / no_pinTransfer validation failed
401no_key / invalid_keyMissing or revoked API key
402insufficient_depositTop up to verify
404not_foundNo matching transaction
404not_registeredNo account for this number (check)
409account_not_connected / account_disconnectedReconnect in the app
409ambiguous_matchProvide more digits
429rate_limited60 requests / minute / key
502upstream_error / transfer_failedWallet provider error

Rate limits & billing

⏱️
60 requests per minute per API key. Exceeding it returns 429 rate_limited.
💸
1% of the matched amount is charged from your deposit on every successful verify. balance, check, transfer and account are not billed.