Cryptain API

Create invoices, take payments on 13 blockchains and get a signed webhook the moment money arrives. JSON over HTTPS, amounts as strings, the same API our plugins use.

Base URLhttps://cryptain.net/api/v1

Quick start

  1. Create a store. Sign up, add your website under Stores and wait for approval (usually within a business day).
  2. Make an API key. Open the store, create a key and copy the secret key straight away. It is shown once.
  3. Set your webhook. Paste your server's webhook address into the store and copy its webhook secret.
  4. Create an invoice and send the customer to its checkout_url. When the payment confirms we call your webhook.

All amounts are decimal strings such as "49.00", never floats, so nothing is lost to rounding.

BODY='{"amount":"49.00","currency":"USD","order_id":"1024"}'
TS=$(date +%s)
SIG=$(printf '%s\nPOST\n/api/v1/invoices\n%s' "$TS" "$BODY" \
  | openssl dgst -sha256 -hmac "$CRYPTAIN_SECRET" | cut -d' ' -f2)

curl https://cryptain.net/api/v1/invoices \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $CRYPTAIN_KEY" -H "X-Timestamp: $TS" \
  -H "X-Signature-Version: 2" -H "X-Signature: $SIG" \
  -d "$BODY"

Authentication

Every request carries four headers. Nothing else is needed: no OAuth, no sessions.

X-Api-KeyYour public key, pk_…
X-TimestampUnix time in seconds, within 5 minutes of our clock
X-Signature-Version2
X-SignatureHex HMAC-SHA256 of the string on the right, keyed with your secret key

The signed string is four parts joined by a single newline: the timestamp, the method in capitals, the path with its query string exactly as sent, and the raw body (an empty string for GET).

Keys can be read-only for reporting tools and can be limited to a list of server IP addresses. Keys made before version 2 signatures still work with HMAC(timestamp + body); tick v2 signatures only on a key once your integration is updated. All Cryptain plugins and SDKs already sign with version 2.

String to sign
1767225600
GET
/api/v1/invoices?status=paid&limit=10

SDKs and plugins

Official libraries sign requests and verify webhooks for you. Each is a single file with no dependencies.

Running a store? Use a ready-made plugin for WooCommerce, WHMCS, Magento, PrestaShop, XenForo and more from the integrations page.

require 'Cryptain.php';

$cryptain = new Cryptain('https://cryptain.net/', getenv('CRYPTAIN_KEY'), getenv('CRYPTAIN_SECRET'));
$invoice  = $cryptain->createInvoice(['amount' => '49.00', 'currency' => 'USD', 'order_id' => '1024']);

// in your webhook handler
$event = Cryptain::verifyWebhook(file_get_contents('php://input'), $_SERVER, getenv('CRYPTAIN_WEBHOOK_SECRET'));
if ($event && Cryptain::isPaid($event['data'])) { /* mark the order paid */ }

Create an invoice

POST/invoices

Returns 201 with the invoice. Send the customer to checkout_url; they pick a coin there unless you set pay_currency.

amountrequiredPrice as a string, e.g. "49.00"
currencyFiat currency such as USD, EUR or INR. Defaults to the store's currency.
order_idYour order reference, up to 120 characters
descriptionShown to the customer on the checkout
customer_emailWe email a receipt when the invoice is paid
pay_currencySkip the coin picker, e.g. USDT_TRC20 (see codes)
success_url, cancel_urlWhere the customer's "Back to store" button goes
callback_urlWebhook address for this invoice only
metadataAny JSON up to 4 KB, returned in every webhook

Retries are safe. Send an Idempotency-Key header and repeating the request returns the same invoice instead of creating a second one.

HTTP/1.1 201 Created

{
  "data": {
    "id": "5b1f0e8a-…",
    "order_id": "1024",
    "status": "new",
    "amount": "49.00",
    "currency": "USD",
    "checkout_url": "https://cryptain.net/pay/5b1f0e8a-…",
    "expires_at": "2026-10-11T12:30:00Z",
    "created_at": "2026-10-11T12:00:00Z"
  }
}

Get and list invoices

GET/invoices/{id}

GET/invoices

The list is newest first, for the store the key belongs to. Filter with status and order_id, page with page and limit (up to 100). The response includes meta.total.

Your webhook handler should read the invoice back with this call before shipping, so a forged request can never mark an order paid.

TS=$(date +%s)
SIG=$(printf '%s\nGET\n/api/v1/invoices?status=paid&limit=10\n%s' "$TS" "" \
  | openssl dgst -sha256 -hmac "$CRYPTAIN_SECRET" | cut -d' ' -f2)

