AakashPay API Docs
Create payment orders, open hosted bKash/Nagad/Upay checkout, verify Transaction IDs, and connect SMS Reader devices through one production-ready API.
https://akashpay-api.axura.workers.dev. Keep API keys and device credentials on trusted backend infrastructure.AakashPay API Documentation
Production API v1.0
Create payment orders, send customers to a hosted bKash/Nagad/Upay checkout, verify Transaction IDs, and monitor SMS Reader activity through one developer-friendly API.
Live environment:
https://akashpay-api.axura.workers.dev
Quick Start · Authentication · Create Payment · Verify Payment · Device API · Errors · Production Checklist
At a glance
| Environment | Base URL |
|---|---|
| Production | https://akashpay-api.axura.workers.dev |
| Capability | Method | Endpoint | Authentication |
|---|---|---|---|
| Service health | GET |
/v1/health |
None |
| Configured gateways | GET |
/v1/gateways |
X-API-Key |
| Create payment | POST |
/v1/payment/create |
X-API-Key |
| Check payment | GET |
/v1/payment/{orderId} |
X-API-Key |
| Public checkout status | GET |
/v1/public/payment/{orderId} |
Order ID bearer |
| Customer Transaction ID verification | POST |
/v1/public/payment/{orderId}/verify |
Order ID bearer |
| Device registration | POST |
/v1/public/devices/register |
None on initial registration |
| SMS logs | GET |
/v1/public/logs/{clientId} |
X-Device-ID |
| Gateway numbers | GET/PATCH |
/v1/public/settings/{clientId}/numbers |
X-Device-ID |
| API-key management | GET/POST |
/v1/public/settings/{clientId}/apikey… |
X-Device-ID |
| Owner monitoring | GET |
/admin/overview |
X-Admin-Token |
AakashPay currently supports the gateway identifiers bkash, nagad, and upay. An order remains valid for 15 minutes after creation. The payment system first attempts automatic verification from the configured SMS Reader scope; the hosted checkout can later accept a customer-supplied Transaction ID as a fallback.
Overview
AakashPay is designed for applications that collect payments through Bangladeshi mobile-finance gateways. The integrator creates an order from a trusted backend, receives a hosted portalUrl, and gives that URL to the customer. The customer completes payment using the displayed gateway number and follows the branded checkout instructions. The integrator then checks the authenticated order status and fulfils the order only after status becomes paid.
The customer-facing portal is separate from the backend API. It displays only customer-safe information and never exposes internal SMS-processing terminology.
Core payment flow
| Step | Action | Result |
|---|---|---|
| 1 | Configure a client gateway number. | bKash, Nagad, or Upay becomes available. |
| 2 | Create an order from your backend. | A pending order and hosted portalUrl are returned. |
| 3 | Redirect or send the customer to portalUrl. |
The customer sees the mobile checkout. |
| 4 | Customer pays to the displayed number. | SMS Reader captures the payment record. |
| 5 | Poll the authenticated order endpoint. | Automatic matching can change the order to paid. |
| 6 | If required, customer submits a Transaction ID. | The fallback matcher checks the order payment record. |
| 7 | Fulfil only after paid. |
Duplicate fulfilment is prevented by your application. |
Quick Start
1. Store your API key securely
Every backend request to the client payment API must include the API key in the X-API-Key header. Keep it in an environment variable and never place it in browser JavaScript, a public mobile bundle, or a customer checkout page.
export AAKASHPAY_API_KEY='ak_live_REPLACE_WITH_YOUR_KEY'
2. Create an order
curl -X POST 'https://akashpay-api.axura.workers.dev/v1/payment/create' \
-H 'Content-Type: application/json' \
-H "X-API-Key: $AAKASHPAY_API_KEY" \
-d '{
"amount": 100,
"userId": "member_42",
"gateway": "bkash"
}'
{
"ok": true,
"orderId": "ord_abc123",
"gateway": "bkash",
"number": "017XXXXXXXX",
"amount": 100,
"status": "pending",
"expiresAt": 1776700900000,
"portalUrl": "https://akashpay-api.axura.workers.dev/pay/ord_abc123"
}
3. Send the checkout URL to the customer
Use the returned portalUrl as the payment link. Do not construct the portal URL yourself when the API has already returned it.
4. Confirm payment from your backend
curl -sS \
-H "X-API-Key: $AAKASHPAY_API_KEY" \
'https://akashpay-api.axura.workers.dev/v1/payment/ord_abc123'
Treat the order as paid only when the returned status is exactly paid.
Authentication
Client API authentication
Use the following header for /v1/gateways, /v1/payment/create, /v1/payment/{orderId}, and /v1/balance/{userId}:
X-API-Key: ak_live_REPLACE_WITH_YOUR_KEY
A missing key returns 401 missing_api_key. An invalid key returns 401 invalid_api_key. A locked key returns 403 client_suspended. If a daily usage limit is configured and reached, the API returns 429 usage_limit_reached.
SMS Reader device authentication
The Android SMS Reader app uses:
X-Device-ID: android-device-123
This header is required for device-scoped settings, logs, and API-key management after registration.
Owner and internal authentication
Owner administration endpoints use X-Admin-Token. Internal matching uses X-System-Key. These credentials must remain on trusted server infrastructure.
API response format
Success responses use ok: true:
{ "ok": true, "data": {} }
Errors use a stable code suitable for application logic:
{
"ok": false,
"error": "Missing X-API-Key header",
"code": "missing_api_key"
}
Create Payment
POST /v1/payment/create
Create a pending payment order and receive its hosted checkout URL.
Authentication: X-API-Key
Success status: 201 Created
Request fields
| Field | Type | Required | Rules |
|---|---|---|---|
amount |
number | Yes | Must be greater than zero. |
userId |
string | Yes | Your customer, member, or invoice reference. |
gateway |
string | Yes | bkash, nagad, or upay. |
Request
{
"amount": 1200,
"userId": "customer_9001",
"gateway": "nagad"
}
Response
{
"ok": true,
"orderId": "ord_msvo0rrw37a069d4fd38",
"gateway": "nagad",
"number": "018XXXXXXXX",
"amount": 1200,
"status": "pending",
"expiresAt": 1776700900000,
"portalUrl": "https://akashpay-api.axura.workers.dev/pay/ord_msvo0rrw37a069d4fd38"
}
Possible errors
| Status | Code | Meaning |
|---|---|---|
400 |
invalid_amount |
Amount is missing, non-numeric, or not positive. |
400 |
invalid_user_id |
userId is missing or not a string. |
400 |
invalid_gateway |
Gateway is not supported. |
403 |
device_blocked |
The client device has been blocked. |
409 |
gateway_not_configured |
The selected gateway has no configured number. |
Check Payment
GET /v1/payment/{orderId}
Return the authenticated client’s order status. The API attempts automatic matching before returning the order.
Authentication: X-API-Key
curl -sS \
-H 'X-API-Key: ak_live_REPLACE_WITH_YOUR_KEY' \
'https://akashpay-api.axura.workers.dev/v1/payment/ord_abc123'
Response fields
| Field | Type | Description |
|---|---|---|
orderId |
string | AakashPay order identifier. |
gateway |
string | Gateway used by the order. |
amount |
number | Required payment amount. |
number |
string | Configured receiving number. |
status |
string | pending, paid, expired, or cancelled. |
trxId |
string/null | Matched Transaction ID, if available. |
createdAt |
number | Creation time in epoch milliseconds. |
expiresAt |
number | Expiration time in epoch milliseconds. |
verifiedAt |
number/null | Verification time, if paid. |
userId |
string | Returned only to authenticated client calls. |
Status values
| Status | Meaning | Recommended action |
|---|---|---|
pending |
Payment is not yet verified. | Continue polling until expiration. |
paid |
Payment has been verified. | Fulfil once and stop polling. |
expired |
The 15-minute order window has ended. | Do not fulfil; create a new order if necessary. |
cancelled |
Order was cancelled operationally. | Do not fulfil. |
Verify Payment
AakashPay has two public endpoints used by the hosted checkout.
Public status
GET /v1/public/payment/{orderId}
This endpoint requires no API key. The orderId itself acts as the bearer token, so treat a checkout URL as sensitive. The response omits the private userId field.
curl -sS \
'https://akashpay-api.axura.workers.dev/v1/public/payment/ord_abc123'
Manual Transaction ID verification
POST /v1/public/payment/{orderId}/verify
Submit a Transaction ID when the customer reaches the manual verification step in the checkout.
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"trxId":"DHG6IB84FK"}' \
'https://akashpay-api.axura.workers.dev/v1/public/payment/ord_abc123/verify'
The endpoint accepts a plain ID or a labelled value such as TrxID: DHG6IB84FK. The ID must contain 6–81 permitted alphanumeric or punctuation characters after normalization.
A successful match returns the public paid-order object. If the ID is valid but is not yet available or does not match the order, the endpoint returns:
{
"ok": false,
"error": "Transaction ID is not available in the AakashPay system yet, or does not match this order",
"code": "transaction_not_verified"
}
The verification decision is based on the payment record scoped to the client’s configured SMS Reader architecture and the order’s payment details. Customer-facing screens do not reveal internal SMS-processing terminology.
Admin manual verification
POST /admin/orders/{orderId}/manual-verify
Authentication: X-Admin-Token.
{ "trxId": "DHG6IB84FK" }
This endpoint is for owner operations and returns whether the submitted ID matched the order.
Recommended polling flow
Poll the authenticated status endpoint every 3–5 seconds. Stop when the status becomes terminal. Do not fulfil on the basis of a browser redirect, a customer screenshot, or a pending response.
async function waitForPaid(orderId, apiKey) {
const base = 'https://akashpay-api.axura.workers.dev';
while (true) {
const response = await fetch(`${base}/v1/payment/${encodeURIComponent(orderId)}`, {
headers: { 'X-API-Key': apiKey }
});
const result = await response.json();
if (!response.ok) {
throw new Error(`${result.code}: ${result.error}`);
}
if (['paid', 'expired', 'cancelled'].includes(result.status)) {
return result;
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
SMS Reader Device API
Register a device
POST /v1/public/devices/register
Register an Android SMS Reader device. Supplying an existing server-issued clientId binds the device to that client. Legacy installs can omit clientId and receive a generated client identifier.
{
"deviceId": "android-device-123",
"deviceName": "Payment Reader Phone",
"clientId": "client_a1b2c3"
}
Example response:
{
"ok": true,
"clientId": "client_a1b2c3",
"created": true,
"apiCreated": true,
"apiKey": "ak_live_NEW_SECRET"
}
Store a newly returned API key immediately. It is not safe to publish it in the WebView frontend.
Get or update gateway numbers
GET /v1/public/settings/{clientId}/numbers
Authentication: X-Device-ID.
{ "ok": true, "numbers": { "bkash": "017XXXXXXXX", "nagad": "018XXXXXXXX" } }
PATCH /v1/public/settings/{clientId}/numbers
{
"numbers": {
"bkash": "017XXXXXXXX",
"nagad": "018XXXXXXXX",
"upay": "013XXXXXXXX"
}
}
Bangladeshi mobile numbers are validated against the 01[3-9]XXXXXXXX format.
Read and clear SMS logs
GET /v1/public/logs/{clientId}
Returns logs scoped to the authenticated device. The response also identifies whether logs came directly from the client scope or from the compatibility nested-log scan.
{ "ok": true, "logs": [], "source": "client" }
DELETE /v1/public/logs/{clientId}
{ "ok": true, "cleared": true }
Manage the linked API key
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/v1/public/settings/{clientId}/apikey |
Read current key metadata. |
POST |
/v1/public/settings/{clientId}/apikey/lock |
Suspend the key. |
POST |
/v1/public/settings/{clientId}/apikey/unlock |
Reactivate the key. |
POST |
/v1/public/settings/{clientId}/apikey/revoke |
Generate a replacement key. |
All four endpoints require X-Device-ID. A revoke operation invalidates the previous key.
Gateways and checkout assets
GET /v1/gateways
Returns only the active gateway numbers configured for the authenticated client.
{
"ok": true,
"gateways": [
{ "id": "bkash", "name": "bKash", "number": "017XXXXXXXX" },
{ "id": "nagad", "name": "Nagad", "number": "018XXXXXXXX" }
]
}
The hosted portal and self-hosted assets are available at:
| Resource | URL |
|---|---|
| Checkout portal | https://akashpay-api.axura.workers.dev/pay/{orderId} |
| bKash instructions | https://akashpay-api.axura.workers.dev/assets/bkash-personal.jpg |
| Nagad instructions | https://akashpay-api.axura.workers.dev/assets/nagad-personal.jpg |
| Nagad logo | https://akashpay-api.axura.workers.dev/assets/nagad-logo.png |
Merchant and Owner APIs
These routes power the merchant dashboard and owner admin panel. They must not be called directly from untrusted customer code.
Merchant authentication
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/auth/request-link |
Request a merchant magic link. |
GET |
/auth/verify |
Verify a magic-link token. |
GET |
/merchant/me |
Read the authenticated merchant profile. |
POST |
/merchant/link-client |
Link a merchant account to a client. |
POST |
/merchant/regenerate-key |
Regenerate a merchant-linked key. |
POST |
/merchant/plan |
Change a merchant plan. |
Owner operations
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/admin/overview |
Read clients, orders, logs, and verification overview. |
PATCH |
/admin/orders/{orderId} |
Edit status, trxId, or note. |
DELETE |
/admin/orders/{orderId} |
Delete an order. |
POST |
/admin/orders/{orderId}/manual-verify |
Match an order using an admin-supplied Transaction ID. |
POST |
/admin/orders/bulk-delete |
Delete up to 200 orders. |
PATCH |
/admin/logs/{clientId}/{logId} |
Edit selected SMS-log fields. |
DELETE |
/admin/logs/{clientId}/{logId} |
Delete one SMS log. |
POST |
/admin/logs/bulk-delete |
Delete up to 200 SMS logs. |
POST |
/admin/devices/{clientId}/status |
Reject or restore a device. |
Client management
| Method | Endpoint | Purpose |
|---|---|---|
GET/POST |
/admin/clients |
List or create clients. |
PATCH/DELETE |
/admin/clients/{clientId} |
Update or delete a client. |
POST |
/admin/clients/link |
Link an app client. |
POST |
/admin/clients/{clientId}/status |
Change client status. |
POST |
/admin/clients/{clientId}/regenerate-key |
Regenerate a client API key. |
GET |
/admin/clients/{clientId}/orders |
Read client orders. |
GET |
/admin/clients/{clientId}/profile |
Read the client profile. |
POST/PATCH/DELETE |
/admin/clients/{clientId}/projects… |
Manage projects. |
POST/PATCH/DELETE |
/admin/clients/{clientId}/teams… |
Manage teams. |
Code Examples
JavaScript / Node.js
const BASE = 'https://akashpay-api.axura.workers.dev';
const API_KEY = process.env.AAKASHPAY_API_KEY;
const response = await fetch(`${BASE}/v1/payment/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
amount: 100,
userId: 'member_42',
gateway: 'bkash'
})
});
const data = await response.json();
if (!response.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data.portalUrl);
PHP
<?php
$base = 'https://akashpay-api.axura.workers.dev';
$apiKey = getenv('AAKASHPAY_API_KEY');
$ch = curl_init($base . '/v1/payment/create');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $apiKey,
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => 100,
'userId' => 'member_42',
'gateway' => 'nagad',
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($body, true);
if ($status < 200 || $status >= 300) {
throw new RuntimeException(($data['code'] ?? 'api_error') . ': ' . ($data['error'] ?? 'Request failed'));
}
echo $data['portalUrl'];
Python
import os
import requests
BASE = "https://akashpay-api.axura.workers.dev"
response = requests.post(
f"{BASE}/v1/payment/create",
headers={
"Content-Type": "application/json",
"X-API-Key": os.environ["AAKASHPAY_API_KEY"],
},
json={"amount": 100, "userId": "member_42", "gateway": "nagad"},
timeout=20,
)
payload = response.json()
response.raise_for_status()
print(payload["portalUrl"])
Webhook Flow
A webhook-style flow is a common pattern in payment documentation, but the current AakashPay Worker does not yet expose an active outbound webhook dispatcher. A webhookUrl field exists in client API settings for dashboard compatibility; setting that field alone does not deliver callbacks.
Until a versioned webhook endpoint is introduced, use this production flow:
| Step | Backend action |
|---|---|
| 1 | Create the order. |
| 2 | Save orderId, amount, gateway, and your own reference. |
| 3 | Send the returned portalUrl to the customer. |
| 4 | Poll GET /v1/payment/{orderId} every 3–5 seconds. |
| 5 | If the customer submits a Transaction ID, allow the hosted portal to call the public verification endpoint. |
| 6 | Fulfil only after authenticated status returns paid. |
| 7 | Stop polling on paid, expired, or cancelled. |
Errors
| HTTP | Code | Meaning |
|---|---|---|
400 |
invalid_amount |
Amount is missing, invalid, or not positive. |
400 |
invalid_user_id |
userId is missing or invalid. |
400 |
invalid_gateway |
Unsupported gateway identifier. |
400 |
invalid_trx_id |
Invalid Transaction ID format. |
401 |
missing_api_key |
X-API-Key is missing. |
401 |
invalid_api_key |
API key is not recognized. |
401 |
unauthorized |
Admin/system credential is invalid. |
403 |
client_suspended |
API key is locked. |
403 |
device_rejected |
Device was rejected by the owner. |
403 |
device_blocked |
Device is blocked from creating orders. |
404 |
order_not_found |
Order does not exist in the client scope. |
409 |
gateway_not_configured |
Gateway number is not configured. |
409 |
transaction_not_verified |
Transaction ID is not available or does not match. |
409 |
device_binding_conflict |
Client is bound to another device. |
429 |
usage_limit_reached |
Daily API limit reached. |
500 |
internal_error |
Unexpected server failure. |
Production Checklist
Before going live, confirm the following items:
| Check | Requirement |
|---|---|
| ✓ | API key is stored in an environment variable or server secret store. |
| ✓ | Payment creation occurs on a trusted backend, not in public frontend code. |
| ✓ | The returned portalUrl is sent to the customer without exposing the API key. |
| ✓ | Your system stores orderId, amount, gateway, and your own customer reference. |
| ✓ | Your fulfilment code accepts payment only when authenticated status is paid. |
| ✓ | Polling stops at terminal statuses and does not create duplicate fulfilment. |
| ✓ | Device registration and SMS log APIs use X-Device-ID securely. |
| ✓ | Admin and system credentials are never shipped in an app or WebView. |
| ✓ | Invalid API responses are handled using the stable code field. |
| ✓ | You do not assume outbound webhooks are active in the current deployment. |
API Limitations and implementation notes
The hosted portal’s public endpoints use the order ID as a bearer token. Anyone who obtains a complete public order URL may view that order’s customer-safe payment fields and submit a Transaction ID for that order. Keep payment links private and avoid posting them in public logs.
The current order lifetime is 15 minutes. The customer-facing manual Transaction ID fallback is part of the hosted checkout flow; client backends should still trust the authenticated order-status endpoint as the final fulfilment authority.
The API key has a daily usage check when a non-zero usage limit is configured. A missing or zero limit is treated as unlimited by the current Worker implementation.
Support and source references
This document is aligned with the deployed Worker route table and implementation rather than with a hypothetical future webhook contract.