Skip to content
Developers

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

Never treat a browser redirect to your success URL as proof of payment. Fetch the invoice (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:

StatusCodeMeaning
401missing_api_key / invalid_keyNo key, or a key that is unknown, revoked or expired.
403insufficient_scopeThe key lacks the scope this endpoint needs.
400idempotency_key_requiredThis endpoint needs an Idempotency-Key header.
422idempotency_key_reusedThe key was already used with a different request.
409request_in_progressA request with the same key is still running; retry shortly.
404invoice_not_found / payout_not_foundNo such object for your account.
422insufficient_balanceThe available balance does not cover the payout and fees.
422destination_not_allowlistedThe payout address is not (yet) on your allowlist.
422per_payout_limit / daily_limitThe payout exceeds your configured limits.
429(rate limited)Too many requests; honour the Retry-After header.

SDKs

BozonPay .NET SDK
BozonPay.Client
new BozonPayClient(new BozonPayClientOptions { ApiKey = key })
BozonPay JavaScript SDK
@bozonpay/client
new BozonPayClient({ apiKey })
BozonPay PHP SDK
bozonpay/client
new 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.