CryptoPayrdocs
Home Dashboard Get API key

API reference

The CryptoPayr API lets you accept cryptocurrency payments, track their status, and pull live market data. It's a plain REST API that returns JSON. The base URL is:

https://cryptopayr.com

Create a payment from your backend, send your customer to the returned hosted checkout, and receive a signed webhook the moment it confirms. No coins ever touch your servers.

On the hosted checkout the customer picks any supported coin and network. Set pay_currency on a payment (or a payment link) to pin a specific coin and skip the picker. Prefer no code? Create a payment link in the dashboard and share the URL.

Authentication

Authenticate write requests with your secret API key as a Bearer token. Find your key in the dashboard. Market-data endpoints (rates, convert) are public.

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Keep your secret key private. Never expose it in client-side code.

Fees

Crypto processing fees are a percentage that starts at 3% and drops automatically with your volume, plus a flat $0.10 per payment — billed only on successful payments. No setup or monthly fees.

Rolling 24h volumeFee
$0 – $9993% + $0.10
$1,000 – $4,9992% + $0.10
$5,000+1% + $0.10

Your current rate is shown in the dashboard. By default the merchant absorbs the fee; set buyer_pays_fees: 1 to add it on top of the amount your customer pays.

Create a payment

POST /api/v1/payment/create

Creates a charge and returns a hosted checkout_url to send your customer to.

FieldTypeDescription
amountnumberRequired. Amount in the fiat currency.
currencystringRequired. 3-letter fiat code, e.g. USD.
webhook_urlstringPer-payment endpoint we POST the signed confirmation to. Overrides your account default for this payment; omit to use the default.
success_urlstringRedirect after a successful payment.
cancel_urlstringRedirect if the customer cancels.
pay_currencystringOptional. Pin the pay-in coin (e.g. USDT) to skip the picker.
pay_networkstringOptional. Network for the pinned coin (e.g. TRON).
buyer_pays_fees0 / 1Add the processing fee on top of the amount.
metadatastringYour own reference (≤ 255 chars), echoed back in webhooks.
on_behalf_ofstringPlatforms only. A client's account ID (usr_…) to credit instead of yourself. Requires a platform account.
platform_fee_percentnumberPlatforms only. Commission percent of the order value (0–50). Deducted from the client's net.
platform_fee_fixednumberPlatforms only. Fixed commission in USD on top of the percent (0–10000).

Marketplaces. If your account is a platform account, pass on_behalf_of with a client's account ID (they share their usr_… id with you) to create the charge on their account, authenticated with your API key. Your commission (platform_fee_percent of the order value plus platform_fee_fixed) is deducted from the client's net when the payment completes and credited to your balance — capped at the client's net so it can never go negative. List the Orders you've created with GET /api/v1/transactions?role=platform, and register a platform webhook in your platform settings to receive a signed order.completed event (with the commission_usd) for each one. Ordinary merchants can ignore these fields.

Send an Idempotency-Key header (any unique string, e.g. a UUID) to make retries safe — replaying the same key for 24h returns the original payment instead of creating a duplicate. Replays come back with an Idempotency-Replayed: true header.

curl -X POST https://cryptopayr.com/api/v1/payment/create \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 149.00,
    "currency": "EUR",
    "webhook_url": "https://shop.example/hook",
    "success_url": "https://shop.example/thanks",
    "metadata": "order_4815"
  }'

Response

{
  "status": "success",
  "message": "Payment created",
  "data": {
    "tid": "8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1",
    "status": "CREATED",
    "amount": 149.00,
    "currency": "EUR",
    "checkout_url": "https://cryptopayr.com/payment/8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1"
  }
}

Look up a payment

GET /api/v1/payment/lookup?tid={tid}

Public, read-only status check. Used for reconciliation and by the hosted checkout to detect confirmation.

curl https://cryptopayr.com/api/v1/payment/lookup?tid=8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1

Response

