Skip to content

Developers

Create an invoice with one REST API request, and your server hears about every payment and payout through a signed webhook.

Three steps to connect

  • Open the shop in the dashboard and issue an API key under “API”. “Invoices only” access is enough for invoices; payouts need full access. The key is shown once, so keep it on your server.
  • Set the webhook URL under “Webhook” and save the signing secret.
  • Create an invoice and send the buyer to the pay_url from the response.

An invoice in one request

curl
curl -X POST https://<domain>/api/v1/invoices \
  -H "Authorization: Bearer cpk_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{"currency": "USD", "amount": "49.90", "reference": "order-1042"}'

A retry with the same Idempotency-Key never creates a second invoice. Amounts are strings, and times are UTC.

Events

  • payment.seen, payment.detected, payment.confirmed, payment.orphaned: seen, found in a block, credited, rolled back
  • invoice.paid, invoice.partially_paid, invoice.expired, invoice.cancelled: invoice state
  • payout.broadcast, payout.confirmed, payout.failed: payout sent, confirmed, failed

An event counts as delivered when your server answers 2xx within 10 seconds. Otherwise CoinsPay retries it with growing pauses: 20 attempts over about a day.

Verifying the signature

Node.js
import crypto from 'node:crypto';

// rawBody is the request body exactly as received, before JSON.parse.
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}

Go, PHP and Python samples are in the dashboard under “Webhook” and in the API reference.

Limits

  • Up to 600 requests a minute per IP.
  • Up to 60 requests a minute per shop, counted separately for addresses, invoices and payouts.
  • Every method and field is described in the API reference.

Start taking crypto today

Signing up takes a minute and setting up a shop about five more. Connecting is free.