Developers · API v1

Accept crypto payments from your code

A small REST API for websites and Telegram bots: create an invoice, send the customer to the checkout page and receive a signed webhook when the payment lands.

Base URL
https://scarabpay.com/pay/api/v1
Auth
Bearer sk_live_… / sk_test_…
Format
JSON · UTF-8
Amounts
USD as strings, e.g. "20.00"
Getting started

Quickstart

Three steps from zero to your first paid invoice.

  1. 1
    Create a project

    Add your website or Telegram bot under “Receive payments” in your account and verify it. Then open the Developers tab and create a key.

  2. 2
    Create an invoice

    Call POST /invoices with the amount and your order id, then send the customer to checkout_url.

  3. 3
    Handle the webhook

    Verify the signature and deliver the order when you receive invoice.paid.

curl -X POST https://scarabpay.com/pay/api/v1/invoices \
  -H "Authorization: Bearer $SCARABPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "20.00",
    "order_id": "order-1001",
    "description": "Gold plan",
    "customer_ref": "tg:55123",
    "return_url": "https://your-site.com/thanks"
  }'

Authentication

Send your secret key in the Authorization header of every request. Keys are shown once when you create them; store them on your server only.

Authorization: Bearer sk_live_••••••••••••••••••••••••••••••••••••••••
Content-Type: application/json
sk_live_

Live key: real payments, credited to your ScarabPay balance.

sk_test_

Sandbox key: test invoices, nothing is charged or credited.

Never put a key in a mobile app, browser code or a public repository. If a key leaks, revoke it from the Developers tab and create a new one.

Sandbox

Use a sk_test_ key to build and test without moving money. Sandbox invoices open the same checkout page with a test banner.

  • Mark an invoice as paid with POST /invoices/{id}/simulate — mode "full" or "partial".
  • Webhooks are sent as usual, with "sandbox": true in the body.
  • Static addresses are live only.

Conventions

  • Requests and responses are JSON. Send Content-Type: application/json.
  • Amounts are USD strings with two decimals. Coin amounts are strings without trailing zeros.
  • Timestamps are ISO 8601 with time zone.
  • Successful responses wrap the object in "data". Lists add "meta" with page, pages and total; use ?page= and ?per_page= (max 100).
  • order_id makes invoice creation safe to retry: the same order_id returns the same invoice (200) instead of creating a new one (201).

Errors

Errors return a non-2xx status and a JSON body with a stable code you can switch on.

StatusCodeMeaning
401unauthorizedMissing, malformed or revoked key.
403forbiddenProject is not active (not verified or suspended).
404not_foundInvoice not found in this project and mode.
409limit_reachedYour tier limit is reached.
409invoice_not_payableThe invoice can no longer change (paid, expired or cancelled).
422validation_failedA field is invalid; see "fields" for details.
422method_not_allowedThe chosen method is not enabled for this project.
429rate_limitedToo many requests. Wait and retry.
502provider_errorTemporary provider error. Retry with the same order_id.
503coin_unavailableThe coin is temporarily unavailable; offer another method.
422
{
  "error": {
    "code": "validation_failed",
    "message": "Invalid request.",
    "fields": { "order_id": ["The order id field is required."] }
  }
}

Rate limits

Each API key can make up to 60 requests per minute. After too many failed key attempts from one IP, that IP is blocked for 10 minutes.

On 429, wait a few seconds and retry. Do not poll invoices in a loop; use webhooks.
Resources

Invoices

One invoice per order. The customer picks a coin on the checkout page (or you preselect it) and pays the exact amount shown. An unpaid invoice expires after 30 minutes.

Create an invoice

POST/invoices

Returns 201 with a new invoice, or 200 with the existing one if order_id was used before.

FieldDescription
amount
string · required
Amount in USD, 1 to 100000.
order_id
string · required
Your unique order id. Letters, digits and _ - : . only, up to 100.
description
string · optional
Shown to the customer on the checkout page.
customer_ref
string · optional
Your customer id, e.g. tg:55123.
customer_email
string · optional
Customer email for receipts.
return_url
url · optional
Where the “Back to store” button goes after payment.
method
string · optional
Preselect a coin and skip the picker, e.g. usdt_trc20. See /currencies.
metadata
object · optional
Up to 20 key/value pairs for your own use.
curl -X POST https://scarabpay.com/pay/api/v1/invoices \
  -H "Authorization: Bearer $SCARABPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "20.00",
    "order_id": "order-1001",
    "description": "Gold plan",
    "customer_ref": "tg:55123",
    "return_url": "https://your-site.com/thanks"
  }'
