Billium
Quickstart

Create an invoice

Use the REST API or the Node SDK to generate a crypto payment request.

Endpoint

POST /api/v1/merchants/merchant/{merchantId}/invoices

Authentication — include your secret API key in the x-api-key header. See Authentication.

Idempotency — set an Idempotency-Key header on every call. See Idempotency.

Request body

{
  "name": "Order #8821",
  "rawAmount": 49.99,
  "currency": "USD",
  "customerEmail": "customer@example.com",
  "redirectUrl": "https://yoursite.com/thank-you"
}
FieldTypeRequiredDescription
namestringYesDisplay name shown on the invoice (max 200 chars).
rawAmountnumberYesExpected amount in the specified currency. Min 0, max 1 000 000.
currencystringNoCurrency code — e.g. "USD". Defaults to "USD".
customerEmailstringNoCustomer email address.
customerNamestringNoCustomer full name.
customerAddressstringNoCustomer billing address (max 500 chars).
customerPhoneNumberstringNoCustomer phone number (max 50 chars).
descriptionstringNoDescription shown on the invoice (max 1000 chars).
redirectUrlstringNoURL to redirect the customer after payment (http or https).

Code examples

import { randomUUID } from 'crypto';
import { Billium } from '@billium/node';

const billium = new Billium({
  apiKey: process.env.BILLIUM_API_KEY,       // sk_...
  merchantId: process.env.BILLIUM_MERCHANT_ID, // mer_...
});

const invoice = await billium.invoices.create(
  {
    name: 'Order #8821',
    rawAmount: 49.99,
    currency: 'USD',
    customerEmail: 'customer@example.com',
    redirectUrl: 'https://yoursite.com/thank-you',
  },
  { idempotencyKey: randomUUID() },
);

console.log(invoice.id);     // 'inv_...'
console.log(invoice.status); // 'AWAITING_PAYMENT'
curl -X POST \
  https://api.billium.to/api/v1/merchants/merchant/$BILLIUM_MERCHANT_ID/invoices \
  -H "x-api-key: sk_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Order #8821",
    "rawAmount": 49.99,
    "currency": "USD",
    "customerEmail": "customer@example.com",
    "redirectUrl": "https://yoursite.com/thank-you"
  }'
import os
import uuid
import httpx

invoice = httpx.post(
    f"https://api.billium.to/api/v1/merchants/merchant/{os.environ['BILLIUM_MERCHANT_ID']}/invoices",
    headers={
        "x-api-key": os.environ["BILLIUM_API_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "name": "Order #8821",
        "rawAmount": 49.99,
        "currency": "USD",
        "customerEmail": "customer@example.com",
    },
    timeout=30,
).json()

Response

{
  "id": "inv_a1b2c3d4e5f6",
  "merchantId": "mer_...",
  "customerId": "cus_...",
  "productId": null,
  "name": "Order #8821",
  "description": null,
  "redirectUrl": "https://yoursite.com/thank-you",
  "rawAmount": "49.990000",
  "endAmount": "49.990000",
  "currency": "USD",
  "status": "AWAITING_PAYMENT",
  "expiresAt": "2026-04-12T05:00:00.000Z",
  "createdAt": "2026-04-12T04:00:00.000Z",
  "updatedAt": "2026-04-12T04:00:00.000Z",
  "customer": {
    "id": "cus_...",
    "email": "customer@example.com",
    "name": null,
    "address": null,
    "phoneNumber": null
  },
  "product": null,
  "payments": [],
  "invoiceTimeline": [
    { "id": "itl_...", "invoiceId": "inv_a1b2c3d4e5f6", "paymentStatus": "AWAITING_PAYMENT", "time": "2026-04-12T04:00:00.000Z" }
  ]
}
FieldDescription
idUnique invoice ID. Store this — it appears in every webhook for this invoice.
statusAlways AWAITING_PAYMENT on creation.
rawAmount / endAmountStrings, not numbers — Decimal(15,6) in the database, serialized as strings to preserve precision. Use a decimal library (decimal.js, BigDecimal) for arithmetic.
expiresAtISO timestamp after which the invoice transitions to EXPIRED.
customer, product, payments, invoiceTimelineAlways present on responses. Empty relations are null or [], never undefined.

Errors

See Error responses for the full error envelope.

StatusCause
400Validation error — body did not match the schema
401Missing or invalid x-api-key
403Key lacks INVOICE_CREATE, or a public key (pk_*) tried to cancel/mutate
409Same Idempotency-Key replayed with a different body
429Rate-limit exceeded — see Rate limits

What to do next

Once you have the invoice, redirect the customer to the hosted checkout (via redirectUrl or by composing the URL yourself). While they pay, wire up your webhook handler for invoice.paid.

On this page