Base URL: https://jatrapay.agi.bd
Go to Dashboard → Brands → open your Brand and copy the Brand API Key (bk_…). Send it in the header of every request:
x-brand-key: bk_xxxxxxxxxxxxxxxx
⚠️ Keep this key on your server only — never in browser code or a mobile app.
Creates a new payment link. Send your customer to pay_url.
curl -X POST https://jatrapay.agi.bd/api/v1/payments \
-H "x-brand-key: bk_xxx" -H "Content-Type: application/json" \
-d '{
"amount": 500,
"order_id": "ORD-1001",
"invoice_name": "Premium Plan",
"gateway": "bkash",
"callback_url": "https://yoursite.com/webhook/jatrapay"
}'
Response:
{ "success": true,
"token": "9f3c…",
"pay_url": "https://jatrapay.agi.bd/pay/9f3c…",
"expires_at": 1767225600 }
amount is required (1–1,000,000). If callback_url is omitted, the Brand's default callback is used. Links are valid for 24 hours by default.
Returns the payment status (pending, paid, expired).
{ "success": true, "token": "9f3c…", "status": "paid", "amount": 500,
"order_id": "ORD-1001", "gateway": "bkash", "trx_id": "9K3X1ABCDE", "paid_at": 1767139200 }
GET/api/v1/payments?status=paid&page=1&limit=20 — lists all of your payments.
When a payment is verified, a POST is sent to your callback_url (up to 3 attempts on failure). You must verify the signature:
POST https://yoursite.com/webhook/jatrapay
x-jatrapay-signature: <HMAC-SHA256 hex of raw body, key = Brand API Key>
{ "event": "payment.paid", "status": "paid", "token": "9f3c…",
"order_id": "ORD-1001", "amount": 500, "trx_id": "9K3X1ABCDE",
"gateway": "bkash", "paid_at": 1767139200 }
Node.js:
const crypto = require('crypto');
app.post('/webhook/jatrapay', express.raw({ type: '*/*' }), (req, res) => {
const sig = crypto.createHmac('sha256', process.env.JATRAPAY_KEY).update(req.body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(req.headers['x-jatrapay-signature'] || '')))
return res.sendStatus(401);
const data = JSON.parse(req.body); // mark order data.order_id as paid
res.sendStatus(200);
});
PHP:
<?php
$raw = file_get_contents('php://input');
$sig = hash_hmac('sha256', $raw, getenv('JATRAPAY_KEY'));
if (!hash_equals($sig, $_SERVER['HTTP_X_JATRAPAY_SIGNATURE'] ?? '')) { http_response_code(401); exit; }
$data = json_decode($raw, true); // mark $data['order_id'] as paid
For safety, callbacks are only sent to public (https) hosts. For final confirmation you can re-check the status with GET /api/v1/payments/:token.
Use this on your own checkout: when a customer submits a TRX ID and amount, it is matched against the received SMS and the amount is credited to the Brand wallet. Enable Verify API on the Brand page to get a bapi_… key.
curl -X POST https://jatrapay.agi.bd/api/v1/verify \
-H "x-verify-key: bapi_xxx" -H "Content-Type: application/json" \
-d '{ "trx_id": "9K3X1ABCDE", "amount": 500, "gateway": "bkash" }'
{ "success": true, "trx_id": "9K3X1ABCDE", "amount": 500, "gateway": "bkash", "balance": 1500 }
The legacy POST /api/brand/verify (header x-brand-api-key) still works.
Header: x-jatrapay-key: mp_… (from Dashboard → Settings)
GET /api/ping → { "status": "ok" } (heartbeat, ?device_name=…)
POST /api/verify { gmail, api_key } (app login check)
POST /api/sms/receive { transaction_id, amount, gateway, sender, raw_sms, timestamp, device_name }
POST /api/log { log, device_name }Every error has this shape:
{ "success": false, "error": "message", "code": "used" }
code: not_found SMS not received yet · used TRX already used · amount_mismatch · expired · invalid_trx. HTTP 401 = invalid key, 429 = too many requests.