curl "https://cryptain.net/api/v1/invoices?status=paid&limit=10" \
  -H "X-Api-Key: $CRYPTAIN_KEY" -H "X-Timestamp: $TS" \
  -H "X-Signature-Version: 2" -H "X-Signature: $SIG"

Cancel an invoice

POST/invoices/{id}/cancel

Cancels an invoice that has not been paid; the body can be empty. Paid or partly paid invoices return 409 cannot_cancel. A payment that still arrives later is recorded and reported, never lost.

{ "data": { "id": "5b1f…", "status": "cancelled", … } }

Invoice object

Once the customer picks a coin the invoice carries the locked rate, the exact crypto amount and a deposit address reserved for this invoice alone.

pay_amountCrypto amount to send
paid_amountCrypto received so far
fee_amountOur fee, in the paid coin
merchant_amountWhat is credited to you
memoDestination tag or memo on networks that need one (XRP, TON)
transactionsEach blockchain transfer with hash, amount and status
{
  "id": "5b1f…", "store_id": "…", "order_id": "1024", "status": "paid",
  "amount": "49.00", "currency": "USD",
  "pay_currency": "USDT_TRC20", "pay_symbol": "USDT", "network": "TRON",
  "pay_amount": "49.00", "paid_amount": "49.00", "rate": "1.0000",
  "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE", "memo": null,
  "fee_amount": "0.49", "merchant_amount": "48.51", "is_late": false,
  "checkout_url": "https://cryptain.net/pay/5b1f…",
  "transactions": [
    { "tx_hash": "9f2c…", "amount": "49.00", "status": "credited", "created_at": "…" }
  ],
  "metadata": { "cart": 88 },
  "expires_at": "…", "paid_at": "…", "created_at": "…"
}

Statuses

newCreated; the customer has not picked a coin yet.
waitingCoin chosen, rate locked, waiting for the payment.
partially paidLess than the amount arrived. The customer can send the rest before the invoice expires.
paidPaid in full, within the store's underpayment tolerance. Deliver the order.
paid overMore than the amount arrived. Treat as paid.
paid latePaid in full after expiry. Treat as paid; the rate may have moved.
expiredTime ran out before full payment. paid_amount shows any partial payment.
cancelledCancelled by you before payment.
refundedYou sent the money back to the customer.

Webhooks

We POST JSON to your store's webhook address whenever an invoice changes. Reply with any 2xx. Failed deliveries retry for 24 hours (after 1 min, 5 min, 15 min, 1 h, 3 h, 6 h, 12 h and 24 h), and you can resend any delivery from the dashboard.

invoice.paidPaid in full, including late payments
invoice.partially_paidPart of the amount arrived
invoice.overpaidMore than the amount arrived
invoice.expiredTime ran out
invoice.cancelledYou cancelled it
invoice.refundedA refund you approved was sent
testSent by the Send test webhook button

Check X-Cryptain-Signature: hex HMAC-SHA256 of timestamp + "." + raw_body keyed with the webhook secret. Reject timestamps older than five minutes, and make your handler idempotent; the same event can arrive twice.

POST /your-webhook HTTP/1.1
Content-Type: application/json
X-Cryptain-Event: invoice.paid
X-Cryptain-Timestamp: 1767225600
X-Cryptain-Signature: 3f9a…

{
  "id": "evt_…",
  "event": "invoice.paid",
  "created_at": "2026-10-11T12:04:10Z",
  "data": { …invoice object… }
}

Recurring invoices

POST/subscriptions

Crypto wallets cannot be charged automatically, so we email the customer a payment link every period. Fields: customer_email, description, amount, and optionally currency, period (weekly, monthly, quarterly, yearly), pay_days (1–30), max_cycles, start (YYYY-MM-DD), customer_name and locale.

POST/subscriptions/{id}/{action}

Actions: pause, resume, cancel and send (bill now). GET /subscriptions/{id} includes a customer_portal_url where the customer sees every invoice. Paid cycles send invoice.paid with metadata.subscription_id.

Request body
{
  "customer_email": "ana@example.com",
  "description": "Pro plan",
  "amount": "29.00",
  "currency": "USD",
  "period": "monthly",
  "pay_days": 7
}

Balances and reporting

GET/balance

GET/withdrawals

GET/stats?from=2026-10-01&to=2026-10-31

Balances per coin, withdrawals, refunds and payouts with their transaction hashes, and a summary of invoices, volume and fees for any date range. Give accounting tools a read-only key.