{
  "status": "success",
  "data": {
    "tid": "8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1",
    "status": "COMPLETED",
    "amount": "149.0000",
    "amount_usd": "172.3200",
    "currency": "EUR",
    "created_at": "2026-06-06 12:00:00",
    "expires_at": "2026-06-06T13:00:00+01:00",
    "expires_in": 3214,
    "coin": "USDT",
    "crypto_amount": 172.32,
    "received": 172.32,
    "remaining": 0,
    "partial": 0
  }
}

The coin, crypto_amount, received, remaining and partial fields appear once the customer has selected a coin on the hosted checkout — handy for showing live payment progress. expires_in is the seconds left before the invoice expires.

List transactions

GET /api/v1/transactions

Returns your transactions, newest first, 50 per page. Authenticate with your secret API key.

QueryTypeNotes
pageinteger1-based page number. Default 1.
statusstringOptional filter: CREATED, PENDING, COMPLETED, EXPIRED or CANCELLED.
sortstringdate (default, newest first) or status (grouped by status).
rolestringPlatforms only. merchant (default — your own received payments) or platform (the Orders you created on behalf of clients). The platform listing adds on_behalf_of, platform_fee_percent, platform_fee_fixed and commission_usd (the credited commission, or null until the Order completes) to each row.
curl https://cryptopayr.com/api/v1/transactions?page=1&status=COMPLETED \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Response

{
  "status": "success",
  "data": {
    "page": 1,
    "per_page": 50,
    "total": 128,
    "total_pages": 3,
    "has_more": true,
    "data": [
      {
        "tid": "8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1",
        "status": "COMPLETED",
        "amount": 149.00,
        "currency": "EUR",
        "amount_usd": 172.32,
        "buyer_pays_fees": 0,
        "metadata": "order-1024",
        "created_at": "2026-06-06T12:00:00+01:00"
      }
    ]
  }
}

Cancel a payment

POST /api/v1/payment/cancel

Void an unpaid invoice early instead of waiting for it to expire. Body: { "tid": "…" }. Only CREATED/PENDING payments can be cancelled — a COMPLETED one returns 409.

curl -X POST https://cryptopayr.com/api/v1/payment/cancel \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"tid":"8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1"}'

Response

{ "status": "success", "data": { "tid": "8f14…", "status": "CANCELLED" } }

Balance

GET /api/v1/balance

Returns your current wallet balance, all USD-denominated. Authenticate with your secret API key.

curl https://cryptopayr.com/api/v1/balance \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Response

{
  "status": "success",
  "data": {
    "currency": "USD",
    "available": 12840.50,
    "pending": 50.00,
    "lifetime_credited": 184210.77
  }
}

available is withdrawable now; pending is reserved for in-flight payouts; lifetime_credited is everything ever credited to you, net of fees.

Account info

GET /api/v1/account

Your own account details — including your public account id (usr_…), which others use to send you internal transfers.

curl https://cryptopayr.com/api/v1/account \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Response

{
  "status": "success",
  "data": {
    "account_id": "usr_AbC123…",
    "business_name": "Acme Store",
    "email": "[email protected]",
    "is_activated": true,
    "fee_percent": 2,
    "fixed_fee": 0.10,
    "referral_code": "AbC123",
    "default_webhook": "https://acme.com/cryptopayr",
    "default_success": null,
    "default_cancel": null,
    "created_at": "2026-01-01T00:00:00+00:00"
  }
}

Create a refund

POST /api/v1/refund/create

Refund a completed payment. We email the customer a link to choose where to receive it; funds move once they claim it. Authenticate with your standard API key.

FieldTypeNotes
tidstringRequired. The payment to refund.
amountnumberOptional USD amount; omit for the full remaining refundable amount.
emailstringOptional; defaults to the payment's stored customer email.
reasonstringOptional note shown to the customer.
curl -X POST https://cryptopayr.com/api/v1/refund/create \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"tid":"8f14…","amount":25,"email":"[email protected]"}'

Response

{ "status": "success", "data": { "refund_id": "…", "status": "REQUESTED", "tid": "8f14…", "claim_url": "https://cryptopayr.com/refund/…" } }

