BozonPay API
A REST API for invoices, payments, payouts, exchange and webhooks. Requests and responses are JSON; amounts are decimal strings.
Base URL & environments
https://api.bozonpay.com/v1
Paths below are relative to this base, e.g. https://api.bozonpay.com/v1/invoices. Integrations built against
https://bozonpay.com/api/v1 keep working: both forms reach the same API. Use testnet networks (for example
ethereum-sepolia, tron-nile, solana-devnet) to test end to end before going live.
Authentication
Create an API key under Developers → API keys in your dashboard and send it as a bearer token. Keys are scoped (for example
invoices:create, payouts:create) and can be restricted to IP ranges. Keep keys on your server.
curl https://api.bozonpay.com/v1/balances \
-H "Authorization: Bearer bzk_xxxxxxxxxxxxxxxx_..."
Create an invoice
curl -X POST https://api.bozonpay.com/v1/invoices \
-H "Authorization: Bearer $BOZONPAY_API_KEY" \
-H "Idempotency-Key: order-10035" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "ORDER-10035",
"amount": "149.99",
"currency": "USD",
"allowedCurrencies": [ { "network": "bitcoin", "asset": "BTC" }, { "network": "ethereum", "asset": "USDC" } ],
"description": "Premium Subscription",
"expiresInMinutes": 30,
"successUrl": "https://shop.example.com/thanks",
"metadata": { "productId": "P100" }
}'
The response contains checkoutUrl: redirect the customer there, or build your own checkout with
POST /invoices/{id}/payment-method.
Verify payment server-side
GET /invoices/{id}) or rely on a signature-verified webhook, and fulfil only when the status is
completed (or overpaid).Webhooks
Each delivery carries X-BozonPay-Event-Id, X-BozonPay-Event-Type, X-BozonPay-Delivery-Id,
X-BozonPay-Attempt and X-BozonPay-Signature: t=<unix>,v1=<hex> where
v1 = HMAC-SHA256(secret, t + "." + rawBody). During a secret rotation the header carries one v1 per secret.
Reject timestamps older than 5 minutes, compare in constant time and de-duplicate by event id: deliveries are at-least-once.
The SDKs do all of this for you.
// Node.js without the SDK
const [t, v1] = header.split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(expected, 'hex'))
&& Math.abs(Date.now() / 1000 - Number(t)) < 300;
Payouts
POST /payouts with externalPayoutId, network, asset, destinationAddress and
amount. Funds are reserved atomically; a payout is reported completed only after the network's confirmation
threshold. Destinations may need to be on your allowlist, and payouts above your threshold wait for approval.
Idempotency
Mutating financial endpoints require an Idempotency-Key. Retrying with the same key and body returns the original response
(header Idempotent-Replayed: true); the same key with a different body returns 422. Keys are kept for 24 hours.
externalOrderId and externalPayoutId uniqueness is enforced independently.
Assets & networks
Assets are always identified by network + symbol: USDT on ethereum and on tron are
different assets with different contracts and decimals. List them with GET /currencies. Funds sent on another network than
the one shown at checkout are held for manual review.
Errors
Errors are RFC 7807 problem details with a stable code; type is
https://docs.bozonpay.com/errors/{code}. Common codes:
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_api_key / invalid_key | No key, or a key that is unknown, revoked or expired. |
| 403 | insufficient_scope | The key lacks the scope this endpoint needs. |
| 400 | idempotency_key_required | This endpoint needs an Idempotency-Key header. |
| 422 | idempotency_key_reused | The key was already used with a different request. |
| 409 | request_in_progress | A request with the same key is still running; retry shortly. |
| 404 | invoice_not_found / payout_not_found | No such object for your account. |
| 422 | insufficient_balance | The available balance does not cover the payout and fees. |
| 422 | destination_not_allowlisted | The payout address is not (yet) on your allowlist. |
| 422 | per_payout_limit / daily_limit | The payout exceeds your configured limits. |
| 429 | (rate limited) | Too many requests; honour the Retry-After header. |
SDKs
BozonPay.Clientnew BozonPayClient(new BozonPayClientOptions { ApiKey = key })
@bozonpay/clientnew BozonPayClient({ apiKey })
bozonpay/clientnew BozonPay\Client(null, $apiKey)
All three default to https://api.bozonpay.com/v1, retry idempotent requests safely and verify webhook
signatures. They are not yet published to NuGet, npm or Packagist: build them from the source repository until then.