{
  "data": [
    { "currency": "USDT_TRC20", "symbol": "USDT", "available": "1227.60", "pending_withdrawal": "0" },
    { "currency": "BTC", "symbol": "BTC", "available": "0.01840000", "pending_withdrawal": "0" }
  ]
}

Coins and rates

GET/currencies

GET/rates?currency=USD

GET/ping

The coins your store accepts, the current price of one coin in a fiat currency, and a quick check that your keys and signature are right.

GET /ping
{
  "ok": true,
  "store": { "id": "…", "name": "Northwind Supply", "status": "active" }
}

Pay button and embed

Put a "Pay with crypto" button on any page with two lines of HTML. With Allow the checkout to open inside my website switched on in the store's branding, it opens as a pop-up; otherwise it goes to the payment page.

Use data-invoice="CHECKOUT-URL" for an invoice your server made, data-mode="redirect" to always leave the page, and data-success-url to continue after payment. The element fires cryptain:paid and cryptain:close.

To embed the checkout yourself, load CHECKOUT-URL?embed=1 in an iframe. It posts {source: "cryptain", type, invoice, status} messages with types paid, closed, status and resize.

<script src="https://cryptain.net/assets/js/button.js" async></script>
<div class="cryptain-button"
     data-link="PAYMENT-LINK-ID"
     data-label="Pay with crypto"
     data-color="#d6b25e"></div>

Testing

  • Testnet. While the platform runs in testnet mode, checkouts use test networks and test coins, so you can pay invoices without real money.
  • Test webhook. Press Send test webhook on your store to receive a signed test event and see the delivery log.
  • Postman. Import the collection, fill in public_key and secret_key, and every request is signed for you.
  • OpenAPI. Generate a client in any language from the OpenAPI 3.1 file.
Check your keys
TS=$(date +%s)
SIG=$(printf '%s\nGET\n/api/v1/ping\n%s' "$TS" "" \
  | openssl dgst -sha256 -hmac "$CRYPTAIN_SECRET" | cut -d' ' -f2)

curl https://cryptain.net/api/v1/ping \
  -H "X-Api-Key: $CRYPTAIN_KEY" -H "X-Timestamp: $TS" \
  -H "X-Signature-Version: 2" -H "X-Signature: $SIG"

Errors

Errors come back with an HTTP status and a stable code; the message says what to fix.

401missing_auth, timestamp_expired, invalid_api_key, invalid_signature, signature_version_required
403account_inactive, insufficient_scope (read-only key), ip_not_allowed
404not_found
409cannot_cancel
422validation_error
429rate_limited: more than 120 requests a minute per key
HTTP/1.1 422 Unprocessable Entity

{
  "error": {
    "code": "validation_error",
    "message": "amount must be a positive number like 25 or 19.99"
  }
}

Currency codes

Use these in pay_currency. Your store accepts the ones you switch on.

BTC
BTCBitcoin, Bitcoin
ETH
ETHEthereum, Ethereum (ERC20)
USDT
USDT_TRC20Tether (TRC20), Tron (TRC20)
USDT
USDT_ERC20Tether (ERC20), Ethereum (ERC20)
USDT
USDT_BEP20Tether (BEP20), BNB Smart Chain (BEP20)
USDT
USDT_POLYGONTether (Polygon), Polygon
USDC
USDC_ERC20USD Coin (ERC20), Ethereum (ERC20)
USDC
USDC_BEP20USD Coin (BEP20), BNB Smart Chain (BEP20)
USDC
USDC_POLYGONUSD Coin (Polygon), Polygon
BNB
BNBBNB, BNB Smart Chain (BEP20)
POL
POLPolygon, Polygon
LTC
LTCLitecoin, Litecoin
DOGE
DOGEDogecoin, Dogecoin
SOL
SOLSolana, Solana
USDT
USDT_SOLTether (Solana), Solana
USDC
USDC_SOLUSD Coin (Solana), Solana
BCH
BCHBitcoin Cash, Bitcoin Cash
XRP
XRPXRP, XRP Ledger
TON
TONToncoin, TON
USDT
USDT_TONTether (TON), TON
ETH
ETH_ARBEthereum (Arbitrum), Arbitrum One
USDC
USDC_ARBUSD Coin (Arbitrum), Arbitrum One
USDT
USDT_ARBTether (Arbitrum), Arbitrum One
ETH
ETH_BASEEthereum (Base), Base
USDC
USDC_BASEUSD Coin (Base), Base

Open an account in two minutes

Free to start. 1% per payment. Upgrade any time.