Integration docs

Base URL of this deployment's API. All request and response bodies are JSON; every failure response carries { ok, error, code }. Authenticate every app-plane call with the two headers below.

X-App-Id: <your app id>
X-App-Secret: <your app secret>

1 · Send an OTP

POST /v5/otp/send
{ "phone": "+8801XXXXXXXXX" }

201 { "ok": true, "sessionId": "...", "expiresAt": 1794000000 }

Phone must be strict E.164. Each send consumes one OTP credit — 402 insufficient_credits when the balance is zero. Rate limits answer 429 rate_limited (per-app per-number) and 429 resend_cooldown (per-number cooldown).

2 · Verify the code

POST /v5/otp/verify
{ "phone": "+8801XXXXXXXXX", "otp": "123456" }

200 { "ok": true, "verified": true }

Failure codes: 400 otp_expired (no active session / TTL passed), 423 otp_locked (too many attempts — retryAfterSec tells you when), 400 invalid_otp_format / invalid_phone.

3 · Poll status

GET /v5/otp/status?phone=%2B8801XXXXXXXXX

200 { "ok": true, "status": "pending|verified|expired",
      "expiresAt": 1794000000, "attemptsLeft": 5,
      "lockedUntil": null, "verifiedAt": null }

404 not_found when the number never requested a session.

Error codes

StatuscodeMeaning
400invalid_phone / invalid_otp_formatNumber or code failed strict validation.
400otp_expiredNo active session for that number, or the TTL passed.
402insufficient_creditsCredit balance is zero — buy a package in the dashboard.
404not_foundNo such session (status) or resource.
409trx_id_existsThat bKash TrxID is already attached to another pending transaction.
423otp_lockedToo many verify attempts; retry after retryAfterSec.
429rate_limited / resend_cooldownPer-app per-number rate limit, or per-number resend cooldown.

Webhook signature

Deliveries to your app's webhook URL carry the header X-DP-Signature: the hex-encoded HMAC-SHA256 of the raw request body, keyed with your app's webhook secret. Verify it with a timing-safe comparison over the raw bytes before parsing the JSON, and answer 2xx promptly — failed deliveries are retried.

curl examples

Set the base URL once, then the three core calls:

# Your deployment's API base URL
API=https://dprelay-api-hug8.onrender.com

# 1) Send an OTP
curl -sS -X POST "$API/v5/otp/send" \
  -H 'Content-Type: application/json' \
  -H 'X-App-Id: <your app id>' \
  -H 'X-App-Secret: <your app secret>' \
  -d '{"phone":"+8801XXXXXXXXX"}'

# 2) Verify the code
curl -sS -X POST "$API/v5/otp/verify" \
  -H 'Content-Type: application/json' \
  -H 'X-App-Id: <your app id>' \
  -H 'X-App-Secret: <your app secret>' \
  -d '{"phone":"+8801XXXXXXXXX","otp":"123456"}'

# 3) Poll status
curl -sS -G "$API/v5/otp/status" \
  --data-urlencode 'phone=+8801XXXXXXXXX' \
  -H 'X-App-Id: <your app id>' \
  -H 'X-App-Secret: <your app secret>'

Credits

Balance: GET /v5/billing/credits → both buckets plus their expiry. Buying: GET /v5/billing/packages → POST /v5/billing/credits/request { "packageCode" } returns the bKash destination + amount → Send Money → POST /v5/billing/credits/submit-trx { "transactionId", "trxId" } → the operator approves and the balance updates. Track it all in GET /v5/billing/transactions. TrxID reuse of a different pending transaction answers 409 trx_id_exists. See the payment guides.