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...
Keep your API key secret. Anyone with your key can create invoices and read your data. Use the Settings API to rotate your key at any time.

POST /v1/auth/register

Create a new shop account and receive your API key.

Request body

FieldTypeRequiredDescription
shop_namestringYesDisplay name for your shop
emailstringYesLogin email
passwordstringYesMin 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

FieldTypeRequiredDescription
emailstringYesAccount email
passwordstringYesAccount 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

FieldTypeDescription
idUUIDInvoice identifier
statusstringpending | completed | expired
bep20_addressstringBSC / BEP20 address
trc20_addressstringTron address
ton_addressstringTON address
erc20_addressstringEthereum address
polygon_addressstringPolygon address
arbitrum_addressstringArbitrum address
optimism_addressstringOptimism address
sol_addressstringSolana address
btc_addressstringBitcoin address
xmr_addressstringMonero address
zec_addressstringZcash address
amount_usdstringRequested USD amount (optional)
expected_btc_amountstringBTC equivalent at creation time
expected_xmr_amountstringXMR equivalent at creation time
expected_zec_amountstringZEC equivalent at creation time
btc_usd_ratestringBTC/USD rate locked at creation
xmr_usd_ratestringXMR/USD rate locked at creation
zec_usd_ratestringZEC/USD rate locked at creation
expires_atRFC3339When the invoice expires (1 hour from creation)
created_atRFC3339Creation timestamp
transactionsarrayDetected payments (only on GET)

POST /v1/invoices

Create a new invoice for an end-user.

Request body

FieldTypeRequiredDescription
external_user_idstringYesYour internal user identifier
amount_usdstringNoExpected 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"
}
EVM addresses are shared. Ethereum, Polygon, Arbitrum, and Optimism all derive from the same key and return identical addresses. Payments on any of those chains are tracked independently.

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

FieldTypeDescription
idUUIDTransaction ID
networkstringNetwork identifier (see Networks)
tx_hashstringOn-chain transaction hash
amount_usdtstringGross received amount (6 decimals)
fee_usdtstringPlatform fee deducted (6 decimals)
net_usdtstringNet credited to balance (6 decimals)
statusstringpending | confirmed
block_numberintegerBlock of confirmation (null until confirmed)
late_paymentbooleanTrue if payment arrived after invoice expiry
amount_coinstringNative coin amount (BTC/XMR/ZEC only)
coin_symbolstringbtc | xmr | zec (or null)
usd_ratestringUSD rate at payment time (native coins only)
created_atRFC3339When the transaction was first detected

Invoice lifecycle

pending

Invoice created. Monitoring all networks.

payment detected

Transaction visible on-chain. Webhook fires. Waiting for confirmations.

completed

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

ParameterTypeDefaultDescription
limitinteger50Max results (1–200)
offsetinteger0Pagination 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

FieldTypeRequiredDescription
supported_currenciesstringNoComma-separated list, e.g. BTC,XMR,ZEC
min_topup_amountstringNoMinimum 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..."
}
Update your API key in all services immediately after regenerating — your old key stops working right away.

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

FieldDescription
eventAlways payment_confirmed
invoice_idThe invoice this payment belongs to
external_user_idYour user identifier from invoice creation
networkWhich blockchain the payment arrived on
tx_hashOn-chain transaction hash for verification
amount_usdtGross amount received (USDT equivalent)
fee_usdtPlatform fee amount
net_usdtNet 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:

AttemptDelay
1Immediate
25 seconds
330 seconds
4120 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 IDChainTokenConfirmationsTypical finality
bep20BNB Smart ChainUSDT BEP2012~36 sec
trc20TronUSDT TRC2020~60 sec
tonTONUSDT Jetton3~15 sec
erc20EthereumUSDT ERC2012~3 min
polygonPolygonUSDT128~4 min
arbitrumArbitrum OneUSDT1~1 sec
optimismOptimismUSDT1~2 sec
solSolanaUSDT SPL1~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 IDCoinDecimalsConfirmations
btcBitcoin83
xmrMonero1210
zecZcash85
Address derivation. Addresses are derived deterministically via BIP32 from your shop's master keys. Each end-user receives a unique wallet index that is never reused — ensuring payments are always attributed correctly.

Errors

All errors return a JSON body with an error field and an appropriate HTTP status code.

HTTP status codes

StatusMeaningCommon cause
200OKRequest succeeded
201CreatedResource created (register, admin create shop)
400Bad RequestMissing required fields or invalid JSON
401UnauthorizedMissing or invalid X-Api-Key
404Not FoundInvoice doesn't exist or belongs to another shop
409ConflictEmail already registered
422Unprocessable EntityPassword too short (min 8 chars)
500Internal Server ErrorDatabase or derivation failure

Error response format

{
  "error": "invalid API key"
}

Common error messages

Error messageStatusResolution
missing X-Api-Key header401Add the header to your request
invalid API key401Check your key or regenerate via Settings
invoice not found404Verify the invoice ID and that it belongs to your shop
email already registered409Use a different email or log in instead
password must be at least 8 characters422Use a longer password
external_user_id is required400Include external_user_id in your invoice request