API Reference
Every endpoint in the v1 API, generated from the OpenAPI 3.1.0 document.
Payment Intents
get /v1/payment_intents
List payment intents, newest first
Cursor pagination (§7.3): read has_more, then pass the last object’s id as starting_after. Use this to reconcile, never as a substitute for webhooks — polling every intent is how you meet the rate limiter.
{
"object": "list",
"url": "/v1/payment_intents",
"has_more": false,
"data": [
{
"id": "pi_7Fk2Qd9RmTs4Vb",
"object": "payment_intent",
"mode": "test",
"status": "confirmed",
"merchant_id": "mch_3Xn8Kp2Wq5Zt",
"amount": "10000",
"currency": "USD",
"currency_decimals": 2,
"received_amount": "100043100",
"metadata": {
"order_id": "1234"
},
"created": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"checkout_url": "https://checkout.payaider.com/c/pi_7Fk2Qd9RmTs4Vb",
"payment_method": {
"object": "payment_method",
"quote_id": "qt_9Tw3Mn8Rp2Yk",
"token_id": "usdt-tron",
"network_id": "tron",
"asset_symbol": "USDT",
"token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"token_decimals": 6,
"wallet_id": "wal_5Rm2Ht9Jc4Nq",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"amount_due": "100043100",
"rate": "1.000431",
"rate_sources": [
{
"source": "primary_cex",
"rate": "1.000390"
},
{
"source": "usd_composite_x_fx",
"rate": "1.000472"
}
],
"locked_at": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"pay_uri": "tron:TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB?amount=100.0431&token=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
}
},
{
"id": "pi_7Fk2Qd9RmTs4Vb",
"object": "payment_intent",
"mode": "test",
"status": "awaiting_payment",
"merchant_id": "mch_3Xn8Kp2Wq5Zt",
"amount": "10000",
"currency": "USD",
"currency_decimals": 2,
"received_amount": "0",
"metadata": {
"order_id": "1234"
},
"created": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"checkout_url": "https://checkout.payaider.com/c/pi_7Fk2Qd9RmTs4Vb",
"payment_method": {
"object": "payment_method",
"quote_id": "qt_9Tw3Mn8Rp2Yk",
"token_id": "usdt-tron",
"network_id": "tron",
"asset_symbol": "USDT",
"token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"token_decimals": 6,
"wallet_id": "wal_5Rm2Ht9Jc4Nq",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"amount_due": "100043100",
"rate": "1.000431",
"rate_sources": [
{
"source": "primary_cex",
"rate": "1.000390"
},
{
"source": "usd_composite_x_fx",
"rate": "1.000472"
}
],
"locked_at": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"pay_uri": "tron:TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB?amount=100.0431&token=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
}
}
]
}post /v1/payment_intents
Create a payment intent
Step 1 of the 15-minute integration (§7.4). Price the order in fiat; settlement is crypto, which is exactly why going global is cheap here.
The intent starts in created with no asset chosen and no address to pay. Redirect the customer to checkout_url and let them pick asset and network there — that is what locks a 15-minute quote and reserves the unique amount.
Send an Idempotency-Key: a retried create without one can mint two intents for one order, and a customer who pays both cannot be refunded by anyone, including us.
{
"amount": 10000,
"currency": "USD",
"metadata": {
"order_id": "1234"
},
"success_url": "https://shop.example/thanks"
}{
"id": "pi_7Fk2Qd9RmTs4Vb",
"object": "payment_intent",
"mode": "test",
"status": "created",
"merchant_id": "mch_3Xn8Kp2Wq5Zt",
"amount": "10000",
"currency": "USD",
"currency_decimals": 2,
"received_amount": "0",
"metadata": {
"order_id": "1234"
},
"created": "2026-08-27T10:15:00.000Z",
"expires_at": null,
"checkout_url": "https://checkout.payaider.com/c/pi_7Fk2Qd9RmTs4Vb",
"payment_method": null
}get /v1/payment_intents/{id}
Retrieve a payment intent
The authoritative status. This is also the recovery path when a webhook is missed: webhook delivery is at-least-once and unordered, so re-polling here is how a consumer resynchronizes (§8).
{
"id": "pi_7Fk2Qd9RmTs4Vb",
"object": "payment_intent",
"mode": "test",
"status": "awaiting_payment",
"merchant_id": "mch_3Xn8Kp2Wq5Zt",
"amount": "10000",
"currency": "USD",
"currency_decimals": 2,
"received_amount": "0",
"metadata": {
"order_id": "1234"
},
"created": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"checkout_url": "https://checkout.payaider.com/c/pi_7Fk2Qd9RmTs4Vb",
"payment_method": {
"object": "payment_method",
"quote_id": "qt_9Tw3Mn8Rp2Yk",
"token_id": "usdt-tron",
"network_id": "tron",
"asset_symbol": "USDT",
"token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"token_decimals": 6,
"wallet_id": "wal_5Rm2Ht9Jc4Nq",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"amount_due": "100043100",
"rate": "1.000431",
"rate_sources": [
{
"source": "primary_cex",
"rate": "1.000390"
},
{
"source": "usd_composite_x_fx",
"rate": "1.000472"
}
],
"locked_at": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"pay_uri": "tron:TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB?amount=100.0431&token=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
}
}post /v1/payment_intents/{id}/select_method
Choose asset + wallet: locks a 15-minute quote and reserves a unique amount
Moves the intent created → awaiting_payment (§3.2). Three things happen atomically:
- An FX quote is locked for 15 minutes from at least two independent sources, with the divergence alarm between the legs (§4.2–§4.3).
- A globally unique amount for this (wallet, token) is reserved. Because funds go straight to the merchant’s shared address, the amount is the only on-chain discriminator there is (§5.1); uniqueness is enforced by a database constraint, so concurrent quotes across API instances cannot collide by construction (§5.2).
payment_methodis filled in with the address, exact amount, contract and pay URI.
Hosted checkout calls this for you. Call it directly only if you are building your own checkout — and then show the customer the network name, the token contract, the exact amount and the countdown (§6.3).
{
"token_id": "usdt-tron",
"wallet_id": "wal_5Rm2Ht9Jc4Nq"
}{
"id": "pi_7Fk2Qd9RmTs4Vb",
"object": "payment_intent",
"mode": "test",
"status": "awaiting_payment",
"merchant_id": "mch_3Xn8Kp2Wq5Zt",
"amount": "10000",
"currency": "USD",
"currency_decimals": 2,
"received_amount": "0",
"metadata": {
"order_id": "1234"
},
"created": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"checkout_url": "https://checkout.payaider.com/c/pi_7Fk2Qd9RmTs4Vb",
"payment_method": {
"object": "payment_method",
"quote_id": "qt_9Tw3Mn8Rp2Yk",
"token_id": "usdt-tron",
"network_id": "tron",
"asset_symbol": "USDT",
"token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"token_decimals": 6,
"wallet_id": "wal_5Rm2Ht9Jc4Nq",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"amount_due": "100043100",
"rate": "1.000431",
"rate_sources": [
{
"source": "primary_cex",
"rate": "1.000390"
},
{
"source": "usd_composite_x_fx",
"rate": "1.000472"
}
],
"locked_at": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"pay_uri": "tron:TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB?amount=100.0431&token=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
}
}post /v1/payment_intents/{id}/cancel
Cancel a payment intent that has not yet been paid
Allowed from created and awaiting_payment only. There is no edge from detected onwards to canceled (§3.2) — money already on its way must never be cancelled out from under the customer, so that attempt returns 409 invalid_state_transition.
{
"reason": "customer abandoned checkout"
}{
"id": "pi_7Fk2Qd9RmTs4Vb",
"object": "payment_intent",
"mode": "test",
"status": "canceled",
"merchant_id": "mch_3Xn8Kp2Wq5Zt",
"amount": "10000",
"currency": "USD",
"currency_decimals": 2,
"received_amount": "0",
"metadata": {
"order_id": "1234"
},
"created": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"checkout_url": "https://checkout.payaider.com/c/pi_7Fk2Qd9RmTs4Vb",
"payment_method": {
"object": "payment_method",
"quote_id": "qt_9Tw3Mn8Rp2Yk",
"token_id": "usdt-tron",
"network_id": "tron",
"asset_symbol": "USDT",
"token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"token_decimals": 6,
"wallet_id": "wal_5Rm2Ht9Jc4Nq",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"amount_due": "100043100",
"rate": "1.000431",
"rate_sources": [
{
"source": "primary_cex",
"rate": "1.000390"
},
{
"source": "usd_composite_x_fx",
"rate": "1.000472"
}
],
"locked_at": "2026-08-27T10:15:00.000Z",
"expires_at": "2026-08-27T10:30:00.000Z",
"pay_uri": "tron:TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB?amount=100.0431&token=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
}
}Payment Links
get /v1/payment_links
List payment links, newest first
Cursor-paginated (§7.3).
{
"object": "list",
"url": "/v1/payment_links",
"has_more": false,
"data": [
{
"id": "plink_5Zt7Rm2Kq9Xd",
"object": "payment_link",
"mode": "test",
"active": true,
"url": "https://pay.payaider.com/pay/5Zt7Rm2Kq9Xd",
"amount": "2500",
"currency": "EUR",
"currency_decimals": 2,
"min_amount": null,
"max_amount": null,
"description": "Studio session deposit",
"metadata": {
"sku": "studio-1h"
},
"created": "2026-08-25T11:00:00.000Z"
}
]
}post /v1/payment_links
Create a reusable or fixed-amount payment link
A shareable URL that mints a fresh payment intent per visit — so two customers paying the same link receive two different unique amounts and can never be confused for one another (§5.1). Omit amount for an open-amount link.
{
"amount": 2500,
"currency": "EUR",
"description": "Studio session deposit",
"metadata": {
"sku": "studio-1h"
}
}{
"id": "plink_5Zt7Rm2Kq9Xd",
"object": "payment_link",
"mode": "test",
"active": true,
"url": "https://pay.payaider.com/pay/5Zt7Rm2Kq9Xd",
"amount": "2500",
"currency": "EUR",
"currency_decimals": 2,
"min_amount": null,
"max_amount": null,
"description": "Studio session deposit",
"metadata": {
"sku": "studio-1h"
},
"created": "2026-08-25T11:00:00.000Z"
}get /v1/payment_links/{id}
Retrieve a payment link
Returns the link and whether it is still active.
{
"id": "plink_5Zt7Rm2Kq9Xd",
"object": "payment_link",
"mode": "test",
"active": true,
"url": "https://pay.payaider.com/pay/5Zt7Rm2Kq9Xd",
"amount": "2500",
"currency": "EUR",
"currency_decimals": 2,
"min_amount": null,
"max_amount": null,
"description": "Studio session deposit",
"metadata": {
"sku": "studio-1h"
},
"created": "2026-08-25T11:00:00.000Z"
}Invoices
get /v1/invoices
List invoices, newest first
Cursor-paginated (§7.3).
{
"object": "list",
"url": "/v1/invoices",
"has_more": false,
"data": [
{
"id": "inv_3Bn6Ls9Xd4Kq",
"object": "invoice",
"mode": "test",
"status": "open",
"number": "SP-2026-000117",
"customer": {
"name": "Adaeze Okafor",
"email": "adaeze@example.com",
"country": "NG"
},
"amount": "4500000",
"currency": "NGN",
"currency_decimals": 2,
"due_date": "2026-09-03T23:59:59.000Z",
"hosted_invoice_url": "https://pay.payaider.com/invoice/3Bn6Ls9Xd4Kq",
"payment_intent": "pi_7Fk2Qd9RmTs4Vb",
"metadata": {
"po_number": "PO-88214"
},
"created": "2026-08-27T08:30:00.000Z"
}
]
}post /v1/invoices
Create an invoice with customer details and an expiry
An invoice is a payment link with a customer, a number and a due date, rendered as a payable page with QR and countdown (§7.2, Stage 8). The customer’s country gates the page against prohibited jurisdictions (§12).
{
"amount": 4500000,
"currency": "NGN",
"customer": {
"name": "Adaeze Okafor",
"email": "adaeze@example.com",
"country": "NG"
},
"due_date": "2026-09-03T23:59:59.000Z",
"metadata": {
"po_number": "PO-88214"
}
}{
"id": "inv_3Bn6Ls9Xd4Kq",
"object": "invoice",
"mode": "test",
"status": "open",
"number": "SP-2026-000117",
"customer": {
"name": "Adaeze Okafor",
"email": "adaeze@example.com",
"country": "NG"
},
"amount": "4500000",
"currency": "NGN",
"currency_decimals": 2,
"due_date": "2026-09-03T23:59:59.000Z",
"hosted_invoice_url": "https://pay.payaider.com/invoice/3Bn6Ls9Xd4Kq",
"payment_intent": "pi_7Fk2Qd9RmTs4Vb",
"metadata": {
"po_number": "PO-88214"
},
"created": "2026-08-27T08:30:00.000Z"
}get /v1/invoices/{id}
Retrieve an invoice
Includes the payment intent minted when the customer opened the invoice, if any.
{
"id": "inv_3Bn6Ls9Xd4Kq",
"object": "invoice",
"mode": "test",
"status": "open",
"number": "SP-2026-000117",
"customer": {
"name": "Adaeze Okafor",
"email": "adaeze@example.com",
"country": "NG"
},
"amount": "4500000",
"currency": "NGN",
"currency_decimals": 2,
"due_date": "2026-09-03T23:59:59.000Z",
"hosted_invoice_url": "https://pay.payaider.com/invoice/3Bn6Ls9Xd4Kq",
"payment_intent": "pi_7Fk2Qd9RmTs4Vb",
"metadata": {
"po_number": "PO-88214"
},
"created": "2026-08-27T08:30:00.000Z"
}Webhooks
get /v1/webhook_endpoints
List webhook endpoints
The signing secret is never included here — it is revealed once, at creation.
{
"object": "list",
"url": "/v1/webhook_endpoints",
"has_more": false,
"data": [
{
"id": "we_6Bd4Lq2Xs7Hf",
"object": "webhook_endpoint",
"mode": "test",
"url": "https://shop.example/webhooks/payaider",
"description": "Order fulfilment",
"enabled_events": [
"*"
],
"api_version": "2026-08-27",
"status": "enabled",
"created": "2026-08-27T09:00:00.000Z"
}
]
}post /v1/webhook_endpoints
Register a webhook endpoint (returns the signing secret once)
Step 3 of the 15-minute integration (§7.4): confirm server-side via webhook, never via success_url alone.
The response carries secret — the whsec_… HMAC key — exactly once. Store it in your secret manager before you close the response: a forged payment.confirmed ships real goods against crypto nobody can claw back, and this signature is your only defence (§8).
"Exactly once" includes idempotent replays. If you send an Idempotency-Key and retry this call, the replay returns the same endpoint object with secret ABSENT: the secret is never written to the 24h replay buffer, which has none of the encryption at rest it is kept under everywhere else (§7.3, §12). A retry that answers Idempotent-Replayed: true is telling you the first call already created the endpoint — if you lost that response, roll the secret rather than registering a second endpoint.
The URL is checked at registration (scheme, embedded credentials, literal private addresses, reserved names) and re-resolved by DNS on every delivery attempt.
{
"url": "https://shop.example/webhooks/payaider",
"description": "Order fulfilment",
"enabled_events": [
"payment.confirmed",
"payment.underpaid",
"payment.overpaid"
]
}{
"id": "we_6Bd4Lq2Xs7Hf",
"object": "webhook_endpoint",
"mode": "test",
"url": "https://shop.example/webhooks/payaider",
"description": "Order fulfilment",
"enabled_events": [
"payment.confirmed",
"payment.underpaid",
"payment.overpaid"
],
"api_version": "2026-08-27",
"status": "enabled",
"created": "2026-08-27T09:00:00.000Z",
"secret": "whsec_EXAMPLE_NOT_A_REAL_SECRET_00000000"
}get /v1/webhook_endpoints/{id}/deliveries
List delivery attempts for a webhook endpoint, newest first
Every attempt, with status code, latency and a truncated copy of your response (§8).
Retries follow 1m, 5m, 30m, 2h, 6h, 12h, 24h — eight attempts over about three days. Only a 2xx counts as delivered: a redirect is a failure, as are 4xx, 5xx, TLS errors and timeouts. After sustained failure the endpoint is disabled and you are emailed, and the events stay queryable.
{
"object": "list",
"url": "/v1/webhook_endpoints/we_6Bd4Lq2Xs7Hf/deliveries",
"has_more": false,
"data": [
{
"id": "whd_4Kq7Bn2Ls9Xd",
"object": "webhook_delivery_attempt",
"delivery_id": "whdl_8Vp3Hf5Jc1Wg",
"endpoint_id": "we_6Bd4Lq2Xs7Hf",
"event_id": "evt_2Ay0Zt7Rm9Kq",
"event_type": "payment.confirmed",
"attempt": 2,
"url": "https://shop.example/webhooks/payaider",
"status_code": 200,
"outcome": "delivered",
"latency_ms": 143,
"response_body": "ok",
"error": null,
"requested_at": "2026-08-27T10:36:00.000Z",
"next_retry_at": null
},
{
"id": "whd_1Ls9Xd4Kq7Bn",
"object": "webhook_delivery_attempt",
"delivery_id": "whdl_8Vp3Hf5Jc1Wg",
"endpoint_id": "we_6Bd4Lq2Xs7Hf",
"event_id": "evt_2Ay0Zt7Rm9Kq",
"event_type": "payment.confirmed",
"attempt": 1,
"url": "https://shop.example/webhooks/payaider",
"status_code": 502,
"outcome": "failed",
"latency_ms": 10000,
"response_body": "",
"error": "upstream timeout after 10000ms",
"requested_at": "2026-08-27T10:35:00.000Z",
"next_retry_at": "2026-08-27T10:36:00.000Z"
}
]
}post /v1/webhook_endpoints/{id}/deliveries/{delivery_id}/redeliver
Redeliver one event to one endpoint
Queues an immediate extra attempt. It never cancels the automatic retry schedule (§8), so a redelivery and a scheduled retry can both arrive — which is fine, because your consumer dedupes on event.id.
{
"id": "whd_7Bn2Ls9Xd4Kq",
"object": "webhook_delivery_attempt",
"delivery_id": "whdl_8Vp3Hf5Jc1Wg",
"endpoint_id": "we_6Bd4Lq2Xs7Hf",
"event_id": "evt_2Ay0Zt7Rm9Kq",
"event_type": "payment.confirmed",
"attempt": 3,
"url": "https://shop.example/webhooks/payaider",
"status_code": null,
"outcome": "failed",
"latency_ms": 0,
"response_body": "",
"error": null,
"requested_at": "2026-08-27T11:02:00.000Z",
"next_retry_at": null
}Wallets
get /v1/wallets
List your receiving wallets
Your own addresses. Payaider holds no key for any of them and cannot move a single token out (§0, §12) — which is the product, and also why verification is strict.
{
"object": "list",
"url": "/v1/wallets",
"has_more": false,
"data": [
{
"id": "wal_5Rm2Ht9Jc4Nq",
"object": "wallet",
"mode": "test",
"network_id": "tron",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"label": "Treasury (TRON)",
"status": "verified",
"verification_method": "signed_message",
"verified_at": "2026-08-20T14:02:00.000Z",
"is_exchange_hosted": false,
"created": "2026-08-20T13:58:00.000Z"
},
{
"id": "wal_8Hf5Jc1Wg0Ay",
"object": "wallet",
"mode": "test",
"network_id": "base",
"address": "0x9f2c4a1d7b83e6045c1a2b9d8e7f6a5b4c3d2e1f",
"label": "Ops (Base)",
"status": "verified",
"verification_method": "signed_message",
"verified_at": "2026-08-21T09:41:00.000Z",
"is_exchange_hosted": false,
"created": "2026-08-21T09:37:00.000Z"
}
]
}post /v1/wallets
Add a receiving wallet (starts unverified)
The wallet lands in pending and receives no quotes until it is verified (§6.2).
Exchange deposit addresses are rejected (decision D10). You cannot sign for one, and decisively: a customer paying from the *same* exchange settles as an off-chain internal ledger transfer with no on-chain transaction at all — invisible to every watcher. You would be credited on the exchange while the invoice expired unpaid.
{
"network_id": "tron",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"label": "Treasury (TRON)",
"controls_private_key": true
}{
"id": "wal_5Rm2Ht9Jc4Nq",
"object": "wallet",
"mode": "test",
"network_id": "tron",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"label": "Treasury (TRON)",
"status": "pending",
"verification_method": null,
"verified_at": null,
"is_exchange_hosted": false,
"created": "2026-08-20T13:58:00.000Z"
}post /v1/wallets/{id}/verify
Prove control of a wallet address
Two accepted proofs (§6.2):
signed_message— sign the challenge issued for this wallet with the address (WalletConnect or a manual signature; TIP-191-style on TRON).micro_deposit— send an exact randomized micro-amount from the claimed address to the Payaider verification address within its TTL; the match is on the sender.
This is a high-risk action: MFA re-prompt, audit event and an email notification (§12).
{
"method": "signed_message",
"signature": "0x7f3c2a1b9d8e6f504132a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d7e8f901"
}{
"id": "wal_5Rm2Ht9Jc4Nq",
"object": "wallet",
"mode": "test",
"network_id": "tron",
"address": "TAF2p47aRXHmN2WwBumwgR2yf2j27HeqbB",
"label": "Treasury (TRON)",
"status": "verified",
"verification_method": "signed_message",
"verified_at": "2026-08-20T14:02:00.000Z",
"is_exchange_hosted": false,
"created": "2026-08-20T13:58:00.000Z"
}Routes
get /v1/routes
List (asset, network) → wallet routes
At most one active route per (asset, network), enforced by a database constraint (§6.1).
{
"object": "list",
"url": "/v1/routes",
"has_more": false,
"data": [
{
"id": "rt_2Qd9RmTs4Vb7",
"object": "route",
"asset_symbol": "USDT",
"network_id": "tron",
"token_id": "usdt-tron",
"wallet_id": "wal_5Rm2Ht9Jc4Nq",
"active": true,
"created": "2026-08-20T14:05:00.000Z"
}
]
}put /v1/routes
Set where money for each (asset, network) lands
A high-risk action (§6.1): MFA re-prompt, audit event, email notification, and a cool-down before new routes receive quotes.
That cool-down exists because re-routing is what an account takeover is *for*. Without it, one stolen session silently redirects every future payment; with it, you get an email and a window to act.
{
"routes": [
{
"asset_symbol": "USDT",
"network_id": "tron",
"wallet_id": "wal_5Rm2Ht9Jc4Nq"
},
{
"asset_symbol": "USDC",
"network_id": "base",
"wallet_id": "wal_8Hf5Jc1Wg0Ay"
}
]
}{
"object": "list",
"url": "/v1/routes",
"has_more": false,
"data": [
{
"id": "rt_2Qd9RmTs4Vb7",
"object": "route",
"asset_symbol": "USDT",
"network_id": "tron",
"token_id": "usdt-tron",
"wallet_id": "wal_5Rm2Ht9Jc4Nq",
"active": true,
"created": "2026-08-20T14:05:00.000Z"
},
{
"id": "rt_9RmTs4Vb7Qd2",
"object": "route",
"asset_symbol": "USDC",
"network_id": "base",
"token_id": "usdc-base",
"wallet_id": "wal_8Hf5Jc1Wg0Ay",
"active": true,
"created": "2026-08-20T14:05:00.000Z"
}
]
}Exchange Rates
get /v1/exchange_rates
Indicative rates for a currency
Display and estimation only. The rate that binds a payment is the one locked on its quote for 15 minutes (§4.3), together with the source observations it was derived from — that quote row is the evidence produced in every support dispute (§4.4).
Currencies whose official and parallel FX rates diverge are quoted from crypto-market-native sources, not from an official peg (§4.2).
{
"object": "list",
"url": "/v1/exchange_rates?currency=USD",
"has_more": false,
"data": [
{
"object": "exchange_rate",
"asset_symbol": "USDT",
"currency": "USD",
"rate": "1.000431",
"sources": [
{
"source": "primary_cex",
"rate": "1.000390"
},
{
"source": "usd_composite_x_fx",
"rate": "1.000472"
}
],
"fetched_at": "2026-08-27T10:14:52.000Z"
},
{
"object": "exchange_rate",
"asset_symbol": "USDC",
"currency": "USD",
"rate": "0.999870",
"sources": [
{
"source": "primary_cex",
"rate": "0.999910"
},
{
"source": "usd_composite_x_fx",
"rate": "0.999830"
}
],
"fetched_at": "2026-08-27T10:14:52.000Z"
}
]
}Health
get /healthz
Liveness probe (public)
Unauthenticated and deliberately shallow: it proves this process is serving and nothing else. A health check that queries the database turns one slow query into a fleet-wide outage as every load balancer pulls every instance at once.
{
"object": "health",
"status": "ok",
"service": "payaider-api",
"api_version": "2026-08-27",
"time": "2026-08-27T10:15:00.000Z"
}