Coldtill Get Coldtill

Merchant API

Paste into Claude Code, Codex, Cursor or your favourite coding agent. View Markdown

The merchant API is what your shop or backend calls: create a payment, send the customer to the hosted checkout, and get a signed webhook when it's paid. Payouts from the hot wallet use the same key.

The machine-readable reference is openapi.yaml (OpenAPI 3.1), rendered at coldtill.com/docs/api. Import it into Postman, Insomnia or a client generator. New here? Start with the guide Accept a payment from your shop.

  • Base URL: your site URL, for example https://pay.example.com.
  • Authentication: an API-Key header with a project API key. Create keys under API keys (project admin, with two-factor). Keys are shown once and stored hashed.
  • Format: JSON request and response bodies. Amounts are decimal strings.
  • Errors: a non-2xx status with {"error": "message"} and sometimes a code.
  • Rate limit: 600 requests per minute per client IP. Exceeding it returns 429.

An API key belongs to one project. It can create payments and payouts, but payouts can only go to that project's address book, and large ones wait for an admin. See Payouts.

Payments

Create a payment

POST /api/v1/payment

curl -X POST https://pay.example.com/api/v1/payment \
  -H "API-Key: $COLDTILL_API_KEY" -H 'Content-Type: application/json' \
  -d '{"customerID":"42","customerEmail":"jane@example.com","amountInUSD":25.00,"invoiceID":"INV-1001"}'
Field Required Description
customerID yes Your ID for the customer. The same ID always gets the same deposit addresses
amountInUSD yes Amount owed, greater than 0 and at most 10,000,000
customerEmail no Stored with the customer
invoiceID no Your order reference, echoed in webhooks
network no Restrict the checkout to one network code (see Network codes)
currency no With network, restrict to one currency, for example USDC
expire no RFC 3339 expiry time, at least a minute ahead. Defaults to the project's payment expiry (24 hours)

Response:

{
  "host": "https://pay.example.com",
  "reference_id": "fd09c7e5-d661-4355-9b04-7b3749b41e2e",
  "url": "https://pay.example.com/pay/fd09c7e5-d661-4355-9b04-7b3749b41e2e"
}

Send the customer to url. A customer has at most one open payment: creating a new one replaces the previous one (REPLACED). If the previous payment has a deposit that is still confirming, the request fails with 409 so that deposit isn't stranded; retry after it confirms.

Get a payment

GET /api/v1/payment/reference/{reference_id}

{
  "invoiceID": "INV-1001",
  "customerID": "42",
  "amountInUSD": "25",
  "filledAmountInUSD": "25.00000000",
  "pendingAmountInUSD": "0",
  "paymentState": "FILLED",
  "referenceID": "fd09c7e5-…",
  "url": "https://pay.example.com/pay/fd09c7e5-…",
  "createdAt": "2026-10-10T15:58:23Z",
  "expiresAt": "2026-10-11T15:58:23Z",
  "deposits": [
    {
      "network": "BASE", "currency": "USDC", "amount": "25", "amountInUSD": "25",
      "transactionHash": "0x…", "status": "confirmed", "confirmations": 12, "requiredConfirmations": 10
    }
  ]
}

Payment states:

State Meaning Final
OPEN Nothing confirmed yet no
PARTIALLY_FILLED Confirmed amount is below 99% of the amount owed no
FILLED Confirmed amount is within 1% of the amount owed yes
OVER_FILLED Confirmed amount is more than 1% above the amount owed yes
CANCELLED Expired before being filled yes
REPLACED Superseded by a newer payment for the same customer yes

Ship the order on FILLED or OVER_FILLED. Prefer webhooks to polling.

Payable currencies

GET /api/v1/blockchain-currency/reference/{reference_id} lists what the customer can pay with for this payment, with the price used for each.

Deposit address

POST /api/v1/deposit-address/reference/{reference_id} with {"blockchain_code": "BASE"} returns the customer's permanent address for that network's family. Use it to build your own checkout instead of the hosted one.

Ticker

GET /api/v1/ticker (no API key) lists enabled networks and tokens with USD prices: blockchainCode, currencyCode, tokenAddress, standard, walletPrecision, family, price.

Payouts

Create a payout

POST /api/v1/withdrawal/merchant with an Idempotency-Key header:

curl -X POST https://pay.example.com/api/v1/withdrawal/merchant \
  -H "API-Key: $COLDTILL_API_KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: refund-INV-1001' \
  -d '{"blockchainCode":"BASE","currencyCode":"USDC","amount":"12.50","toAddress":"0xAbC…"}'
Field Description
blockchainCode Network code: an EVM network or SOL
currencyCode ETH, USDC, USDT, SOL… as enabled on that network
amount Decimal amount in the currency, at most the token's decimals
toAddress or addressBookId Destination. Through the API it must be in the project's address book

Retrying with the same Idempotency-Key and the same parameters returns the original payout. Reusing a key with different parameters returns 409.

The response is the payout (see Payout object) with status pending or pending-approval.

List and get payouts

  • GET /api/v1/withdrawal/merchant?limit=50&offset=0 lists payouts created through the API.
  • GET /api/v1/withdrawal/{id}/merchant returns one payout.

Payout object

{
  "id": "5d1c…", "status": "sent", "network": "BASE", "currency": "USDC",
  "tokenContract": "0x8335…", "amount": "12.5", "amountInUSD": "12.50", "toAddress": "0xAbC…",
  "transactionHash": "0x…", "blockNumber": 21234567, "idempotencyKey": "refund-INV-1001",
  "createdBy": "system", "createdAt": "…", "updatedAt": "…"
}

approvalReason explains why a payout is held; failureReason explains a failure. Statuses are listed in Payouts.

Network codes

Code Mainnet Testnet mode
ETH Ethereum Sepolia
BASE Base Base Sepolia
POLYGON Polygon Amoy
BSC BNB Smart Chain BSC testnet
TRON Tron Nile
SOL Solana Devnet
BTC Bitcoin Signet

SDK

sdk/typescript wraps these endpoints and verifies webhooks, with no dependencies:

import { Coldtill, verifyWebhookRequest, isPaid } from '@coldtill/sdk'

const kh = new Coldtill({ baseUrl: 'https://pay.example.com', apiKey: process.env.COLDTILL_API_KEY! })
const { url } = await kh.createPayment({ customerID: '42', amountInUSD: 25, invoiceID: 'INV-1001' })

The WooCommerce plugin adds Coldtill as a payment method for USD stores.