Skip to main content
ShadhinPay Docs

API conventions

Base URLs, versioning, the response envelope, the Money type, resource IDs, timestamps, and pagination.

These conventions hold across every endpoint. Read this once and the rest of the API guides will make sense.

Base URLs

EnvironmentBase URL
Livehttps://api.shadhinpay.pay/api/v1
Sandboxhttps://sandbox.shadhinpay.pay/api/v1

The API is versioned in the path (/api/v1). Within a major version the contract is additive only — new optional fields and new endpoints may appear, but existing fields and operations won't be renamed or removed. Build your clients to ignore unknown fields and default-handle unknown enum values so additive changes never break you.

The response envelope

Every JSON response is wrapped in a consistent envelope.

A successful response:

{
  "status": "success",
  "message": "Payment initiated successfully",
  "data": {
    "paymentId": "PAY_20260517_K8X3M2"
  }
}

An error response (see Errors for the full list):

{
  "status": "error",
  "errorType": "VALIDATION_ERROR",
  "message": "Invalid payment request",
  "data": {
    "amount": "Amount must be between 10.00 and 200000.00"
  }
}

The meaningful payload is always under data. On validation errors, data is a map of field name to message.

Money

Monetary values are objects — or, on request bodies, decimal strings — never JSON numbers:

{ "amount": "500.00", "currency": "BDT" }
  • amount is a string so decimal places round-trip exactly ("500.00" is distinct from "500") and there's no floating-point rounding error. Parse it with a decimal type in your language, never a float.
  • currency is always "BDT" in v1.0.
  • Payment amounts must be between "10" and "200000" BDT.

Timestamps

All timestamps are ISO-8601 strings in UTC, e.g. 2026-05-17T10:00:00Z.

Resource IDs

IDs that appear in URLs and webhooks are opaque, non-sequential strings — never guessable integers. Examples:

ResourceFormatExample
PaymentPAY_<date>_<base36>PAY_20260517_K8X3M2
RefundREF_<date>_<base36>REF_20260517_M4N1J7
Invoice numberINV-<businessId>-<seq>INV-HB_71c4-00042
Business (external)HB_<hex>HB_71c4a09e
Client / merchant (external)HM_<hex>HM_4f8c2b1a

Pagination

List endpoints return a paged envelope:

{
  "status": "success",
  "data": {
    "content": [ /* items */ ],
    "page": 0,
    "size": 20,
    "totalElements": 150,
    "totalPages": 8,
    "isFirst": true,
    "isLast": false,
    "hasNext": true,
    "hasPrevious": false
  }
}

Control it with query parameters:

ParameterDefaultNotes
page0Zero-indexed
size20Maximum 100

Rate limits

Requests are rate-limited per business, per route. Typical steady-state limits:

EndpointLimit
POST /payments100 / min
POST .../refunds30 / min
GET /payments/**1000 / min
POST /invoices60 / min
Everything else600 / min

Exceeding a limit returns 429 with errorType: RATE_LIMIT_EXCEEDED and a Retry-After header. Back off and retry after the indicated delay.

Next steps

On this page