Create an invoice via API

POST /api/invoices with idempotency, metadata, and the fields that actually matter.

Creating an invoice is a single POST. The fields that matter most on day one are fiat_amount, fiat_currency, crypto_currency, crypto_network, and external_id.

Request#

POST /api/invoices

Headers: Authorization: Bearer <api-key>, Content-Type: application/json.

{
  "fiat_amount": "49.99",
  "fiat_currency": "USD",
  "crypto_currency": "BTC",
  "crypto_network": "bitcoin",
  "ttl_minutes": 30,
  "external_id": "order-2026-0117-001",
  "description": "Order #1234",
  "metadata": { "order_id": "1234", "customer_id": "cust_42" },
  "webhook_url": "https://example.com/webhooks/crypto-merchant"
}

Required fields#

  • fiat_amount — decimal string.
  • fiat_currency — ISO 4217, uppercase (USD, EUR…).
  • crypto_currency — uppercase (BTC, ETH, USDT…).
  • crypto_network — lowercase (bitcoin, ethereum, tron…). See the currencies table.
  • external_id — your own unique identifier. Enables idempotency. Strongly recommended.
  • description — shown to operators in the dashboard.
  • metadata — any JSON you want echoed back in every webhook. See Reconcile with metadata.

Optional fields#

  • ttl_minutes — default 30. Minimum sensible value is 5; maximum 1440.
  • webhook_url — overrides the merchant's default webhook URL for this one invoice. Most integrations leave this unset.

Samples#

cURL#

curl -X POST https://api.example.com/api/invoices \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fiat_amount": "49.99",
    "fiat_currency": "USD",
    "crypto_currency": "BTC",
    "crypto_network": "bitcoin",
    "external_id": "order-2026-0117-001",
    "metadata": { "order_id": "1234" }
  }'

TypeScript (fetch)#

type CreateInvoiceInput = {
  fiat_amount: string;
  fiat_currency: string;
  crypto_currency: string;
  crypto_network: string;
  external_id?: string;
  description?: string;
  metadata?: Record<string, unknown>;
  ttl_minutes?: number;
};

export async function createInvoice(input: CreateInvoiceInput, apiKey: string) {
  const res = await fetch("https://api.example.com/api/invoices", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(input),
  });
  if (!res.ok) {
    throw new Error(`invoice create failed: ${res.status} ${await res.text()}`);
  }
  return res.json();
}

Python (httpx)#

import httpx

def create_invoice(input: dict, api_key: str) -> dict:
    with httpx.Client(timeout=10) as client:
        r = client.post(
            "https://api.example.com/api/invoices",
            headers={"Authorization": f"Bearer {api_key}"},
            json=input,
        )
        r.raise_for_status()
        return r.json()

Response#

A 201 with the full invoice object (same shape as the webhook payload). Cache the invoice_id — you will use it for subsequent reads and reconciliation.

Errors#

  • 400 — validation failure. Body is a structured error object naming the offending field.
  • 401 — missing or bad API key.
  • 422 — currency/network combination not supported. See Payments & confirmations for the supported matrix.
  • 5xx — retry with exponential backoff; the request is not guaranteed idempotent unless you supply external_id.

Next steps#