Quickstart
Create your first invoice with payment_currency=any, send the buyer to hosted checkout and receive a signed webhook.
Create your first invoice in one REST call, send the buyer to hosted checkout and receive a signed webhook when the payment confirms.
1. Get your test keys
- Register — email, password, 2FA. No KYC.
- Connect a wallet for the assets you want to accept in Wallets — your keys, your coins.
- Copy the
pk_test_…key from API keys.
Not writing code? Install the ZeroKYC plugin for WooCommerce — crypto payments start working in WordPress without a single API call. We can also set everything up for you: done for you, from $99.
Sandbox invoices are simulated: no blockchain transaction happens, the mock chain confirms them instantly, and the API contract is identical to production — same endpoints, same webhook payloads, same signature scheme. Differences: sandbox payments confirm in seconds (production waits for real network confirmations), and you can simulate a payment instantly from the invoice page in the console (see Sandbox & testing).
The examples below use the production base URL https://api.zerokyc-payments.com — it accepts pk_test_… keys and runs their invoices on the simulated chain.
2. Create an invoice
Use payment_currency: "any" to let the buyer pick the coin at checkout — one invoice covers every enabled asset:
curl -X POST https://api.zerokyc-payments.com/v1/invoices \
-H "Authorization: Bearer pk_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042" \
-d '{
"amount": "49.00",
"base_currency": "USD",
"payment_currency": "any",
"order_id": "order-1042",
"ttl_minutes": 360,
"success_url": "https://yourapp.com/paid",
"webhook_url": "https://yourapp.com/webhooks/zkp",
"metadata": { "customer_id": "cust_8821" }
}'
The response contains one payment option per enabled asset, plus a hosted checkout URL:
{
"id": "inv_9f2k...",
"status": "pending",
"order_id": "order-1042",
"amount": "49.00",
"base_currency": "USD",
"expires_at": "2026-09-06T18:20:11Z",
"checkout_url": "https://app.zerokyc-payments.com/pay/inv_9f2k...",
"options": [
{ "asset": "USDT", "network": "tron", "amount_crypto": "49.00", "rate": "1.0", "status": "open" },
{ "asset": "BTC", "network": "bitcoin", "amount_crypto": "0.000524", "rate": "93457.91", "status": "open" },
{ "asset": "TON", "network": "ton", "amount_crypto": "9.81", "rate": "4.99", "status": "open" },
{ "asset": "ETH", "network": "ethereum", "amount_crypto": "0.0101", "rate": "2970.29", "status": "open" },
{ "asset": "BNB", "network": "bsc", "amount_crypto": "0.0815", "rate": "601.22", "status": "open" }
]
}
Notes:
amountis fiat (USD, EUR or RUB) or the asset itself ("base_currency": "BTC").- Send an
Idempotency-Keyheader: retrying the same creation returns the same invoice instead of a duplicate (details). ttl_minutesdefaults to 360 (6 hours); the allowed range is 10 minutes to 72 hours.metadatais echoed back in the invoice and in every webhook — put your customer or order reference there.- On validation failures the API answers with a uniform error envelope, e.g.
{"error": {"code": "invalid_request", "message": "ttl_minutes must be between 10 and 4320", "doc_url": "..."}}.
Every field of the request and response is covered in the Invoices API reference.
3. Send the buyer to checkout
Redirect to checkout_url. The hosted page shows every option with its crypto amount, the address, a QR code and a live TTL timer; it polls status every 5 seconds. White-label and RU/EN locales are built in.
4. Receive the webhook
When the payment confirms you get a payment.confirmed POST:
{
"id": "evt_5c1a...",
"type": "payment.confirmed",
"created_at": "2026-09-06T14:02:33Z",
"data": {
"invoice_id": "inv_9f2k...",
"order_id": "order-1042",
"amount": "49.00",
"base_currency": "USD",
"option": { "asset": "TON", "network": "ton", "amount_crypto": "9.81", "paid_amount": "9.81" },
"tx": { "hash": "9d24...", "confirmations": 1, "explorer_url": "https://tonviewer.com/transaction/9d24...", "output_index": 0 },
"metadata": { "customer_id": "cust_8821" }
}
}
Respond with any 2xx within 10 seconds. Every delivery is signed — verify it before trusting the payload (next step); failed deliveries retry on a 1m → 5m → 30m → 2h → 12h ladder (delivery semantics).
5. Verify the signature
The timestamp and the signature travel together in one header, X-ZKP-Signature: t=<unix seconds>,v1=<hex>; the HMAC is computed over "{t}.{raw_body}":
import { createHmac, timingSafeEqual } from "node:crypto";
// X-ZKP-Signature: t=1694007753,v1=ade537fa...
const [t, v1] = Object.fromEntries(
req.headers["x-zkp-signature"].split(",").map((p) => p.trim().split("=")),
);
const expected = createHmac("sha256", WEBHOOK_SECRET)
.update(`${t}.${req.rawBody}`)
.digest("hex");
const ok = timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(v1, "hex"));
if (!ok || Math.abs(Date.now() / 1000 - Number(t)) > 300) return res.status(400).end();
Full snippets for Node, PHP and Python: HMAC verification.
What's next
- Integration overview — the full step-by-step setup walkthrough: keys, first invoice, checkout, webhooks, HMAC, going live.
- Invoices API — every endpoint and field: list, get, cancel, QR codes.
- Invoice lifecycle — statuses, detection and what fires which webhook.
- Webhooks reference — every event, the payload contract and retry semantics.
- Errors and Rate limits & idempotency — building a resilient client.
- Direct checkout guide — connect your own wallet non-custodially.