List refunds

GET /api/v1/refunds?page={n}&status={s}

Your refunds, newest first, 50 per page. Optional status: REQUESTED, PROCESSING, COMPLETED, REJECTED, CANCELLED. Same {page,per_page,total,total_pages,has_more,data[]} envelope as the other lists.

Webhooks

When a payment is confirmed we POST a JSON event to your webhook_url — the endpoint you set per payment, or your account default from the dashboard settings. This is how you fulfil orders server-side; never rely on the buyer's browser reaching your success_url.

Request we send

POST /your/webhook  HTTP/1.1
Content-Type: application/json
X-CryptoPayr-Signature: 9f86d081884c7d659a2feaa0c55ad015a839f4f8...

{
  "tid": "8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1",
  "status": "COMPLETED",
  "amount": "149.0000",
  "currency": "EUR",
  "metadata": "order_4815",
  "timestamp": 1781049600
}

Payload fields

FieldTypeDescription
tidstringThe transaction id you got from create.
statusstringThe event — see the table below.
amountstringOrder amount in the fiat currency.
currencystring3-letter fiat code.
metadatastringYour own reference, echoed back verbatim.
timestampintegerUnix time the event was sent.

Events

statusWhen it fires
COMPLETEDPayment confirmed on-chain (or an in-tolerance underpayment was accepted). Funds are credited to your balance. This is the event to fulfil orders on.
PENDINGSent if you re-send an event while a transaction is still awaiting confirmations (e.g. via the dashboard's Resend webhook).
EXPIRED / CANCELLEDSent if you re-send an event for a transaction that expired or was cancelled. Use it to release a held order.
REFUNDEDA refund against this payment was sent on-chain. Fires automatically when the refund completes — reconcile your records and notify the customer if needed.
Today a successful payment delivers a single COMPLETED event automatically. The other statuses share the same signed format and are delivered when an event is re-sent from the dashboard — treat your handler as status-driven and idempotent (key on tid) so it stays correct as more automatic events are added.

Verifying the signature

Every webhook carries an X-CryptoPayr-Signature header: an HMAC-SHA256 of the raw request body keyed with your secret API key (the same sk_live_… key you authenticate with). Compute the HMAC over the exact bytes you received and compare in constant time. Our official plugins verify the same way.

$body = file_get_contents('php://input');
$sig  = $_SERVER['HTTP_X_CRYPTOPAYR_SIGNATURE'] ?? '';

$expected = hash_hmac('sha256', $body, $API_KEY);   // your sk_live_… key
if (!hash_equals($expected, $sig)) {
    http_response_code(400);
    exit;                                            // reject — not from CryptoPayr
}

$event = json_decode($body, true);
if ($event['status'] === 'COMPLETED') {
    // mark order $event['metadata'] as paid (idempotent on $event['tid'])
}
http_response_code(200);
Always return 200 OK once you've stored the event. We retry on non-2xx responses. Rotating your API key in the dashboard also rotates the webhook signing key.

Send a buyer receipt

POST /api/v1/payment/receipt

A customer email is never required to take a payment. Call this after a completed payment to email the buyer a branded receipt — it's what the hosted checkout uses when a buyer opts in. The transaction must already be COMPLETED.

FieldTypeDescription
tidstringRequired. The completed transaction id.
emailstringRequired. Where to send the receipt.
curl -X POST https://cryptopayr.com/api/v1/payment/receipt \
  -H "Content-Type: application/json" \
  -d '{ "tid": "8f14e45f-ceea-467e-9a36-dcd1f8d7a2b1", "email": "[email protected]" }'

Response

{ "status": "success", "message": "Receipt sent", "data": { "tid": "8f14e45f-..." } }

Exchange rates

GET /api/v1/rates — every tracked coin's USD price.
GET /api/v1/rates/{COIN} — one coin.

curl https://cryptopayr.com/api/v1/rates/BTC

Response

{
  "status": "success",
  "data": { "currency": "BTC", "usd_rate": 68000, "base": "USD" }
}

Currency conversion

GET /api/v1/convert?from={A}&to={B}&amount={n}

Convert between any supported fiat/crypto pair, routed through USD. Crypto results carry 8 decimals, fiat 2.

curl "https://cryptopayr.com/api/v1/convert?from=USD&to=BTC&amount=100"

Response

{
  "status": "success",
  "data": {
    "from": "USD", "to": "BTC", "amount": 100,
    "result": 0.00147058, "rate": 0.0000147058
  }
}

Underpayments & expiry

Invoices expire 1 hour after creation. The hosted checkout shows a live countdown.

If a buyer sends less than the invoiced amount, the checkout shows the exact shortfall so they can top up to the same address. Merchants can opt to accept underpayments in the dashboard — when enabled, a short payment is marked complete and the normal webhook fires.

A customer email is never required to take a payment. After a successful payment the buyer can optionally enter their email to receive a receipt.

Request a withdrawal

Programmatically withdraw your available balance to a crypto address. Because this endpoint moves funds out, it is authenticated with a separate withdrawal key — not your standard API key.

Generate the withdrawal key under Developers → Withdrawal key. For security only a hash is stored, so the key (wk_live_…) is shown once at generation. Regenerating invalidates the previous key immediately.

POST /api/v1/payout/create

Authenticate with the withdrawal key as a Bearer token:

Authorization: Bearer wk_live_xxxxxxxxxxxxxxxxxxxxxxxx
FieldTypeNotes
amountnumberRequired. USD amount to withdraw from your available balance.
currencystringCoin to receive, e.g. USDT. Default USDT.
networkstringNetwork, e.g. TRON. Default TRON.
addressstringDestination wallet address. Required unless address_id is given.
address_idintegerOptional. Use a saved address book entry — its address, currency and network are used.
curl -X POST https://cryptopayr.com/api/v1/payout/create \ -H "Authorization: Bearer wk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"amount":50,"currency":"USDT","network":"TRON","address":"T..."}'

Response

{ "status": "success", "message": "Payout requested", "data": { "payout_id": 42, "status": "PROCESSING", "amount": 50, "platform_fee": 0.5, "receive_estimate": 48.4, "currency": "USDT", "network": "TRON", "address": "T..." } }

Payouts are dispatched immediately — there is no manual approval step. A successful call returns PROCESSING; on-chain confirmation moves it to COMPLETED. Your balance is checked atomically, so a payout can never exceed your available balance.

If the call fails it returns HTTP 502 with: "Something possible went wrong, your payout might have been sent, if not contact us [email protected]". In this case the withdrawal may already have been sent and your balance is not credited back — do not blindly retry; contact support to reconcile. Always send an Idempotency-Key so a retried request replays the original result instead of dispatching a second payout.

If a withdrawal whitelist is enabled on your account, the address must be one of your saved addresses.

List payouts

GET /api/v1/payouts

Returns your payouts, newest first, 50 per page. This is a read endpoint — authenticate with your standard API key (not the withdrawal key).

QueryTypeNotes
pageinteger1-based page number. Default 1.
statusstringOptional filter: PENDING, PROCESSING, COMPLETED or REJECTED.
curl https://cryptopayr.com/api/v1/payouts?page=1 \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Response

{
  "status": "success",
  "data": {
    "page": 1,
    "per_page": 50,
    "total": 7,
    "total_pages": 1,
    "has_more": false,
    "data": [
      {
        "payout_id": 42,
        "status": "COMPLETED",
        "amount": 50.00,
        "platform_fee": 0.50,
        "receive_estimate": 48.40,
        "currency": "USDT",
        "network": "TRON",
        "address": "T...",
        "tx_hash": "9d2c…",
        "created_at": "2026-06-06T12:00:00+01:00"
      }
    ]
  }
}

Get a payout

GET /api/v1/payout/lookup?id={payout_id}

Returns a single payout you own. Authenticate with your standard API key. A payout id that isn't yours returns 404.

curl https://cryptopayr.com/api/v1/payout/lookup?id=42 \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Response

{
  "status": "success",
  "data": {
    "payout_id": 42,
    "status": "COMPLETED",
    "amount": 50.00,
    "platform_fee": 0.50,
    "receive_estimate": 48.40,
    "currency": "USDT",
    "network": "TRON",
    "address": "T...",
    "tx_hash": "9d2c…",
    "created_at": "2026-06-06T12:00:00+01:00"
  }
}

The tx_hash is the processor/on-chain reference once the payout has been dispatched (null until then).

Transfer to an account

Move balance to another CryptoPayr account instantly and for free — an internal book transfer, nothing touches the chain. Because it moves funds out of your balance, it uses your withdrawal key (wk_live_…), not your standard API key.

POST /api/v1/transfer/create

Authorization: Bearer wk_live_xxxxxxxxxxxxxxxxxxxxxxxx
FieldTypeNotes
recipientstringRequired. The destination account's public id, e.g. usr_AbC123… (aliases: to, user_id).
amountnumberRequired. USD amount to send from your available balance.
notestringOptional memo (≤255 chars).
curl -X POST https://cryptopayr.com/api/v1/transfer/create \ -H "Authorization: Bearer wk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"recipient":"usr_AbC123","amount":25,"note":"invoice 1024"}'

Response

{ "status": "success", "message": "Transfer complete", "data": { "transfer_id": "txf_AbC123…", "status": "COMPLETED", "amount": 25, "currency": "USD", "recipient": "usr_AbC123", "recipient_name": "Acme Store", "note": "invoice 1024", "created_at": "2026-06-08T12:00:00+01:00" } }

The transfer is atomic — your balance is debited and the recipient credited in one step, or nothing happens. Insufficient balance returns 422; an unknown recipient returns 404; sending to your own account is rejected. Send an Idempotency-Key header so a retried request replays the original result instead of sending twice.

List transfers

GET /api/v1/transfers?page={n}&direction={in|out}

Your internal transfers (sent and received), newest first, 50 per page. Each row has a direction (in/out) and the counterparty account id. Authenticate with your standard API key.

Get a transfer

GET /api/v1/transfer/lookup?id={txf_…}

One transfer you're a party to (by its txf_ id). Returns 404 if you're neither sender nor recipient.

Estimate a payout

GET /api/v1/payout/estimate?amount={n}¤cy={c}&network={n}

Preview the platform fee, network fee and net "you receive" amount before sending a withdrawal — nothing is created. currency defaults USDT, network defaults TRON.

{ "status": "success", "data": { "amount": 50, "platform_fee": 0.5, "network_fee": 1.1, "receive_estimate": 48.4, "currency": "USDT", "network": "TRON" } }

Payout addresses

GET /api/v1/payout-addresses — list saved addresses.
POST /api/v1/payout-addresses — add one (currency, network, address, optional label, make_default).
DELETE /api/v1/payout-addresses?id={id} — remove one.

Your saved address book — the same one the withdrawal endpoint can target with address_id, and that the optional withdrawal whitelist enforces.

Payment statuses

StatusMeaning
CREATEDPayment created, awaiting the customer.
PENDINGFunds detected, awaiting confirmations.
COMPLETEDPaid and confirmed. Webhook fired.
EXPIREDCheckout link expired before payment.
CANCELLEDCancelled by the customer or merchant.

Errors

Errors use standard HTTP status codes and a JSON body:

{ "status": "failed", "error": "Invalid API key", "timestamp": 1781049600 }
CodeMeaning
401Missing or invalid API key.
404Resource or currency not found.
422Missing or invalid parameters.
429Rate limit exceeded — see below.
500Something went wrong on our side.

Rate limits

Every API response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix time). When you exceed a window we return 429 with a Retry-After header.

EndpointLimit
POST /payment/create120 / min per merchant
GET /rates, /convert, /payment/lookup120 / min per IP
POST /payment/receipt20 / min per IP

Need a hand? Email [email protected].