Skip to content

Payments API

All payment endpoints require an X-API-Key header containing the merchant API key issued by the admin.

Amounts are strings

Amounts are always quoted strings with up to 6 decimals (USDT precision). Never send or parse them as JSON numbers — precision will be lost.

Create a payment order

http
POST /api/v1/payments
X-API-Key: your-api-key
Content-Type: application/json

Request body:

json
{
  "external_id": "order_123",
  "amount": "99.00",
  "currency": "USDT",
  "callback_url": "https://your-backend.com/webhooks/paywarden",
  "ttl": 3600
}
FieldTypeRequiredDescription
external_idstringyesYour internal order id, 1–255 chars. Unique per merchant
amountstringyesPositive decimal string with up to 6 fractional digits, e.g. "99.00"
currencystringnoMust be "USDT" if provided. Defaults to "USDT"
callback_urlstringyeshttps/http URL that will receive webhooks. Rejected at create time if it resolves to private, loopback, or reserved network addresses
ttlintegernoOrder lifetime in seconds. Defaults to the server-configured ORDER_TTL

Response 201 Created (or 200 OK when returning an existing order for an idempotent replay):

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "external_id": "order_123",
  "address": "TXxx...unique-payment-address",
  "amount": "99.000000",
  "currency": "USDT",
  "network": "nile",
  "status": "pending",
  "expires_at": "2026-04-08T01:00:00.000Z",
  "created_at": "2026-04-08T00:00:00.000Z",
  "reused": false
}

reused: true indicates the server matched an existing order by (merchant_id, external_id) and returned it unchanged. Replays with different amount, currency, or callback_url for the same external_id are rejected with 409 EXTERNAL_ID_CONFLICT.

Errors:

  • 400 INVALID_AMOUNT — amount is not a positive decimal string (≤ 6 fractional digits)
  • 400 INVALID_BODY — other validation failure (bad external_id, unsafe callback_url, etc.)
  • 409 EXTERNAL_ID_CONFLICTexternal_id already exists with different parameters
  • 503 NO_ADDRESSES — address pool is temporarily exhausted; retry shortly

Get order details

http
GET /api/v1/payments/:id
X-API-Key: your-api-key

Merchant-scoped: returns 404 if the order belongs to a different merchant.

Response 200:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "external_id": "order_123",
  "address": "TXxx...",
  "derivation_idx": 1420,
  "amount_expected": "99.000000",
  "amount_received": "99.000000",
  "currency": "USDT",
  "network": "nile",
  "status": "confirmed",
  "callback_url": "https://your-backend.com/webhooks/paywarden",
  "tx_hash": "def456...",
  "expires_at": null,
  "created_at": "2026-04-08T00:00:00.000Z",
  "updated_at": "2026-04-08T00:05:00.000Z",
  "events": [
    {
      "event_type": "order_created",
      "payload": { "amount": "99.000000", "externalId": "order_123" },
      "created_at": "2026-04-08T00:00:00.000Z"
    },
    {
      "event_type": "payment_detected",
      "payload": { "tx_hash": "def456...", "amount": "99.000000", "from_address": "TPayer..." },
      "created_at": "2026-04-08T00:03:00.000Z"
    },
    {
      "event_type": "payment_confirmed",
      "payload": { "tx_hash": "def456...", "amount_received": "99.000000", "amount_expected": "99.000000", "status": "confirmed", "confirmations": 19 },
      "created_at": "2026-04-08T00:05:00.000Z"
    }
  ]
}

expires_at is only meaningful while the order is pending; once the order transitions out of pending it is returned as null (the deadline no longer applies).

Error 404: { "error": "Order not found", "code": "NOT_FOUND" }

Order status values

StatusDescription
pendingAwaiting payment. Expires at expires_at if no transfer is seen
detectedTransfer observed on-chain, awaiting confirmations
confirmedConfirmations reached, amount matches expected — webhook queued
underpaidConfirmations reached, received less than expected — webhook queued
overpaidConfirmations reached, received more than expected — webhook queued
callback_sentWebhook delivery in progress
callback_failedWebhook delivery permanently failed after max retries
completedWebhook acknowledged with 2xx by your backend
expiredOrder expired in pending with no payment. Terminal
cancelledOrder cancelled. Terminal

List orders

http
GET /api/v1/payments?status=confirmed&limit=20&offset=0
X-API-Key: your-api-key
Query paramDefaultDescription
limit20Items per page
offset0Items to skip
statusFilter by order status

Response 200:

json
{
  "data": [ /* array of order rows */ ],
  "total": 142,
  "limit": 20,
  "offset": 0
}

Released under the BSL 1.1 License.