API Reference
chcks API
REST API for accepting crypto payments. Base URL: https://chcks.co/api
Overview
All responses are JSON. All request bodies must be application/json. The API has three groups:
Invoices API
API Key
Hosted checkout for online stores. Create an invoice, redirect your customer, receive a webhook.
Public API
None
No auth. For custom storefronts, kiosks, and mobile apps built on a chcks catalog.
Management API
API Key
Read and write your merchant profile, product catalog, and orders.
5 minutes to first payment
Quick Start
Add crypto checkout to an existing product using the Gateway API. Three steps: create a payment, redirect the customer, handle the webhook.
1. Create a payment
Call this from your server when the customer initiates checkout. Returns a payment_url to redirect to.
POST https://chcks.co/api/v1/invoices
Authorization: Bearer csr_your_key_here
Content-Type: application/json
{
"amount": "29.99",
"currency": "USDT",
"order_id": "order_abc123",
"webhook_url": "https://yourapp.com/webhooks/chcks",
"webhook_secret": "your_webhook_secret",
"description": "Pro plan — monthly"
}{
"uuid": "pay_k7x9m2p4",
"amount": "29.99",
"currency": "USDT",
"status": "pending",
"payment_url": "https://chcks.co/gateway/pay_k7x9m2p4",
"address": "UQBxxxxxxxxxxx",
"memo": "83741926",
"deeplinks": {
"ton": "ton://transfer/UQBxx...?amount=29990000&text=83741926",
"tonkeeper": "tonkeeper://transfer/..."
},
"expires_at": "2026-01-01T12:30:00.000Z",
"created_at": "2026-01-01T12:00:00.000Z"
}2. Redirect the customer
Redirect your user to payment_url. The chcks checkout page handles currency selection, QR display, and confirmation polling automatically.
// Node.js / Express example res.redirect(paymentData.payment_url);
3. Receive the webhook
When the blockchain confirms the payment, chcks POSTs to your webhook_url. Always verify the signature before fulfilling the order.
import crypto from "crypto";
app.post("/webhooks/chcks", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["x-signature"] as string;
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET!)
.update(req.body)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send("Invalid signature");
}
const { event, payment } = JSON.parse(req.body.toString());
if (event === "payment.confirmed") {
// fulfill the order using payment.external_order_id
await fulfillOrder(payment.external_order_id);
}
res.json({ ok: true });
});Authentication
Gateway and Management endpoints require a Bearer token. Public endpoints need no auth.
Authorization: Bearer csr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keep it server-side
csr_... key grants full write access to your account. Never embed it in browser JavaScript, mobile apps, or client-side code. Treat it like a database password. Generate a new one from Settings → API Key if it is ever exposed.Errors & Status Codes
All errors return { message: string } in the response body.
400Bad RequestMissing or invalid parameter in the request body or path.401UnauthorizedAPI key missing, malformed, or not matching any merchant.403ForbiddenThe resource exists but belongs to a different merchant.404Not FoundResource does not exist.409ConflictOperation not valid for the resource's current state (e.g. payment no longer pending).422UnprocessablePrecondition failed — e.g. merchant has no wallet address configured.502Exchange ErrorUpstream exchange (FixedFloat) failed to create the order. Retry or choose a different currency.500Internal Server ErrorUnexpected error. Contact support if it persists.Requires API Key
Gateway Payments
Create hosted checkout links that handle payment collection for you. Customers can pay in TON or USDT natively, or in 30+ other chains via an exchange bridge (BTC, ETH, SOL, BNB, etc.). You always settle in your configured currency (TON or USDT).
/api/v1/invoicesAPI Keyamountstring|number✓Amount in your settlement currency. Use a decimal string for precision, e.g. "29.99".currency"TON"|"USDT"✓Settlement currency — what lands in your wallet after the payment.from_currencystring—Customer payment currency. Omit for TON/USDT native. Set to "BTC", "ETH", "SOL", etc. to route through the exchange bridge.order_idstring—Your internal order reference. Returned as external_order_id in all responses and webhook.webhook_urlstring—HTTPS URL to receive the payment.confirmed event. Must start with https://.webhook_secretstring—Secret for HMAC-SHA256 signature verification on your webhook endpoint.descriptionstring—Human-readable description shown on the checkout page.{
"uuid": "pay_k7x9m2p4",
"external_order_id": "order_abc123",
"amount": "29.99",
"currency": "USDT",
"status": "pending",
"description": "Pro plan — monthly",
"payment_url": "https://chcks.co/gateway/pay_k7x9m2p4",
"address": "UQBxxxxxxxxxxx", // destination wallet
"memo": "83741926", // unique memo for matching
"deeplinks": {
"ton": "ton://transfer/...",
"tonkeeper": "tonkeeper://..."
},
"deposit_address": null, // null on native path
"from_currency": null,
"from_amount": null,
"expires_at": "2026-01-01T12:30:00.000Z",
"created_at": "2026-01-01T12:00:00.000Z"
}{
"uuid": "pay_j3r8n5w1",
"external_order_id": "order_abc123",
"amount": "29.99",
"currency": "USDT",
"status": "pending",
"payment_url": "https://chcks.co/gateway/pay_j3r8n5w1",
"deposit_address": "bc1qxxxxxxxxx", // customer sends BTC here
"from_currency": "BTC",
"from_amount": "0.00031", // exact BTC amount to send
"deeplinks": null, // no deeplinks on exchange path
"address": null,
"memo": null,
"expires_at": "2026-01-01T12:30:00.000Z",
"created_at": "2026-01-01T12:00:00.000Z"
}/api/v1/invoices/:uuidAPI Key{
"uuid": "pay_k7x9m2p4",
"external_order_id": "order_abc123",
"amount": "29.99",
"currency": "USDT",
"status": "confirmed", // pending | confirmed | expired
"description": "Pro plan — monthly",
"payment_url": "https://chcks.co/gateway/pay_k7x9m2p4",
"address": "UQBxxxxxxxxxxx",
"memo": "83741926",
"from_currency": null,
"from_amount": null,
"deposit_address": null,
"tx_hash": "a1b2c3d4...", // null until confirmed
"confirmed_at": "2026-01-01T12:04:17.000Z",
"expires_at": "2026-01-01T12:30:00.000Z",
"created_at": "2026-01-01T12:00:00.000Z",
"webhook_url": "https://yourapp.com/webhooks/chcks",
"webhook_delivered_at": "2026-01-01T12:04:18.000Z",
"webhook_attempts": 1
}/api/v1/invoices/:uuid/checkAPI Key// Confirmed
{ "status": "confirmed", "tx_hash": "a1b2c3d4..." }
// Still pending
{ "status": "pending", "tx_hash": null }
// Expired
{ "status": "expired", "tx_hash": null }/api/v1/invoices/chooseAPI Keyuuidstring✓UUID of the current pending payment.fromCurrencystring✓Target currency for the customer to pay in (e.g. "ETH", "BTC", "TON", "USDT").{ "uuid": "pay_n4q6t8v2" } // new payment UUID — use this going forward409 { "message": "Payment is no longer pending" }
404 { "message": "Payment not found" }
400 { "message": "Unsupported currency" }/api/v1/invoicesAPI Keylimitinteger20Max results to return. Capped at 100.offsetinteger0Number of records to skip for pagination.{
"data": [
{
"uuid": "pay_k7x9m2p4",
"external_order_id": "order_abc123",
"amount": "29.99",
"currency": "USDT",
"status": "confirmed",
"description": "Pro plan — monthly",
"from_currency": null,
"from_amount": null,
"deposit_address": null,
"payment_url": "https://chcks.co/gateway/pay_k7x9m2p4",
"tx_hash": "a1b2c3d4...",
"confirmed_at": "2026-01-01T12:04:17.000Z",
"expires_at": "2026-01-01T12:30:00.000Z",
"created_at": "2026-01-01T12:00:00.000Z",
"webhook_url": "https://yourapp.com/webhooks/chcks",
"webhook_delivered_at": "2026-01-01T12:04:18.000Z",
"webhook_attempts": 1
}
],
"limit": 20,
"offset": 0
}Webhooks
When a payment is confirmed on-chain, chcks makes a POST request to your webhook_url. Webhook delivery is attempted immediately after confirmation and retried on failure.
POST https://yourapp.com/webhooks/chcks
Content-Type: application/json
X-Signature: sha256=a3f1b2c4...
{
"event": "payment.confirmed",
"payment": {
"uuid": "pay_k7x9m2p4",
"external_order_id": "order_abc123",
"amount": "29.99",
"currency": "USDT",
"tx_hash": "a1b2c3d4...",
"confirmed_at": "2026-01-01T12:04:17.000Z"
}
}The X-Signature header is sha256= followed by the HMAC-SHA256 of the raw request body using your webhook_secret. Read the body as raw bytes before parsing JSON.
import crypto from "crypto";
import express from "express";
app.post("/webhooks/chcks",
express.raw({ type: "application/json" }),
(req, res) => {
const sig = req.headers["x-signature"] as string;
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET!)
.update(req.body) // raw Buffer, not parsed string
.digest("hex");
if (!crypto.timingSafeEqual(
Buffer.from(sig),
Buffer.from(expected)
)) {
return res.status(401).json({ error: "Invalid signature" });
}
const { event, payment } = JSON.parse(req.body.toString());
if (event === "payment.confirmed") {
await fulfillOrder(payment.external_order_id);
}
res.json({ ok: true });
}
);Polling fallback
POST /api/v1/invoices/:uuid/check to manually verify and GET /api/v1/invoices/:uuid to retrieve the confirmed tx_hash.No authentication required
Public API
Build a custom ordering experience on top of a chcks merchant catalog. No API key needed — these endpoints are safe to call from browsers and mobile apps.
/api/public/ordersPublicslugstring✓Merchant slug from GET /api/menu/:slug.itemsarray✓Array of { productId: number, quantity: number }. Empty arrays return 400.tableIdnumber—Table ID to associate with the order. Shown on kitchen display.customerNotestring—Free-text note visible to kitchen staff.{
"slug": "cafe-central",
"items": [
{ "productId": 1, "quantity": 2 },
{ "productId": 3, "quantity": 1 }
],
"tableId": 4,
"customerNote": "No sugar please"
}{
"order": {
"id": 42,
"status": "created",
"totalAmount": "10.50",
"tableId": 4,
"source": "customer",
"customerNote": "No sugar please",
"merchantId": 1,
"createdAt": "...",
"items": [
{
"id": 101,
"orderId": 42,
"productId": 1,
"quantity": 2,
"price": "3.50",
"subtotal": "7.00"
},
{
"id": 102,
"orderId": 42,
"productId": 3,
"quantity": 1,
"price": "3.50",
"subtotal": "3.50"
}
]
}
}400 { "message": "No valid items in order" }
404 { "message": "Merchant not found" }/api/payment/:idPublic{
"payment": {
"id": 7,
"amount": "10.50",
"currency": "USDT",
"status": "pending",
"memo": "83741926",
"walletTo": "UQBxxxxxxxxxxx",
"txHash": null,
"createdAt": "...",
"expiresAt": "...",
"confirmedAt": null,
"paymentProvider": "ton", // "ton" or "fixedfloat"
"fromCurrency": null,
"depositAddress": null
},
"merchant": {
"name": "Café Central",
"slug": "cafe-central"
},
"order": {
"id": 42,
"totalAmount": "10.50",
"items": [...]
},
"tonDeeplink": "ton://transfer/UQBxx...?amount=10500000&text=83741926",
"tonkeeperUrl": "tonkeeper://transfer/...",
"checkUrl": null, // set after confirmation
"receiptHash": null // set after confirmation
}/api/payment/:id/checkPublic{
"status": "pending",
"message": "Payment is being verified..."
}{
"status": "confirmed",
"txHash": "a1b2c3d4...",
"checkUrl": "/check/17",
"receiptHash": "f3a9b2c1..."
}{
"status": "expired",
"message": "Payment has expired"
}/api/receipt/:hashPublic{
"check": {
"id": 17,
"checkNumber": "CHK-0017",
"receiptHash": "f3a9b2c1...",
"data": {
"merchant": { "name": "Café Central", "walletAddress": "UQBxx..." },
"order": { "id": 42, "items": [...], "totalAmount": "10.50" },
"payment": { "amount": "10.50", "currency": "USDT", "txHash": "a1b2c3..." }
},
"createdAt": "..."
},
"payment": {
"amount": "10.50",
"currency": "USDT",
"status": "confirmed",
"txHash": "a1b2c3d4...",
"confirmedAt": "..."
},
"merchant": { "name": "Café Central", "slug": "cafe-central" }
}400 { "message": "Invalid receipt hash" }
404 { "message": "Receipt not found" }Requires API Key · /api/v1
Management API
Read and write your merchant profile, product catalog, and orders. All endpoints use Authorization: Bearer csr_.... API keys here use direct key lookup — no session required.
/api/v1/businessAPI Key{
"id": 1,
"name": "Café Central",
"slug": "cafe-central",
"email": "[email protected]",
"walletAddress": "UQBxxxxxxxxxxx",
"isActive": true,
"settings": {
"paymentCurrency": "USDT",
"displayCurrency": "USD"
},
"createdAt": "..."
}/api/v1/businessAPI KeywalletAddressstring—Your TON wallet address. All future payments will settle here.settingsobject—Partial settings object. Supported keys: paymentCurrency ("TON" | "USDT"), displayCurrency (ISO 4217 code).{
"walletAddress": "UQBnewaddress...",
"settings": {
"paymentCurrency": "TON",
"displayCurrency": "EUR"
}
}400 { "message": "No updatable fields provided" }/api/v1/productsAPI Key[
{
"id": 1,
"merchantId": 1,
"name": "Espresso",
"price": "3.50",
"description": "Double shot, no milk",
"categoryId": 1,
"imageUrl": "https://...",
"isActive": true,
"isMenuVisible": true,
"createdAt": "...",
"updatedAt": "..."
}
]/api/v1/productsAPI Keynamestring✓Product name shown to customers on the menu.pricestring✓Price in your display currency. Use a string to avoid floating-point issues, e.g. "3.50".descriptionstring—Short description shown on the menu and receipt.categoryIdnumber—Category ID. null or omit for uncategorized.imageUrlstring—Absolute URL to the product image. Shown on menu and in the dashboard.isMenuVisibleboolean—Whether the product appears on the public QR menu. Defaults to true.isActiveboolean—Whether the product is active in the catalog. Defaults to true.{
"name": "Flat White",
"price": "4.00",
"description": "Double ristretto, steamed milk",
"categoryId": 1,
"imageUrl": "https://example.com/flat-white.jpg",
"isMenuVisible": true
}/api/v1/transactionsAPI Key[
{
"id": 42,
"merchantId": 1,
"status": "completed", // created|confirmed|preparing|ready|completed|paid|cancelled
"totalAmount": "10.50",
"tableId": 4,
"source": "customer", // customer|pos|menu|qr|website
"customerNote": "No sugar",
"createdAt": "...",
"items": [
{
"id": 101,
"productId": 1,
"quantity": 2,
"price": "3.50",
"subtotal": "7.00",
"product": {
"id": 1,
"name": "Espresso",
"price": "3.50"
}
}
],
"table": { "id": 4, "name": "Table 4" }
}
]/api/v1/transactions/:id/paymentAPI Key{
"orderId": 42,
"payment": {
"id": 7,
"status": "pending",
"amount": "10.50",
"currency": "USDT",
"walletTo": "UQBxxxxxxxxxxx",
"memo": "83741926",
"expiresAt": "...",
"confirmedAt": null,
"txHash": null
},
"qrValue": "ton://transfer/UQBxx...?amount=10500000&text=83741926",
"tonDeeplink": "ton://transfer/UQBxx...?amount=10500000&text=83741926",
"tonkeeperUrl": "tonkeeper://transfer/...",
"checkUrl": null,
"receiptHash": null
}404 { "message": "Order not found" }
403 { "message": "Forbidden" }
404 { "message": "No active payment found for this order" }/api/v1/transactions/chooseAPI KeyorderIdnumber✓ID of the order to switch currency for.fromCurrencystring✓New customer payment currency (e.g. "ETH", "BTC", "TON", "USDT").{ "paymentCode": "pay_n4q6t8v2" }404 { "message": "Order not found" }
404 { "message": "No pending payment for this order" }
422 { "message": "Merchant wallet not configured" }
400 { "message": "Unsupported currency" }Schema
Types Reference
Key object shapes returned by the API.
Payment status
pendingPayment created, waiting for on-chain confirmation. Expires after 15–30 minutes.confirmedPayment confirmed on-chain. tx_hash is set. Webhook has been (or will be) delivered.expiredPayment window elapsed with no confirmed transaction.failedExchange bridge reported a failure (exchange-path payments only).Order status
createdOrder received, payment not yet confirmed.confirmedPayment confirmed — kitchen can begin preparation.preparingKitchen is actively preparing the order.readyOrder ready for pickup or delivery.completedOrder fulfilled.paidLegacy alias for completed.cancelledOrder cancelled before completion.Supported currencies
Settlement currencies (what lands in your wallet): TON, USDT.
Customer payment currencies include all settlement currencies plus exchange-bridge chains:
TONUSDTBTCETHSOLBNBLTCDOGEXRPTRXMATICAVAXATOMDOTADAXLM+ moreOrder source
customerQR menu — placed by the customer directly.posPoint of sale — placed by staff via the POS interface.menuDigital menu kiosk.qrQR code scan (table-specific).websiteOnline store integration.chcks API Reference — [email protected]