API Reference · v1
Crypto Invoice API
A simple REST API to create invoices, accept crypto (USDT, BTC, ETH, LTC, BNB, Binance Pay…) and get notified when a payment lands. JSON in, JSON out. Works from any language.
Introduction
The Crypto Invoice API lets you generate invoices programmatically and accept any supported cryptocurrency. Each invoice gets a hosted pay page (mobile-first), a live status (pending → paid), an automatic price conversion (e.g. a USDT invoice can be paid in LTC at live rate) and an optionalsuccess_redirect_url that sends the buyer back to your site 3 seconds after payment.
Quickstart
- Create an account on Crypto Invoice.
- Add a wallet under Wallets (one per currency/network).
- Connect your Binance API under API so payments can be verified.
- Create a platform API key under API → Platform keys. Save the key and secret — the secret is shown only once.
- Create your first invoice with the call below.
curl -X POST https://pay.itemodeverify.com/api/public/v1/invoices \
-H "Authorization: Bearer ci_xxxxxxxxxxxx:sk_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 25,
"currency": "USDT",
"description": "Pro plan",
"success_redirect_url": "https://yoursite.com/thanks"
}'Authentication
All API requests are authenticated with a key and a secret. Send them either as a Bearer token (recommended) or as two custom headers.
# Recommended: Bearer "<key>:<secret>"
-H "Authorization: Bearer ci_xxxxxxxxxxxx:sk_xxxxxxxxxxxx"
# Alternative: separate headers
-H "x-api-key: ci_xxxxxxxxxxxx"
-H "x-api-secret: sk_xxxxxxxxxxxx"Never expose your secret in client-side code. Treat it like a password.
Base URL
All endpoints live under /api/public/v1/. Responses are always JSON.
Invoices
The Invoice object:
{
"id": "7b285a28-c45b-496b-831f-b84c11ca7630",
"amount": 25,
"currency": "USDT",
"status": "pending", // pending | paid | expired | cancelled
"description": "Pro plan",
"customer_email": null,
"expires_at": "2026-05-21T10:00:00Z",
"paid_at": null,
"paid_amount": null,
"tx_hash": null,
"wallet_id": null,
"tolerance_pct": 2,
"success_redirect_url": "https://yoursite.com/thanks",
"source": "api",
"created_at": "2026-05-20T09:02:18Z",
"pay_url": "https://pay.itemodeverify.com/pay/7b285a28-c45b-496b-831f-b84c11ca7630"
}Create an invoice
| Field | Type | Description |
|---|---|---|
| amount* | number | Amount due, in the invoice currency. |
| currency* | string | USDT, USDC, BTC, ETH, LTC, BNB, SOL, TRX or BINANCE_PAY. |
| description | string | Shown to the buyer on the pay page. |
| customer_email | string | Customer email (optional). |
| wallet_id | uuid | Force a specific wallet. Otherwise the buyer can pick any enabled wallet. |
| expires_at | ISO date | Absolute expiry time. |
| expires_in_minutes | integer | Shortcut: expire N minutes from now. |
| tolerance_pct | number 0-10 | Accepted over/under in % (default 2). Cross-currency uses min 3%. |
| success_redirect_url | url | Buyer is auto-redirected here 3s after payment is confirmed. |
curl -X POST https://pay.itemodeverify.com/api/public/v1/invoices \
-H "Authorization: Bearer $CI_KEY:$CI_SECRET" \
-H "Content-Type: application/json" \
-d '{
"amount": 49.99,
"currency": "USDT",
"description": "Order #1234",
"customer_email": "buyer@example.com",
"expires_in_minutes": 60,
"success_redirect_url": "https://shop.example.com/order/1234/done"
}'Returns 201 Created with the full Invoice object.
Retrieve an invoice
curl https://pay.itemodeverify.com/api/public/v1/invoices/7b285a28-c45b-496b-831f-b84c11ca7630 \
-H "Authorization: Bearer $CI_KEY:$CI_SECRET"List invoices
| Field | Type | Description |
|---|---|---|
| status | string | Filter: pending | paid | expired | cancelled |
| limit | integer | Max 200 (default 50) |
curl "https://pay.itemodeverify.com/api/public/v1/invoices?status=paid&limit=20" \
-H "Authorization: Bearer $CI_KEY:$CI_SECRET"Response shape: { "data": Invoice[] }
Cancel an invoice
curl -X POST https://pay.itemodeverify.com/api/public/v1/invoices/$INVOICE_ID/cancel \
-H "Authorization: Bearer $CI_KEY:$CI_SECRET"Returns 409 if the invoice is already paid, expired, or cancelled.
Hosted pay page
Every invoice exposes a buyer-facing pay page at:
It auto-detects the merchant branding (logo, colors), shows all enabled payment methods with QR + address, checks for the payment every 5 seconds, and — if you set success_redirect_url — sends the buyer back to your site 3 seconds after the payment is confirmed.
Success redirect (after-payment)
Pass a success_redirect_url when you create the invoice and the buyer will be automatically forwarded to that URL exactly 3 seconds after the payment is confirmed on the hosted pay page. The 3-second delay lets the buyer see the green "Payment received" confirmation before leaving.
The redirect URL must be absolute (https://…). We append two query parameters so your landing page can identify the order: ?invoice_id=<id>&status=paid.
1. Create the invoice with a redirect URL
curl -X POST https://pay.itemodeverify.com/api/public/v1/invoices \
-H "Authorization: Bearer $CI_KEY:$CI_SECRET" \
-H "Content-Type: application/json" \
-d '{
"amount": 19.99,
"currency": "USDT",
"description": "Order #A-1029",
"success_redirect_url": "https://shop.example.com/thank-you?order=A-1029"
}'2. Handle the redirect on your thank-you page
When the buyer lands on your URL, read the query string to identify the order. For security, never trust the URL alone — confirm the invoice status server-side (either by hitting GET /api/public/v1/invoices/{id} or by waiting for the webhook described below) before granting access to paid content.
// pages/thank-you.js (Next.js / Remix / vanilla — same idea)
const url = new URL(window.location.href);
const invoiceId = url.searchParams.get("invoice_id");
const status = url.searchParams.get("status"); // "paid"
// Verify server-side before unlocking the order
const res = await fetch(`/api/verify-order?invoice_id=${invoiceId}`);
const { paid } = await res.json();
document.querySelector("#status").textContent =
paid ? "Thanks! Your order is confirmed." : "Still confirming your payment…";Tips
- The 3-second delay is fixed. The buyer always sees the "Payment received" screen first.
- If the buyer closes the tab before the redirect, the invoice stays
paid— fulfil the order from the webhook, not the redirect. - If you omit
success_redirect_url, the pay page simply shows the success screen and stays put. - The URL must be HTTPS in production. Local URLs (
http://localhost) work for testing. - You can change the URL per-invoice — perfect for routing buyers to the right order page.
Webhooks
Add a webhook URL under API → Webhook. We POST a JSON body and an X-Signatureheader (HMAC-SHA256 of the body using your webhook secret). Verify the signature before trusting the payload.
// Webhook payload
{
"event": "invoice.paid",
"invoice_id": "7b285a28-c45b-496b-831f-b84c11ca7630",
"amount": "0.00186",
"tx": "373310330720"
}
// Header
X-Signature: 5f8c… // hex HMAC-SHA256(body, webhook_secret)import crypto from "node:crypto";
app.post("/webhooks/crypto-invoice", express.raw({ type: "*/*" }), (req, res) => {
const sig = req.headers["x-signature"];
const expected = crypto.createHmac("sha256", process.env.CI_WEBHOOK_SECRET)
.update(req.body).digest("hex");
if (sig !== expected) return res.status(401).end();
const event = JSON.parse(req.body.toString());
// mark order as paid…
res.status(200).end();
});IPN delivery & timing
Payments are detected by our verifier, which polls Binance for new deposits every 60 seconds. As soon as a matching transaction is found, the invoice flips to paid and we immediately POST invoice.paid to your webhook URL and fire your Telegram notification. End-to-end latency is typically 30–90 seconds from network confirmation.
| Field | Type | Description |
|---|---|---|
| invoice.paid | event | Invoice fully matched on-chain (or via Binance Pay). Sent once. |
| invoice.created | event | A new invoice was created via API or dashboard. |
| invoice.cancelled | event | Invoice cancelled by you or the buyer. |
| invoice.expired | event | Invoice passed its expires_at without payment. |
| ping | event | Test delivery sent from API → Webhook → Send test. |
Test it: in API → Webhook, click Send test to receive a ping. To replay a real event, open any paid invoice and click Resend webhook. All attempts (status code + response body) are stored in Webhook logs.
Retries: we currently deliver each event once. If your endpoint returns a non-2xx, the failure is logged and a webhook.failed Telegram alert is sent — use the logs to resend manually. Your endpoint should respond with 200 within ~10 seconds and process asynchronously.
Errors
Errors return a standard JSON envelope:
{ "error": { "code": "invalid_input", "message": "amount: must be positive" } }| Field | Type | Description |
|---|---|---|
| 400 | invalid_json / invalid_input / invalid_wallet | The request body or query was rejected. |
| 401 | missing_credentials / invalid_key / invalid_secret | Authentication failed. |
| 404 | not_found | The resource doesn't exist. |
| 409 | not_cancellable | Action not allowed for the resource's current state. |
| 500 | db_error | Something went wrong on our end. |
Full example: charge a customer end-to-end
A complete Node.js flow: create an invoice, send the buyer to the pay page, and listen for the webhook to fulfil the order.
import express from "express";
import crypto from "node:crypto";
const app = express();
const CI_KEY = process.env.CI_KEY;
const CI_SECRET = process.env.CI_SECRET;
const CI_WEBHOOK_SECRET = process.env.CI_WEBHOOK_SECRET;
// 1. Create an invoice when the user clicks "Pay"
app.post("/checkout", express.json(), async (req, res) => {
const { orderId, amountUsd, email } = req.body;
const r = await fetch("https://pay.itemodeverify.com/api/public/v1/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${CI_KEY}:${CI_SECRET}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: amountUsd,
currency: "USDT",
description: `Order #${orderId}`,
customer_email: email,
expires_in_minutes: 30,
success_redirect_url: `https://shop.example.com/orders/${orderId}/thanks`,
}),
});
const invoice = await r.json();
// Save invoice.id against your order, then redirect the user
await db.orders.update(orderId, { invoiceId: invoice.id });
res.redirect(invoice.pay_url); // https://pay.itemodeverify.com/pay/<id>
});
// 2. Receive the webhook when the payment lands
app.post("/webhooks/crypto-invoice",
express.raw({ type: "*/*" }),
async (req, res) => {
const sig = req.headers["x-signature"];
const expected = crypto
.createHmac("sha256", CI_WEBHOOK_SECRET)
.update(req.body).digest("hex");
if (sig !== expected) return res.status(401).end();
const event = JSON.parse(req.body.toString());
if (event.event === "invoice.paid") {
const order = await db.orders.findOne({ invoiceId: event.invoice_id });
await db.orders.update(order.id, { status: "paid", txHash: event.tx });
// → fulfil the order, send a receipt, etc.
}
res.status(200).end();
}
);
app.listen(3000);https://pay.itemodeverify.com