Phoenix Pay
API Reference

API Overview

Phoenix Pay REST API conventions, authentication, error handling, and pagination.

Base URL

https://pay.phoenixverse.io/api

All endpoint paths in this documentation are relative to this base URL.

Authentication

Every API request (except /api/.well-known/signing-key) must include three headers:

HeaderDescription
X-Key-IdYour API key ID
X-TimestampCurrent Unix timestamp (seconds)
X-SignatureBase64-encoded Ed25519 signature of "{timestamp}.{body}"
Authenticated request
curl https://pay.phoenixverse.io/api/deposits \
  -H "X-Key-Id: your-key-id" \
  -H "X-Timestamp: $(date +%s)" \
  -H "X-Signature: <signature>" \
  -H "Content-Type: application/json"

See the Authentication guide for signing examples in Node.js, Python, Elixir, and Go.

Request Format

  • Content-Type: application/json for all POST requests
  • Character encoding: UTF-8
  • Amounts: Integer cents (smallest currency unit). 5000 = 50.00.
Example request body
{
  "reference_id": "order-12345",
  "amount": 5000,
  "currency": "USDT",
  "channel": "crypto_address"
}

Amounts are always in the smallest currency unit (cents). 5000 means 50.00 in the given currency. Decimal strings like "50.00" are accepted as a convenience and auto-converted to 5000, but integer format is recommended.

Response Format

Phoenix Pay has two response patterns:

Create Responses (Action-Based)

When you create a deposit or withdrawal, you get an action telling your application what to do next:

Create response
{
  "intent_id": "01912e4a-7b3c-7def-8a90-1234567890ab",
  "action": "redirect",
  "url": "https://checkout.chapa.co/checkout/payment/abc123"
}

See Payment Lifecycle — Actions for all action types.

GET Responses (Full Resource)

When you retrieve a payment, you get the full intent object with the latest attempt details:

Single resource (200 OK)
{
  "id": "01912e4a-7b3c-7def-8a90-1234567890ab",
  "reference_id": "order-12345",
  "display_ref": "DEP-20260311-A1B2C3",
  "type": "deposit",
  "status": "completed",
  "amount": 5000,
  "currency": "USDT",
  "channel": "crypto_address",
  "payment_method": null,
  "error_code": null,
  "error_detail": null,
  "psp": "nowpayments",
  "psp_external_id": "np_5012345678",
  "inserted_at": "2026-03-11T12:30:00.000000Z",
  "updated_at": "2026-03-11T12:45:00.000000Z"
}

List Responses (Paginated)

List endpoints return a data array with a meta pagination object:

List response (200 OK)
{
  "data": [ ... ],
  "meta": {
    "page": 1,
    "limit": 25,
    "total": 142,
    "total_pages": 6,
    "has_next": true,
    "has_prev": false
  }
}

Pagination

List endpoints use offset-based pagination:

ParameterTypeDefaultMaxDescription
pageinteger1Page number (1-indexed)
limitinteger25100Results per page

The meta object contains:

FieldTypeDescription
pageintegerCurrent page number
limitintegerResults per page
totalintegerTotal matching records
total_pagesintegerTotal pages
has_nextbooleanWhether there is a next page
has_prevbooleanWhether there is a previous page

Error Format

Error response
{
  "error": "missing required parameter: reference_id"
}

PSP errors may include an additional message field:

PSP error response
{
  "error": "insufficient_liquidity",
  "message": "PSP does not support this currency in your region"
}

Common HTTP Status Codes

StatusMeaning
200 OKRequest succeeded
201 CreatedDeposit or withdrawal created successfully
400 Bad RequestMissing or invalid request parameters
401 UnauthorizedMissing or invalid request signature, or stale timestamp
403 ForbiddenAuthenticated but insufficient permissions
404 Not FoundResource does not exist or belongs to a different tenant
409 ConflictDuplicate reference_id — the existing intent_id is returned
422 Unprocessable EntityPSP rejected the request
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorUnexpected server error

Rate Limiting

Endpoint TypeLimit
Create endpoints (POST)100 requests/minute per tenant
Read endpoints (GET)300 requests/minute per tenant
Webhook endpointsUnlimited (PSP-initiated)

Rate limits are per tenant (organization), not per API key.

API Endpoints