Response
{
  "data": {
    "id": "inv_8f2k1m0q7x3c9v5b4n6z",
    "order_id": "order-1001",
    "status": "pending",
    "amount": "20.00",
    "currency": "USD",
    "paid_amount": "0.00",
    "description": "Gold plan",
    "customer_ref": "tg:55123",
    "method": null,
    "coin": null,
    "network": null,
    "address": null,
    "coin_amount": null,
    "checkout_url": "https://scarabpay.com/pay/i/inv_8f2k1m0q7x3c9v5b4n6z",
    "sandbox": false,
    "expires_at": "2026-10-19T12:30:00+00:00",
    "paid_at": null,
    "created_at": "2026-10-19T12:00:00+00:00"
  }
}

Retrieve an invoice

GET/invoices/{id|order_id}

Look up by invoice id (inv_…) or by your order_id.

curl -X GET https://scarabpay.com/pay/api/v1/invoices/order-1001 \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": { "id": "inv_8f2k…", "status": "paid", "paid_amount": "20.00", "…": "…" }
}

List invoices

GET/invoices

Newest first. Filter with ?status= and ?order_id=.

curl -X GET https://scarabpay.com/pay/api/v1/invoices?status=paid&per_page=25 \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": [
    { "id": "inv_8f2k…", "status": "paid", "…": "…" }
  ],
  "meta": { "page": 1, "pages": 1, "total": 1 }
}

Cancel an invoice

POST/invoices/{id}/cancel

Stops a pending invoice. Payments that already arrived are still processed.

curl -X POST https://scarabpay.com/pay/api/v1/invoices/inv_8f2k…/cancel \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": { "id": "inv_8f2k…", "status": "cancelled", "…": "…" }
}

Simulate a payment

POST/invoices/{id}/simulatesandbox

Sandbox only. "full" pays the invoice, "partial" makes it underpaid.

FieldDescription
mode
string · optional
"full" (default) or "partial".
curl -X POST https://scarabpay.com/pay/api/v1/invoices/inv_8f2k…/simulate \
  -H "Authorization: Bearer $SCARABPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "full"}'
Response
{
  "data": { "id": "inv_8f2k…", "status": "paid", "sandbox": true, "…": "…" }
}

Static addresses

A permanent deposit address per customer, good for balance top-ups in bots. Every deposit creates a payment and a deposit.received webhook.

Get or create an address

POST/static-addresseslive

Returns 201 for a new address, 200 if this customer already has one for that method.

FieldDescription
customer_ref
string · required
Your customer id. Letters, digits and _ - : . @ only.
method
string · required
Coin and network, e.g. usdt_trc20.
curl -X POST https://scarabpay.com/pay/api/v1/static-addresses \
  -H "Authorization: Bearer $SCARABPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customer_ref": "tg:55123", "method": "usdt_trc20"}'
Response
{
  "data": {
    "customer_ref": "tg:55123",
    "network": "TRX",
    "address": "TQ5n3mGq…",
    "memo": null,
    "total_received": "0.00",
    "last_deposit_at": null,
    "created_at": "2026-10-19T12:00:00+00:00"
  }
}

List addresses

GET/static-addresses

Active addresses, filter with ?customer_ref=.

curl -X GET https://scarabpay.com/pay/api/v1/static-addresses?customer_ref=tg:55123 \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": [
    { "customer_ref": "tg:55123", "network": "TRX", "address": "TQ5n3mGq…", "…": "…" }
  ],
  "meta": { "page": 1, "pages": 1, "total": 1 }
}

Payments

List payments

GET/payments

Every on-chain payment with fee and net amount. Filter with ?customer_ref=.

curl -X GET https://scarabpay.com/pay/api/v1/payments?customer_ref=tg:55123 \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": [
    { "id": "pay_42", "invoice_id": "inv_8f2k…", "order_id": "order-1001", "customer_ref": "tg:55123", "coin": "USDT", "network": "TRX", "coin_amount": "20", "amount": "20.00", "fee": "0.20", "net": "19.80", "tx_hash": "a1b2…", "status": "credited", "created_at": "2026-10-19T12:04:10+00:00" }
  ],
  "meta": { "page": 1, "pages": 1, "total": 1 }
}

Account

Project info

GET/me

The project behind the key, its mode and fee.

curl -X GET https://scarabpay.com/pay/api/v1/me \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": {
    "project": "mp_…",
    "name": "My store",
    "type": "website",
    "target": "https://your-site.com",
    "status": "active",
    "environment": "live",
    "fee_percent": 1
  }
}

Available methods

GET/currencies

Coins and networks you can pass as "method".

curl -X GET https://scarabpay.com/pay/api/v1/currencies \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": [
    { "method": "usdt_trc20", "coin": "USDT", "network": "TRX", "label": "USDT (TRC20)" },
    { "method": "btc", "coin": "BTC", "network": "BTC", "label": "Bitcoin" }
  ]
}

Balance

GET/balance

Your available ScarabPay balance in USD.

