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
POST /api/v1/payments
X-API-Key: your-api-key
Content-Type: application/jsonRequest body:
{
"external_id": "order_123",
"amount": "99.00",
"currency": "USDT",
"callback_url": "https://your-backend.com/webhooks/paywarden",
"ttl": 3600
}| Field | Type | Required | Description |
|---|---|---|---|
external_id | string | yes | Your internal order id, 1–255 chars. Unique per merchant |
amount | string | yes | Positive decimal string with up to 6 fractional digits, e.g. "99.00" |
currency | string | no | Must be "USDT" if provided. Defaults to "USDT" |
callback_url | string | yes | https/http URL that will receive webhooks. Rejected at create time if it resolves to private, loopback, or reserved network addresses |
ttl | integer | no | Order 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):
{
"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 (badexternal_id, unsafecallback_url, etc.)409 EXTERNAL_ID_CONFLICT—external_idalready exists with different parameters503 NO_ADDRESSES— address pool is temporarily exhausted; retry shortly
Get order details
GET /api/v1/payments/:id
X-API-Key: your-api-keyMerchant-scoped: returns 404 if the order belongs to a different merchant.
Response 200:
{
"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
| Status | Description |
|---|---|
pending | Awaiting payment. Expires at expires_at if no transfer is seen |
detected | Transfer observed on-chain, awaiting confirmations |
confirmed | Confirmations reached, amount matches expected — webhook queued |
underpaid | Confirmations reached, received less than expected — webhook queued |
overpaid | Confirmations reached, received more than expected — webhook queued |
callback_sent | Webhook delivery in progress |
callback_failed | Webhook delivery permanently failed after max retries |
completed | Webhook acknowledged with 2xx by your backend |
expired | Order expired in pending with no payment. Terminal |
cancelled | Order cancelled. Terminal |
List orders
GET /api/v1/payments?status=confirmed&limit=20&offset=0
X-API-Key: your-api-key| Query param | Default | Description |
|---|---|---|
limit | 20 | Items per page |
offset | 0 | Items to skip |
status | — | Filter by order status |
Response 200:
{
"data": [ /* array of order rows */ ],
"total": 142,
"limit": 20,
"offset": 0
}