Developer · API v1
API Document Guide
Complete documentation to integrate FIREPAY Gateway deposits, withdrawals, statements, and webhooks for Bangladesh currency (BDT).
Confidential — for integrating merchant developers · Last updated July 2026
Technical API payloads, field names, and code samples remain in English for integration accuracy.
Overview
FIREPAY Gateway lets your platform accept mobile-wallet deposits and send withdrawals to customer wallet numbers in Bangladesh (BDT).
Deposits
Your server creates a deposit via API → customer is redirected to a hosted payment page → customer cashes out to an agent wallet and submits their TrxID → FIREPAY automatically verifies and approves the deposit
Withdrawals
Your server creates a withdrawal via API (status pending) → admin releases to an agent (processing) → agent approves or rejects → every status change is sent to your webhook
Statements
Your server requests a date-range summary (Opening, Deposit, Withdraw, Fee, Settlement, Adjustment, Closing) — scoped to your API key only
Integration
Server-to-server REST API authenticated with an API key
Base URLs
Your FIREPAY administrator provides the production API_HOST and PAYMENT_HOST for your environment. Do not hardcode hosts from public materials.
Production
Commercial API
https://{API_HOST}/v1
Payment page
https://{PAYMENT_HOST}
Local dev
Commercial API
http://localhost:3001/v1
Payment page
http://localhost:5173
All commercial endpoints are prefixed with /v1. Payment pages are served at /deposit/{deposit_id}?token=... on the payment base URL.
Fees
amount
Gross wallet move (what the player paid or received)
fee_percent
Platform fee percent for this client
fee_amount
FIREPAY's share
client_amount
What settles to you (amount − fee_amount)
Authentication
Every commercial API request must include a valid client API key.
Obtaining an API key
Ask your FIREPAY administrator for:
- API key
- Default webhook URL
Keys are created in the backoffice Clients page. The full API key is shown once at creation — store it securely on your server. Clients do not generate their own keys.
Key format: fp_{prefix}_{secret} (example: fp_a1b2c3d4_xK9mN2pQ7rS4tU8vW1yZ3)
Request headers
Authorization
Bearer fp_xxxxxxxx
X-API-Key
Alternative to Authorization — pass the raw key
Content-Type
application/json
curl -X POST "https://{API_HOST}/v1/deposits" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 500, "wallet": "bkash", "order_id": "ORD-1001", "return_url": "https://yoursite.com/payment/return" }'Security
Response format
Success
{ "success": true, "data": { }}HTTP status 200 for reads, 201 for creates.
Error
{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "order_id and return_url are required" }}IDs & order references
Understanding these values prevents broken reconciliation after payment.
order_id (request)
Who creates it
You
Example
ORD-1001
Use for
Your unique order reference (must be unique per client)
external_order_id
Who creates it
FIREPAY (echo)
Example
ORD-1001
Use for
Same as your request order_id — match webhooks & return URL to this
order_id (response)
Who creates it
FIREPAY
Example
K7M9P2Q4R5
Use for
Internal FIREPAY order number (10 chars)
deposit_id
Who creates it
FIREPAY
Example
DPABCD1234
Use for
Public deposit ID — DP + 8 characters
withdrawal_id
Who creates it
FIREPAY
Example
WDXYZ98765
Use for
Public withdrawal ID — WD + 8 characters
Rules for integrators
- Return URL query param
order_idis always your original reference (external_order_id). - In JSON responses and webhooks, prefer
external_order_idwhen matching your shop order. - Credit the player only after webhook / GET confirmation — never from the browser redirect alone.
Quick start
- Create deposit — POST /v1/deposits with amount, wallet, unique order_id, and return_url.
- Redirect customer — Send the user to payment_url from the response.
- Customer pays — They cash out to the agent wallet number shown on the page and submit their TrxID.
- Auto verification — FIREPAY automatically verifies the payment and updates the deposit status.
- Confirm on your server — Trust webhooks (and/or poll GET /v1/deposits/:depositId). Return URL is a UX redirect only — always verify server-side before crediting the player.
Your server FIREPAY Customer | | | |-- POST /v1/deposits ---->| | |<-- payment_url ----------| | | | | |-- redirect customer ---------------------------->| | |<-- cash out + TrxID --| | |-- verify + approve ---| |<-- webhook deposit.approved | | |-- redirect return_url >|Deposit flow
End-to-end deposit lifecycle between your merchant system and FIREPAY.
- Merchant submits deposit — POST /v1/deposits
- FIREPAY validates the request
- FIREPAY returns payment_url + agent_number with status pending
- Merchant processes the response
- Merchant redirects the customer to payment_url
- Hosted page: customer pays + submits TrxID → Cash Out → automatic verification
- FIREPAY approves / rejects / expires
- Webhook callback → merchant credits if approved
- return_url is UX only — process result server-side
Critical
return_url alone — wait for deposit.approved webhook or GET status.Wallets API
/v1/walletsReturns branding for bKash / Nagad / Rocket / Upay plus the global deposit countdown minutes.
{ "success": true, "data": { "deposit_expiry_minutes": 15, "wallets": [ { "wallet": "bkash", "header_color": "#E2136E", "accent_color": "#E2136E", "logo_path": "uploads/wallet-logos/bkash.png", "logo_url": "https://{CDN_HOST}/uploads/wallet-logos/bkash.png" }, { "wallet": "nagad", "header_color": "#F6921E", "accent_color": "#F6921E", "logo_url": "https://{CDN_HOST}/uploads/wallet-logos/nagad.png" }, { "wallet": "rocket", "header_color": "#6B2D8B", "accent_color": "#00A651", "logo_url": "https://{CDN_HOST}/uploads/wallet-logos/rocket.png" }, { "wallet": "upay", "header_color": "#E31837", "accent_color": "#E31837", "logo_url": "https://{CDN_HOST}/uploads/wallet-logos/upay.png" } ] }}Deposits API
Create deposit
/v1/depositsamount
Type
number
Required
Yes
Description
Deposit amount in BDT (e.g. 500 or 500.00)
wallet
Type
string
Required
Yes
Description
bkash | nagad | rocket | upay
order_id
Type
string
Required
Yes
Description
Your unique order reference (unique per client)
return_url
Type
string
Required
Yes
Description
HTTPS URL — redirect customer after payment
callback_url
Type
string
Required
No
Description
Override webhook URL for this deposit only
{ "success": true, "data": { "deposit_id": "DPABCD1234", "order_id": "K7M9P2Q4R5", "external_order_id": "ORD-1001", "status": "pending", "amount": 500, "fee_percent": 5, "fee_amount": 25, "client_amount": 475, "wallet": "bkash", "wallet_label": "bKash", "wallet_logo_url": "https://{CDN_HOST}/uploads/wallet-logos/bkash.png", "wallet_header_color": "#E2136E", "wallet_accent_color": "#E2136E", "agent_number": "01623072275", "payment_url": "https://{PAYMENT_HOST}/deposit/DPABCD1234?token=...", "expires_at": "2026-07-14T15:30:00.000Z", "deposit_expiry_minutes": 15 }}deposit_id
Public FIREPAY deposit ID (DP + 8 chars)
order_id
Internal FIREPAY order number
external_order_id
Your original order_id from the request
agent_number
Wallet number the customer must cash out to
payment_url
Hosted payment page — redirect the customer here
expires_at
Deposit session expiry (default 15 minutes, configurable)
client_amount
Amount that settles to you after fee
Get deposit status
/v1/deposits/:depositIdPoll this if the customer closes the browser before redirect, or to confirm webhook data.
{ "success": true, "data": { "deposit_id": "DPABCD1234", "order_id": "K7M9P2Q4R5", "external_order_id": "ORD-1001", "status": "approved", "amount": 500, "fee_percent": 5, "fee_amount": 25, "client_amount": 475, "wallet": "bkash", "wallet_label": "bKash", "agent_number": "01623072275", "customer_trx_id": "DFC78W6RL3", "return_url": "https://yoursite.com/payment/return", "expires_at": "2026-07-14T15:30:00.000Z", "approved_at": "2026-07-14T15:12:00.000Z", "created_at": "2026-07-14T15:00:00.000Z" }}Submit TrxID (server-side, optional)
/v1/deposits/:depositId/submit-trxRate limit: 10 requests per minute per IP. Normally the customer submits TrxID on the hosted payment page. This endpoint is available if you build a custom payment UI.
trx_id
Type
string
Required
Yes
Description
Customer transaction ID from their wallet app
token
Type
string
Required
Yes
Description
Page token from payment_url
{ "success": true, "data": { "deposit_id": "DPABCD1234", "status": "awaiting_sms", "message": "Transaction ID submitted. Awaiting confirmation.", "verification_expires_at": "2026-07-14T15:15:00.000Z" }}If payment is already verified, status may be approved immediately with a return_url in the response (including your original order_id as the query param).
Hosted payment page
Redirect the customer to payment_url. The page is wallet-branded (bKash / Nagad / Rocket / Upay) and shows the assigned agent cash-out number.
1
Customer sees amount, agent wallet number, and deposit timer
2
Customer sends money from their wallet app (Cash Out / Send Money)
3
Customer enters TrxID and taps Submit
4
If deposit timer hits 00:00 → redirect with status=expired
5
Verification wait while payment is confirmed
6
On approval → status=approved; verify timeout → status=rejected
Return URL query parameters
FIREPAY appends these parameters when redirecting the customer:
status
Example
approved
Description
approved | review | fraud | rejected | expired
deposit_id
Example
DPABCD1234
Description
FIREPAY deposit ID
order_id
Example
ORD-1001
Description
Your original order_id (external_order_id)
https://yoursite.com/payment/return?status=approved&deposit_id=DPABCD1234&order_id=ORD-1001Do not credit from return URL
Withdrawal flow
Create → pending (backoffice hold) → admin processing → agent result → webhooks.
- Merchant submits withdrawal — POST /v1/withdrawals
- FIREPAY validates the request
- Status pending — held in backoffice; webhook: withdrawal.pending
- Merchant processes the response
- Admin sets processing (agent assigned); webhook: withdrawal.processing
- Agent/admin approved / rejected — callback result
- Merchant completes or refunds
Optional: poll GET /v1/withdrawals/:id if webhook is delayed.
Withdrawals API
Request a payout to a customer mobile-wallet number. Flow: create → status pending (held in backoffice, no agent yet) → admin sets processing (agent assigned) → agent approves or rejects. FIREPAY notifies your webhook on every status change.
Create withdrawal
/v1/withdrawalsamount
Type
number
Required
Yes
Description
Withdrawal amount in BDT
wallet
Type
string
Required
Yes
Description
bkash | nagad | rocket | upay
customer_phone
Type
string
Required
Yes
Description
Recipient wallet number (BD mobile, e.g. 017XXXXXXXX)
order_id
Type
string
Required
Yes
Description
Your unique withdrawal reference (unique per client)
callback_url
Type
string
Required
No
Description
Override webhook URL for this withdrawal only
{ "success": true, "data": { "withdrawal_id": "WDXYZ98765", "order_id": "N4P6Q8R0S1", "external_order_id": "WDR-9001", "status": "pending", "amount": 1000, "fee_percent": 2, "fee_amount": 20, "client_amount": 980, "wallet": "bkash", "wallet_label": "bKash", "customer_phone": "01712345678", "assigned_agent_id": null, "auto_withdraw": false, "created_at": "2026-07-14T15:00:00.000Z" }}On create, FIREPAY also sends withdrawal.pending to your webhook. Withdrawals do not auto-expire while pending or processing.
Get withdrawal status
/v1/withdrawals/:withdrawalId{ "success": true, "data": { "withdrawal_id": "WDXYZ98765", "order_id": "N4P6Q8R0S1", "external_order_id": "WDR-9001", "status": "approved", "amount": 1000, "fee_percent": 2, "fee_amount": 20, "client_amount": 980, "wallet": "bkash", "wallet_label": "bKash", "customer_phone": "01712345678", "agent_trx_id": "8XJ2K9LM", "reject_remarks": null, "expires_at": null, "approved_at": "2026-07-14T15:08:00.000Z", "rejected_at": null, "created_at": "2026-07-14T15:00:00.000Z" }}Statements API
Fetch your Client Summary totals for a date range (same numbers as the FIREPAY Report → Client Summary page).
Security & timezone
Get statement summary
/v1/statements/summaryfrom_date
Type
string
Required
Yes
Description
Start date YYYY-MM-DD (inclusive)
to_date
Type
string
Required
Yes
Description
End date YYYY-MM-DD (inclusive)
{ "success": true, "data": { "from_date": "2026-07-28", "to_date": "2026-07-28", "timezone": "Asia/Dhaka", "currency": "BDT", "opening": 3338.8, "deposit": 0, "withdraw": 1100, "fee": 9.9, "settlement": 0, "adjustment": 0, "closing": 2248.7 }}opening
Opening balance at the start of from_date
deposit
Approved deposits in the period (gross)
withdraw
Approved withdrawals in the period (gross)
fee
Total fees in the period (deposit + withdraw)
settlement
Client settlements / top-ups net in the period
adjustment
Client adjustments net in the period
closing
Closing balance at the end of to_date
curl -G "https://{API_HOST}/v1/statements/summary" \ -H "Authorization: Bearer YOUR_API_KEY" \ --data-urlencode "from_date=2026-07-28" \ --data-urlencode "to_date=2026-07-28"Webhooks
Ask your administrator to set a default webhook URL on your client account (backoffice → Clients → Edit client). FIREPAY sends POST requests with JSON event payloads for both deposits and withdrawals.
Delivery
Method
POST
Content-Type
application/json
User-Agent
FIREPAY-Webhook/1.0
Timeout
10 seconds
Retries
Up to 3
Respond with HTTP 200 quickly. Handle events idempotently (same deposit_id / withdrawal_id may be delivered more than once).
Per-transaction override
Pass optional callback_url when creating a deposit or withdrawal to override the client default webhook URL for that transaction only.
Events
deposit.approved
When fired
Payment confirmed
Action on your side
Credit the player
deposit.review
When fired
Held for review (e.g. balance chain)
Action on your side
Do not credit yet
deposit.fraud
When fired
Fraud signal
Action on your side
Do not credit
deposit.rejected
When fired
Rejected (verify timeout / admin)
Action on your side
Do not credit / mark failed
withdrawal.pending
When fired
Withdrawal created (held in backoffice)
Action on your side
Record as pending on your side
withdrawal.processing
When fired
Admin released to an agent
Action on your side
Update status — payout in progress
withdrawal.approved
When fired
Agent (or admin) completed payout
Action on your side
Complete withdrawal on your side
withdrawal.rejected
When fired
Agent (or admin) rejected the withdrawal
Action on your side
Mark failed / refund flow
withdrawal.expired
When fired
Marked expired by admin (rare)
Action on your side
Mark failed / refund flow
Payload structure
{ "event": "deposit.approved", "timestamp": "2026-07-14T15:12:00.000Z", "data": { }}{ "event": "deposit.approved", "timestamp": "2026-07-14T15:12:00.000Z", "data": { "deposit_id": "DPABCD1234", "order_id": "K7M9P2Q4R5", "external_order_id": "ORD-1001", "amount": 500, "fee_percent": 5, "fee_amount": 25, "client_amount": 475, "wallet": "bkash", "trx_id": "DFC78W6RL3", "approved_at": "2026-07-14T15:12:00.000Z" }}{ "event": "deposit.review", "timestamp": "2026-07-14T15:12:00.000Z", "data": { "deposit_id": "DPABCD1234", "order_id": "K7M9P2Q4R5", "external_order_id": "ORD-1001", "amount": 500, "fee_percent": 5, "fee_amount": 25, "client_amount": 475, "wallet": "nagad", "trx_id": "75NRHGZ1", "status": "review", "reason": "Balance chain mismatch — manual review required", "at": "2026-07-14T15:12:00.000Z" }}{ "event": "deposit.rejected", "timestamp": "2026-07-14T15:18:00.000Z", "data": { "deposit_id": "DPABCD1234", "order_id": "K7M9P2Q4R5", "external_order_id": "ORD-1001", "amount": 500, "fee_percent": 5, "fee_amount": 25, "client_amount": 475, "wallet": "bkash", "trx_id": "DFC78W6RL3", "remarks": null, "rejected_at": "2026-07-14T15:18:00.000Z" }}{ "event": "withdrawal.pending", "timestamp": "2026-07-14T15:00:00.000Z", "data": { "withdrawal_id": "WDXYZ98765", "order_id": "N4P6Q8R0S1", "external_order_id": "WDR-9001", "amount": 1000, "fee_percent": 2, "fee_amount": 20, "client_amount": 980, "wallet": "bkash", "customer_phone": "01712345678", "status": "pending", "created_at": "2026-07-14T15:00:00.000Z" }}{ "event": "withdrawal.processing", "timestamp": "2026-07-14T15:05:00.000Z", "data": { "withdrawal_id": "WDXYZ98765", "order_id": "N4P6Q8R0S1", "external_order_id": "WDR-9001", "amount": 1000, "fee_percent": 2, "fee_amount": 20, "client_amount": 980, "wallet": "bkash", "customer_phone": "01712345678", "status": "processing", "at": "2026-07-14T15:05:00.000Z" }}{ "event": "withdrawal.approved", "timestamp": "2026-07-14T15:08:00.000Z", "data": { "withdrawal_id": "WDXYZ98765", "order_id": "N4P6Q8R0S1", "external_order_id": "WDR-9001", "amount": 1000, "fee_percent": 2, "fee_amount": 20, "client_amount": 980, "wallet": "bkash", "customer_phone": "01712345678", "status": "approved", "trx_id": "8XJ2K9LM", "approved_at": "2026-07-14T15:08:00.000Z" }}{ "event": "withdrawal.rejected", "timestamp": "2026-07-14T15:18:00.000Z", "data": { "withdrawal_id": "WDXYZ98765", "order_id": "N4P6Q8R0S1", "external_order_id": "WDR-9001", "amount": 1000, "fee_percent": 2, "fee_amount": 20, "client_amount": 980, "wallet": "bkash", "customer_phone": "01712345678", "status": "rejected", "remarks": "Invalid account number", "rejected_at": "2026-07-14T15:18:00.000Z" }}{ "event": "withdrawal.expired", "timestamp": "2026-07-14T16:00:00.000Z", "data": { "withdrawal_id": "WDXYZ98765", "order_id": "N4P6Q8R0S1", "external_order_id": "WDR-9001", "amount": 1000, "fee_percent": 2, "fee_amount": 20, "client_amount": 980, "wallet": "bkash", "customer_phone": "01712345678", "status": "expired", "expired_at": "2026-07-14T16:00:00.000Z" }}Status reference
Deposit statuses
pending
Created; customer has not submitted TrxID yet
awaiting_trx
Intermediate / transitional state
awaiting_sms
TrxID submitted; awaiting payment confirmation
approved
Payment confirmed — credit the customer
review
Held for manual review — do not credit yet
fraud
Fraud signal — do not credit
rejected
Verification failed, timed out, or admin rejected
expired
Deposit session timed out
Withdrawal statuses
pending
Created; held in backoffice until admin releases (webhook: withdrawal.pending)
processing
Assigned to an agent for payout (webhook: withdrawal.processing)
approved
Agent/admin completed payout — complete on your side (webhook: withdrawal.approved)
rejected
Agent/admin rejected (see reject_remarks / webhook remarks)
expired
Marked expired by admin (rare; no auto-expiry on pending/processing)
Error codes
401
Code
UNAUTHORIZED
Description
Missing or invalid API key
403
Code
CLIENT_INACTIVE
Description
Client account disabled
403
Code
FORBIDDEN
Description
Invalid payment page token
400
Code
VALIDATION_ERROR
Description
Missing or invalid request fields
400
Code
INVALID_AMOUNT
Description
Amount must be positive
400
Code
INVALID_WALLET
Description
Wallet must be bkash, nagad, rocket, or upay
400
Code
INVALID_PHONE
Description
customer_phone is not a valid BD mobile
400
Code
INVALID_TRX_ID
Description
Transaction ID format invalid
409
Code
DUPLICATE_ORDER
Description
order_id already used for this client
409
Code
DUPLICATE_TRX_ID
Description
TrxID already used for this wallet
409
Code
ALREADY_PROCESSED
Description
Withdrawal already approved or rejected
404
Code
NOT_FOUND
Description
Deposit or withdrawal not found
410
Code
EXPIRED
Description
Deposit or withdrawal window expired
Code examples
Node.js — create deposit, redirect, and verify
const API_KEY = process.env.FIREPAY_API_KEY;const BASE = process.env.FIREPAY_API_BASE; // https://{API_HOST}/v1 async function createDeposit({ amount, wallet, orderId, returnUrl }) { const res = await fetch(`${BASE}/deposits`, { method: 'POST', headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount, wallet, order_id: orderId, return_url: returnUrl, }), }); const json = await res.json(); if (!json.success) throw new Error(json.error?.message); return json.data; // payment_url, deposit_id, external_order_id} async function getDeposit(depositId) { const res = await fetch(`${BASE}/deposits/${depositId}`, { headers: { Authorization: `Bearer ${API_KEY}` }, }); const json = await res.json(); if (!json.success) throw new Error(json.error?.message); return json.data;} // Checkout:app.post('/checkout/deposit', async (req, res) => { const deposit = await createDeposit({ amount: req.body.amount, wallet: 'bkash', orderId: `ORD-${Date.now()}`, returnUrl: 'https://yoursite.com/payment/return', }); res.redirect(deposit.payment_url);}); // After redirect — verify server-side:app.get('/payment/return', async (req, res) => { const deposit = await getDeposit(req.query.deposit_id); if ( deposit.status === 'approved' && deposit.external_order_id === req.query.order_id ) { // creditPlayer(deposit.external_order_id, deposit.amount); } res.send('OK');});Node.js — create withdrawal
async function createWithdrawal({ amount, wallet, customerPhone, orderId, callbackUrl }) { const res = await fetch(`${BASE}/withdrawals`, { method: 'POST', headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount, wallet, customer_phone: customerPhone, order_id: orderId, ...(callbackUrl ? { callback_url: callbackUrl } : {}), }), }); const json = await res.json(); if (!json.success) throw new Error(json.error?.message); return json.data;}PHP — create deposit
<?php$apiKey = getenv('FIREPAY_API_KEY');$payload = [ 'amount' => 500, 'wallet' => 'bkash', 'order_id' => 'ORD-' . time(), 'return_url' => 'https://yoursite.com/payment/return',];$ch = curl_init('https://{API_HOST}/v1/deposits');curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $apiKey, 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_RETURNTRANSFER => true,]);$response = json_decode(curl_exec($ch), true);curl_close($ch);if (!$response['success']) { throw new Exception($response['error']['message'] ?? 'API error');}header('Location: ' . $response['data']['payment_url']);exit;Python — poll deposit status
import osimport requests API_KEY = os.environ["FIREPAY_API_KEY"]BASE = os.environ["FIREPAY_API_BASE"] # https://{API_HOST}/v1HEADERS = {"Authorization": f"Bearer {API_KEY}"} def get_deposit(deposit_id: str) -> dict: r = requests.get(f"{BASE}/deposits/{deposit_id}", headers=HEADERS, timeout=30) r.raise_for_status() data = r.json() if not data.get("success"): raise RuntimeError(data.get("error", {}).get("message")) return data["data"] # Match your order with external_order_id# d = get_deposit("DPABCD1234")# if d["status"] == "approved" and d["external_order_id"] == "ORD-1001":# credit_player(...)Production checklist
- Use HTTPS for all API calls and webhook endpoints
- Store API keys server-side only
- Configure a webhook URL with your FIREPAY administrator (or use callback_url)
- Use a unique order_id per deposit and withdrawal
- Match shop orders using external_order_id
- Credit only on deposit.approved (webhook and/or confirmed GET)
- Poll GET /v1/deposits/:id if the customer closes the browser before redirect
- Handle all withdrawal webhook events (pending, processing, approved, rejected)
- Handle webhook events idempotently (retries)
- Use GET /v1/statements/summary for your Opening / Deposit / Withdraw / Fee / Settlement / Adjustment / Closing totals
- Request API key + webhook URL from your FIREPAY administrator before go-live