curl -X GET https://scarabpay.com/pay/api/v1/balance \
  -H "Authorization: Bearer $SCARABPAY_KEY"
Response
{
  "data": { "currency": "USD", "available": "125.40" }
}
Webhooks

Delivery

Set your webhook URL and copy the signing secret from the Developers tab. We POST a JSON event to your URL on every change. Reply with any 2xx status within 10 seconds.

HeadersDescription
X-ScarabPay-EventEvent name.
X-ScarabPay-DeliveryUnique event id. Use it to ignore duplicates.
X-ScarabPay-SignatureTimestamp and HMAC-SHA256 signature. t=<unix>,v1=<hex>
Request
POST /scarabpay-webhook HTTP/1.1
Host: your-site.com
Content-Type: application/json
X-ScarabPay-Event: invoice.paid
X-ScarabPay-Delivery: evt_3b7d0f1c9e5a2b8d4f6a1c0e
X-ScarabPay-Signature: t=1760875450,v1=5f8a…c21e

{
  "id": "evt_3b7d0f1c9e5a2b8d4f6a1c0e",
  "event": "invoice.paid",
  "project": "mp_…",
  "sandbox": false,
  "created_at": "2026-10-19T12:04:10+00:00",
  "data": { "id": "inv_8f2k…", "order_id": "order-1001", "status": "paid", "paid_amount": "20.00", "…": "…" }
}

Events

EventWhen
invoice.paidInvoice is fully paid. Deliver the order.
invoice.underpaidCustomer paid less than the amount.
invoice.expiredInvoice expired without payment.
invoice.latePayment arrived after the invoice expired.
invoice.updatedAnother payment update on the invoice (e.g. a payment under review).
deposit.receivedA deposit landed on a static address.
address.idleA static address had no deposits for a long time.
address.unboundA static address was released from its customer.
payout.sentA payout from your balance was sent.
project.suspendedYour project was suspended.
pingTest event from the “Send test” button.

Verify signature

Compute HMAC-SHA256 of "t.body" with your webhook secret and compare it to v1 in constant time. Use the raw body exactly as received, and reject timestamps older than 5 minutes.

$secret = getenv('SCARABPAY_WEBHOOK_SECRET');
$body = file_get_contents('php://input');           // raw body
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_SCARABPAY_SIGNATURE'] ?? ''), $sig);

$expected = hash_hmac('sha256', ($sig['t'] ?? '') . '.' . $body, $secret);
if (abs(time() - (int) ($sig['t'] ?? 0)) > 300 || ! hash_equals($expected, $sig['v1'] ?? '')) {
    http_response_code(400);
    exit;
}

$event = json_decode($body, true);
if ($event['event'] === 'invoice.paid') {
    // deliver order $event['data']['order_id'] (once per X-ScarabPay-Delivery)
}
http_response_code(200);

Retries

If your server does not answer with 2xx, we retry up to 8 times: after 1 min, 5 min, 15 min, 1 h, 3 h, 6 h and 12 h. You can also resend any event from the delivery log.

  1. #1+0
  2. #2+1m
  3. #3+5m
  4. #4+15m
  5. #5+1h
  6. #6+3h
  7. #7+6h
  8. #8+12h
Handlers must be idempotent: the same event can arrive more than once. Store X-ScarabPay-Delivery and skip repeats.
Reference

Objects

Invoice
idInvoice id, starts with inv_.
order_idYour unique order id. Letters, digits and _ - : . only, up to 100.
statusSee invoice statuses.
amount · paid_amountUSD received so far.
method · coin · networkChosen coin and network, null until selected.
address · coin_amountDeposit address and exact coin amount to pay.
checkout_urlHosted payment page. Send the customer here.
sandboxtrue for sandbox invoices.
expires_at · paid_at · created_atISO 8601 timestamps.
Payment
idPayment id, starts with pay_.
invoice_id · order_id · customer_refYour customer id, e.g. tg:55123.
coin · network · coin_amountChosen coin and network, null until selected.
amount · fee · netGross USD, our fee and the net credited to you.
tx_hashBlockchain transaction hash.
statuscredited, held, review or sandbox.
Static address
customer_refYour customer id, e.g. tg:55123.
network · addressDeposit address and exact coin amount to pay.
memoMemo / tag when the network needs one (e.g. TON).
total_receivedTotal USD received on this address.
last_deposit_at · created_atISO 8601 timestamps.

Invoice statuses

pending

Waiting for payment.

paid

Fully paid and credited. Deliver the order.

underpaid

Less than the amount arrived. The received part is credited.

expired

Time ran out without payment.

late

Paid after expiry. The payment is held for a check before it is credited.

cancelled

Cancelled by you.

Need help with the integration?

Our support team can review your setup and test events with you.

Contact support