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.

API key format
Authorization: Bearer csr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keep it server-side

Your 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).

POST/api/v1/invoicesAPI Key
Create a payment. Returns a payment_url to redirect your customer to. Native TON/USDT and exchange-bridge paths have different response shapes.
Request body
FieldTypeReqDescription
amountstring|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.
Response — 201 (native TON / USDT)
{
  "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"
}
Response — 201 (exchange bridge, from_currency set)
{
  "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"
}
GET/api/v1/invoices/:uuidAPI Key
Fetch full payment details. Includes webhook delivery status and confirmed transaction hash when available.
Response
{
  "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
}
POST/api/v1/invoices/:uuid/checkAPI Key
Manually trigger an on-chain verification pass. Use this as a polling fallback if your webhook endpoint was temporarily unreachable. No request body required.
Response
// Confirmed
{ "status": "confirmed", "tx_hash": "a1b2c3d4..." }

// Still pending
{ "status": "pending", "tx_hash": null }

// Expired
{ "status": "expired", "tx_hash": null }
POST/api/v1/invoices/chooseAPI Key
Switch a pending payment to a different customer payment currency. Creates a new payment record linked to the same order and returns the new UUID. The original payment remains in pending state — your checkout UI should use the new UUID going forward.
Request body
FieldTypeReqDescription
uuidstring✓UUID of the current pending payment.
fromCurrencystring✓Target currency for the customer to pay in (e.g. "ETH", "BTC", "TON", "USDT").
Response
{ "uuid": "pay_n4q6t8v2" }  // new payment UUID — use this going forward
Errors
409  { "message": "Payment is no longer pending" }
404  { "message": "Payment not found" }
400  { "message": "Unsupported currency" }
GET/api/v1/invoicesAPI Key
List all gateway payments for your account, newest first. Useful for reconciliation and dashboard displays.
Query parameters
ParamTypeDefaultDescription
limitinteger20Max results to return. Capped at 100.
offsetinteger0Number of records to skip for pagination.
Response
{
  "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.

Webhook request
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"
  }
}
Signature verification

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

If your webhook endpoint is down, use 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.

GET/api/menu/:slugPublic
Fetch a merchant's public catalog: visible products, active categories, and payment configuration. Invisible or inactive products are excluded.
Path parameters
slug The merchant's unique URL identifier (e.g. cafe-central).
Response
{
  "merchant": {
    "name": "Café Central",
    "slug": "cafe-central",
    "walletAddress": "UQBxxxxxxxxxxx",
    "paymentCurrency": "USDT",     // your settlement currency
    "displayCurrency": "USD",      // currency prices are shown in
    "settings": {}
  },
  "products": [
    {
      "id": 1,
      "name": "Espresso",
      "price": "3.50",
      "description": "Double shot, no milk",
      "categoryId": 1,
      "imageUrl": "https://...",
      "isActive": true,
      "isMenuVisible": true,
      "merchantId": 1,
      "createdAt": "..."
    }
  ],
  "categories": [
    { "id": 1, "name": "Coffee", "sortOrder": 0, "isActive": true }
  ]
}
Errors
404  { "message": "Merchant not found" }
POST/api/public/ordersPublic
Place an order against a merchant's catalog. Prices are validated server-side against current product prices — the client does not set the amount. Returns the created order.
Request body
FieldTypeReqDescription
slugstring✓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.
Request example
{
  "slug": "cafe-central",
  "items": [
    { "productId": 1, "quantity": 2 },
    { "productId": 3, "quantity": 1 }
  ],
  "tableId": 4,
  "customerNote": "No sugar please"
}
Response — 201
{
  "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"
      }
    ]
  }
}
Errors
400  { "message": "No valid items in order" }
404  { "message": "Merchant not found" }
GET/api/payment/:idPublic
Get full payment details including TON deeplinks for rendering a QR code. Also auto-triggers on-chain verification for pending payments.
Response
{
  "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
}
POST/api/payment/:id/checkPublic
Poll for payment confirmation. Triggers an on-chain verification check and returns the current status. Poll every 5 seconds — the payment window is 15 minutes for QR menu orders.
Response — pending
{
  "status": "pending",
  "message": "Payment is being verified..."
}
Response — confirmed
{
  "status": "confirmed",
  "txHash": "a1b2c3d4...",
  "checkUrl": "/check/17",
  "receiptHash": "f3a9b2c1..."
}
Response — expired
{
  "status": "expired",
  "message": "Payment has expired"
}
GET/api/receipt/:hashPublic
Verify a receipt by its SHA-256 hash. Independent verification — does not require trusting chcks. The hash must be a 64-character lowercase hex string.
Response
{
  "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" }
}
Errors
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.

GET/api/v1/businessAPI Key
Return your full merchant profile. Password field is never included.
Response
{
  "id": 1,
  "name": "Café Central",
  "slug": "cafe-central",
  "email": "[email protected]",
  "walletAddress": "UQBxxxxxxxxxxx",
  "isActive": true,
  "settings": {
    "paymentCurrency": "USDT",
    "displayCurrency": "USD"
  },
  "createdAt": "..."
}
PUT/api/v1/businessAPI Key
Update your wallet address or settings. Only fields provided in the body are updated. Returns the updated merchant profile.
Request body
FieldTypeReqDescription
walletAddressstring—Your TON wallet address. All future payments will settle here.
settingsobject—Partial settings object. Supported keys: paymentCurrency ("TON" | "USDT"), displayCurrency (ISO 4217 code).
Request example
{
  "walletAddress": "UQBnewaddress...",
  "settings": {
    "paymentCurrency": "TON",
    "displayCurrency": "EUR"
  }
}
Errors
400  { "message": "No updatable fields provided" }
GET/api/v1/productsAPI Key
List all products in your catalog, including hidden and inactive ones. Use GET /api/menu/:slug for the public-facing filtered list.
Response
[
  {
    "id": 1,
    "merchantId": 1,
    "name": "Espresso",
    "price": "3.50",
    "description": "Double shot, no milk",
    "categoryId": 1,
    "imageUrl": "https://...",
    "isActive": true,
    "isMenuVisible": true,
    "createdAt": "...",
    "updatedAt": "..."
  }
]
POST/api/v1/productsAPI Key
Create a product in your catalog. Returns the created product with a 201 status.
Request body
FieldTypeReqDescription
namestring✓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.
Request example
{
  "name": "Flat White",
  "price": "4.00",
  "description": "Double ristretto, steamed milk",
  "categoryId": 1,
  "imageUrl": "https://example.com/flat-white.jpg",
  "isMenuVisible": true
}
GET/api/v1/transactionsAPI Key
List all orders for your merchant, with full item details and table information. Newest first.
Response
[
  {
    "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" }
  }
]
GET/api/v1/transactions/:id/paymentAPI Key
Get the active payment for an order with wallet address, memo, and TON deeplinks ready for QR rendering. Returns 404 if no active payment exists.
Response
{
  "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
}
Errors
404  { "message": "Order not found" }
403  { "message": "Forbidden" }
404  { "message": "No active payment found for this order" }
POST/api/v1/transactions/chooseAPI Key
Switch a pending order payment to a different customer payment currency. Creates a new payment record. Use the returned paymentCode to redirect the customer.
Request body
FieldTypeReqDescription
orderIdnumber✓ID of the order to switch currency for.
fromCurrencystring✓New customer payment currency (e.g. "ETH", "BTC", "TON", "USDT").
Response
{ "paymentCode": "pay_n4q6t8v2" }
Errors
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+ more

Order 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]