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}/invoicesAuthentication — 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"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name shown on the invoice (max 200 chars). |
rawAmount | number | Yes | Expected amount in the specified currency. Min 0, max 1 000 000. |
currency | string | No | Currency code — e.g. "USD". Defaults to "USD". |
customerEmail | string | No | Customer email address. |
customerName | string | No | Customer full name. |
customerAddress | string | No | Customer billing address (max 500 chars). |
customerPhoneNumber | string | No | Customer phone number (max 50 chars). |
description | string | No | Description shown on the invoice (max 1000 chars). |
redirectUrl | string | No | URL 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" }
]
}| Field | Description |
|---|---|
id | Unique invoice ID. Store this — it appears in every webhook for this invoice. |
status | Always AWAITING_PAYMENT on creation. |
rawAmount / endAmount | Strings, not numbers — Decimal(15,6) in the database, serialized as strings to preserve precision. Use a decimal library (decimal.js, BigDecimal) for arithmetic. |
expiresAt | ISO timestamp after which the invoice transitions to EXPIRED. |
customer, product, payments, invoiceTimeline | Always present on responses. Empty relations are null or [], never undefined. |
Errors
See Error responses for the full error envelope.
| Status | Cause |
|---|---|
400 | Validation error — body did not match the schema |
401 | Missing or invalid x-api-key |
403 | Key lacks INVOICE_CREATE, or a public key (pk_*) tried to cancel/mutate |
409 | Same Idempotency-Key replayed with a different body |
429 | Rate-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.