Payonara API
Payonara is a non-custodial crypto payment gateway. Your customers pay directly to deterministically derived wallet addresses — funds go straight to your wallets, never ours.
REST API
JSON over HTTPS. Authenticate with your API key header.
Webhooks
Receive real-time POST notifications on every confirmed payment.
Non-Custodial
Unique addresses derived via BIP32. No pooled wallets, no custody risk.
Multi-Chain
USDT on 8 networks plus BTC, XMR, ZEC — one API, every invoice.
Base URL
https://api.payonara.org
Response format
All responses are JSON. Successful responses return the resource directly. Errors follow a consistent shape:
{
"error": "human-readable description"
}Authentication
Every shop-facing API request must include your API key in the X-Api-Key header. Your key is a 64-character hex string generated when you register.
X-Api-Key: 4a7f3c9e2b1d...
POST /v1/auth/register
Create a new shop account and receive your API key.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
shop_name | string | Yes | Display name for your shop |
email | string | Yes | Login email |
password | string | Yes | Min 8 characters |
Response 201
{
"token": "4a7f3c9e...",
"shop_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"name": "My Store",
"email": "you@example.com",
"api_key": "4a7f3c9e..."
}POST /v1/auth/login
Retrieve your API key using email and password.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Account email |
password | string | Yes | Account password |
Response 200
{
"token": "4a7f3c9e...",
"shop_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"name": "My Store",
"email": "you@example.com",
"api_key": "4a7f3c9e..."
}Quick Start
Get a payment invoice live in under 5 minutes.
1. Create an account
curl -X POST https://api.payonara.org/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"shop_name": "My Store",
"email": "you@example.com",
"password": "supersecret"
}'Save the api_key from the response.
2. Set your webhook URL
curl -X PUT https://api.payonara.org/v1/settings/webhook \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "webhook_url": "https://yoursite.com/payments/webhook" }'3. Create an invoice
curl -X POST https://api.payonara.org/v1/invoices \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_user_id": "user_42",
"amount_usd": "25.00"
}'4. Show addresses to your customer
The response contains one address per supported network. Display the networks you want to accept. Payonara monitors all of them simultaneously — the first confirmed payment completes the invoice.
5. Receive webhook when paid
Once a payment reaches the required confirmation threshold, Payonara sends a POST to your webhook URL. See the Webhooks section for the full payload.
Invoices
An invoice is a payment request tied to one of your end-users. It contains a unique address on every supported network and expires after 1 hour.
Invoice object
| Field | Type | Description |
|---|---|---|
id | UUID | Invoice identifier |
status | string | pending | completed | expired |
bep20_address | string | BSC / BEP20 address |
trc20_address | string | Tron address |
ton_address | string | TON address |
erc20_address | string | Ethereum address |
polygon_address | string | Polygon address |
arbitrum_address | string | Arbitrum address |
optimism_address | string | Optimism address |
sol_address | string | Solana address |
btc_address | string | Bitcoin address |
xmr_address | string | Monero address |
zec_address | string | Zcash address |
amount_usd | string | Requested USD amount (optional) |
expected_btc_amount | string | BTC equivalent at creation time |
expected_xmr_amount | string | XMR equivalent at creation time |
expected_zec_amount | string | ZEC equivalent at creation time |
btc_usd_rate | string | BTC/USD rate locked at creation |
xmr_usd_rate | string | XMR/USD rate locked at creation |
zec_usd_rate | string | ZEC/USD rate locked at creation |
expires_at | RFC3339 | When the invoice expires (1 hour from creation) |
created_at | RFC3339 | Creation timestamp |
transactions | array | Detected payments (only on GET) |
POST /v1/invoices
Create a new invoice for an end-user.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
external_user_id | string | Yes | Your internal user identifier |
amount_usd | string | No | Expected USD amount. If provided, native coin amounts are calculated and locked. |
Example request
curl -X POST https://api.payonara.org/v1/invoices \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_user_id": "user_42",
"amount_usd": "100.00"
}'Response 200
{
"id": "9e4c83a1-...",
"status": "pending",
"bep20_address": "0xAbCd...",
"trc20_address": "TXyz...",
"ton_address": "UQAb...",
"erc20_address": "0xAbCd...",
"polygon_address": "0xAbCd...",
"arbitrum_address": "0xAbCd...",
"optimism_address": "0xAbCd...",
"sol_address": "7xKc...",
"btc_address": "bc1q...",
"xmr_address": "48...",
"zec_address": "t1...",
"amount_usd": "100.00",
"expected_btc_amount": "0.00153420",
"expected_xmr_amount": "1.234567890123",
"expected_zec_amount": "4.12300000",
"btc_usd_rate": "65200.00",
"xmr_usd_rate": "81.04",
"zec_usd_rate": "24.25",
"expires_at": "2025-01-01T13:00:00Z",
"created_at": "2025-01-01T12:00:00Z"
}GET /v1/invoices/:id
Retrieve an invoice and all associated transactions.
Example request
curl https://api.payonara.org/v1/invoices/9e4c83a1-... \ -H "X-Api-Key: YOUR_API_KEY"
Transaction object
| Field | Type | Description |
|---|---|---|
id | UUID | Transaction ID |
network | string | Network identifier (see Networks) |
tx_hash | string | On-chain transaction hash |
amount_usdt | string | Gross received amount (6 decimals) |
fee_usdt | string | Platform fee deducted (6 decimals) |
net_usdt | string | Net credited to balance (6 decimals) |
status | string | pending | confirmed |
block_number | integer | Block of confirmation (null until confirmed) |
late_payment | boolean | True if payment arrived after invoice expiry |
amount_coin | string | Native coin amount (BTC/XMR/ZEC only) |
coin_symbol | string | btc | xmr | zec (or null) |
usd_rate | string | USD rate at payment time (native coins only) |
created_at | RFC3339 | When the transaction was first detected |
Invoice lifecycle
Invoice created. Monitoring all networks.
Transaction visible on-chain. Webhook fires. Waiting for confirmations.
Required confirmations reached. Balance credited.
An invoice also becomes expired if it remains unpaid for 1 hour. Late payments (received after expiry) are still recorded and credited — they will have late_payment: true.
Users & Balances
Each of your end-users is identified by an external_user_id you control. Payonara tracks their cumulative USDT balance from all confirmed payments.
GET /v1/users/:external_user_id/balance
Retrieve a user's current USDT balance.
Example request
curl https://api.payonara.org/v1/users/user_42/balance \ -H "X-Api-Key: YOUR_API_KEY"
Response 200
{
"external_user_id": "user_42",
"balance_usdt": "95.210000"
}Returns 0.000000 for unknown users — no 404 is returned.
GET /v1/users/:external_user_id/transactions
List confirmed transactions for a user, sorted by date descending.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max results (1–200) |
offset | integer | 0 | Pagination offset |
Example request
curl "https://api.payonara.org/v1/users/user_42/transactions?limit=10&offset=0" \ -H "X-Api-Key: YOUR_API_KEY"
Response 200
{
"transactions": [
{
"id": "a1b2c3...",
"invoice_id": "9e4c83a1...",
"network": "trc20",
"tx_hash": "abc123...",
"amount_usdt": "100.000000",
"fee_usdt": "1.500000",
"net_usdt": "98.500000",
"status": "confirmed",
"block_number": 12345678,
"late_payment": false,
"amount_coin": null,
"coin_symbol": null,
"usd_rate": null,
"created_at": "2025-01-01T12:05:00Z"
}
]
}Settings
Manage your shop profile, API key, webhook, and accepted currencies.
GET /v1/settings
Retrieve current shop configuration.
{
"shop_id": "d290f1ee-...",
"name": "My Store",
"email": "you@example.com",
"api_key": "4a7f3c9e...",
"webhook_url": "https://yoursite.com/webhook",
"fee_rate": "0.0150",
"supported_currencies": "BTC,XMR,ZEC",
"min_topup_amount": "5.000000"
}PUT /v1/settings/profile
Update shop name and email.
curl -X PUT https://api.payonara.org/v1/settings/profile \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "New Name", "email": "new@example.com" }'PUT /v1/settings/webhook
Set or update the webhook URL.
curl -X PUT https://api.payonara.org/v1/settings/webhook \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "webhook_url": "https://yoursite.com/payments/webhook" }'PUT /v1/settings/payment
Configure which currencies to accept and a minimum top-up amount.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
supported_currencies | string | No | Comma-separated list, e.g. BTC,XMR,ZEC |
min_topup_amount | string | No | Minimum accepted amount in USDT |
POST /v1/settings/regenerate-key
Generate a new API key. The old key is immediately invalidated.
curl -X POST https://api.payonara.org/v1/settings/regenerate-key \ -H "X-Api-Key: YOUR_API_KEY"
Response 200
{
"api_key": "new64hexcharacterkey..."
}Webhooks
Payonara sends an HTTP POST to your configured webhook URL whenever a payment transaction is confirmed on-chain.
Payload
{
"event": "payment_confirmed",
"invoice_id": "9e4c83a1-...",
"external_user_id": "user_42",
"network": "trc20",
"tx_hash": "abc123...",
"amount_usdt": "100.000000",
"fee_usdt": "1.500000",
"net_usdt": "98.500000"
}Payload fields
| Field | Description |
|---|---|
event | Always payment_confirmed |
invoice_id | The invoice this payment belongs to |
external_user_id | Your user identifier from invoice creation |
network | Which blockchain the payment arrived on |
tx_hash | On-chain transaction hash for verification |
amount_usdt | Gross amount received (USDT equivalent) |
fee_usdt | Platform fee amount |
net_usdt | Net amount credited to user balance |
Request headers
Content-Type: application/json User-Agent: CryptoPaymentGateway/1.0
Retry policy
If your endpoint does not return a 2xx status, Payonara retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 5 seconds |
| 3 | 30 seconds |
| 4 | 120 seconds |
After 4 failed attempts the event is dropped. Make your endpoint idempotent — use tx_hash as a deduplication key.
Responding to webhooks
Return any 2xx status code as fast as possible. Do your processing asynchronously to avoid timeouts (10s limit).
Example handler (Node.js)
app.post('/payments/webhook', express.json(), async (req, res) => {
const { event, tx_hash, net_usdt, external_user_id } = req.body
if (event !== 'payment_confirmed') return res.sendStatus(200)
// Idempotency check
if (await db.txExists(tx_hash)) return res.sendStatus(200)
await db.creditUser(external_user_id, net_usdt)
await db.recordTx(tx_hash)
res.sendStatus(200)
})Supported Networks
Every invoice receives a unique address on all enabled networks simultaneously. Amounts are denominated in USDT regardless of the underlying network.
USDT networks
| Network ID | Chain | Token | Confirmations | Typical finality |
|---|---|---|---|---|
bep20 | BNB Smart Chain | USDT BEP20 | 12 | ~36 sec |
trc20 | Tron | USDT TRC20 | 20 | ~60 sec |
ton | TON | USDT Jetton | 3 | ~15 sec |
erc20 | Ethereum | USDT ERC20 | 12 | ~3 min |
polygon | Polygon | USDT | 128 | ~4 min |
arbitrum | Arbitrum One | USDT | 1 | ~1 sec |
optimism | Optimism | USDT | 1 | ~2 sec |
sol | Solana | USDT SPL | 1 | ~0.5 sec |
Native coin networks
These networks accept payment in the native coin. When you provide amount_usd, the expected coin amount is calculated at invoice creation using live CoinGecko prices and locked for the invoice lifetime.
| Network ID | Coin | Decimals | Confirmations |
|---|---|---|---|
btc | Bitcoin | 8 | 3 |
xmr | Monero | 12 | 10 |
zec | Zcash | 8 | 5 |
Errors
All errors return a JSON body with an error field and an appropriate HTTP status code.
HTTP status codes
| Status | Meaning | Common cause |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created (register, admin create shop) |
| 400 | Bad Request | Missing required fields or invalid JSON |
| 401 | Unauthorized | Missing or invalid X-Api-Key |
| 404 | Not Found | Invoice doesn't exist or belongs to another shop |
| 409 | Conflict | Email already registered |
| 422 | Unprocessable Entity | Password too short (min 8 chars) |
| 500 | Internal Server Error | Database or derivation failure |
Error response format
{
"error": "invalid API key"
}Common error messages
| Error message | Status | Resolution |
|---|---|---|
missing X-Api-Key header | 401 | Add the header to your request |
invalid API key | 401 | Check your key or regenerate via Settings |
invoice not found | 404 | Verify the invoice ID and that it belongs to your shop |
email already registered | 409 | Use a different email or log in instead |
password must be at least 8 characters | 422 | Use a longer password |
external_user_id is required | 400 | Include external_user_id in your invoice request |