@billium/node
Official Node.js SDK for Billium — invoices, webhook management, and webhook signature verification.
@billium/node is the official Node.js SDK for Billium. It covers the Invoices API, webhook management, and webhook signature verification.
Current coverage: the Node SDK currently exposes billium.invoices and billium.webhooks. The rest of the Billium REST API — customers, products, wallets, API keys, team, reports — is fully functional but only accessible over raw HTTP for now. SDK coverage of these resources is on the near-term roadmap. In the meantime, call those endpoints directly with fetch / undici / your HTTP client of choice, using the same x-api-key header documented in Authentication.
Installation
npm install @billium/node
# or
pnpm add @billium/node
# or
yarn add @billium/nodeRequirements: Node.js ≥ 18.
Initialization
import { Billium } from '@billium/node';
const billium = new Billium({
apiKey: process.env.BILLIUM_API_KEY, // sk_... or pk_...
merchantId: process.env.BILLIUM_MERCHANT_ID, // mer_...
webhookSecret: process.env.BILLIUM_WEBHOOK_SECRET, // whsec_...
});Billium is a named export only — there is no default export. Both ESM and CJS consumers must destructure it:
// ESM
import { Billium } from '@billium/node';
// CJS
const { Billium } = require('@billium/node');Constructor options
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Billium API key. Use a secret key (sk_*) for full access. Public keys (pk_*) only cover invoices.create, invoices.get, and product reads; calling anything else throws a BilliumError at the SDK layer without a round-trip. |
merchantId | string | — | Your merchant ID (mer_...). Required together with apiKey to call billium.invoices.* and the webhook management methods. |
webhookSecret | string | — | Your webhook secret (whsec_...). When set, billium.webhooks.verify() can be called without passing the secret every time. |
baseUrl | string | https://api.billium.to | Override the API base URL (useful for self-hosted deployments or integration tests against a staging env). |
maxRetries | number | 2 | Retry attempts on network errors, 5xx, and 429. Set to 0 to disable. |
baseDelayMs | number | 500 | Starting backoff delay. Subsequent retries use exponential backoff with full jitter. |
maxDelayMs | number | 30000 | Upper bound on the backoff delay. Retry-After headers are honored and clamped by this value. |
Retry behavior
GET,PUT,PATCH,DELETEare always retried whenmaxRetries > 0.POSTis retried only when anIdempotency-Keyheader is set on the request — otherwise retrying could create a duplicate resource.invoices.create()sends this header automatically when you passoptions.idempotencyKey.- Retryable statuses:
429,500,502,503,504. Everything else (400, 401, 403, 404, 409, 422…) throwsBilliumApiErrorimmediately. Retry-Afterheaders are honored — the SDK waits for the server-specified delay before retrying.
See Idempotency for the full rationale and recommended patterns.
Invoices
Requires apiKey and merchantId in the constructor.
billium.invoices.create(params, options?)
Creates a new invoice.
import { randomUUID } from 'crypto';
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'Params
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Invoice display name (max 200 chars). |
rawAmount | number | Yes | Expected amount in the currency (min 0, max 1 000 000). |
currency | string | No | Currency code. Defaults to USD. |
customerEmail | string | No | Customer email. |
customerName | string | No | Customer name. |
customerAddress | string | No | Customer billing address. |
customerPhoneNumber | string | No | Customer phone number. |
description | string | No | Invoice description (max 1000 chars). |
redirectUrl | string | No | Post-payment redirect URL. |
Options
| Field | Type | Description |
|---|---|---|
idempotencyKey | string | Set this in production. Enables safe retries and protects against accidental duplicate invoices when your own code retries. See Idempotency. |
billium.invoices.get(invoiceId)
const invoice = await billium.invoices.get('inv_...');billium.invoices.list(params?)
const result = await billium.invoices.list({ page: 1, limit: 20, search: 'order' });
// result.data → Invoice[]
// result.total → number
// result.page → number
// result.limit → numberbillium.invoices.cancel(invoiceId)
Cancels an invoice. Throws BilliumApiError if the invoice is already in a terminal state, and BilliumError immediately if the configured API key is a public key (pk_*).
const invoice = await billium.invoices.cancel('inv_...');
console.log(invoice.status); // 'CANCELLED'Invoice shape
Decimal fields are serialized as strings to preserve precision — use a decimal library (decimal.js) for arithmetic.
interface Invoice {
id: string;
merchantId: string;
customerId: string | null;
productId: string | null;
name: string;
description: string | null;
redirectUrl: string | null;
rawAmount: string; // e.g. "49.990000"
endAmount: string; // e.g. "49.990000" (rawAmount + fees, when applicable)
currency: string;
status: InvoiceStatus;
expiresAt: string | null;
createdAt: string;
updatedAt: string;
// Relations — always present on responses, never undefined
customer: InvoiceCustomer | null;
product: InvoiceProduct | null;
payments: InvoicePayment[];
invoiceTimeline: InvoiceTimelineEntry[];
}Webhook signature verification
billium.webhooks.verify(rawBody, signature, secret?, options?)
Parses and verifies an incoming webhook request. Throws if the signature is invalid or the timestamp is outside the tolerance window.
// With webhookSecret set in the constructor:
const event = billium.webhooks.verify(
rawBody, // Buffer | string — raw request body
req.headers['x-signature'], // x-signature header value
);
// Or pass the secret explicitly (overrides the constructor value):
const event = billium.webhooks.verify(rawBody, signature, 'whsec_...');Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
rawBody | Buffer | string | — | Raw request body, not parsed JSON. |
signature | string | — | Value of the x-signature header. |
secret | string | — | Webhook secret. Can be omitted if webhookSecret is set in the constructor. |
options.tolerance | number | 300 | Max allowed age of the timestamp in seconds. Set to 0 to disable. |
Returns WebhookEvent
interface WebhookEvent {
event: string; // e.g. 'invoice.paid'
id: string; // unique event ID — use for deduplication
data: unknown; // event-specific payload
timestamp: string; // ISO 8601
}Throws
| Error | When |
|---|---|
BilliumError | No secret was provided (neither in constructor nor as argument) |
BilliumWebhookSignatureError | Header is malformed or HMAC does not match |
BilliumWebhookTimestampError | Timestamp is outside the tolerance window |
Webhook management
Requires apiKey and merchantId, and the apiKey must be a secret key (sk_*). Public keys throw BilliumError at the SDK layer before hitting the network.
billium.webhooks.create(params)
const webhook = await billium.webhooks.create({
url: 'https://yoursite.com/webhooks/billium',
events: ['invoice.paid', 'invoice.expired'], // or ['invoice.*']
description: 'Production order fulfillment',
});
// Store this secret now — it's only shown once.
const secret = webhook.webhookSecrets[0].secretKeyPreview; // 'whsec_...'Params
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL Billium will POST events to. |
events | WebhookEventType[] | Yes | Subscribed events. Use invoice.* or payment.* for wildcards. |
description | string | No | Free-text description. |
isActive | boolean | No | Defaults to true. |
retryCount | number | No | 0–10. Defaults to 3. |
timeout | number | No | Delivery timeout in ms (1000–30000). Defaults to 30000. |
billium.webhooks.list()
const webhooks = await billium.webhooks.list();billium.webhooks.update(webhookId, params)
await billium.webhooks.update('wh_...', {
events: ['invoice.*', 'payment.confirmed'],
});billium.webhooks.delete(webhookId)
await billium.webhooks.delete('wh_...');billium.webhooks.ping(webhookId)
Sends a test delivery to the endpoint so you can confirm your handler is reachable.
await billium.webhooks.ping('wh_...');Error types
All errors extend BilliumError, which extends the native Error class.
import {
BilliumError,
BilliumApiError,
BilliumWebhookSignatureError,
BilliumWebhookTimestampError,
} from '@billium/node';
// API errors
try {
const invoice = await billium.invoices.create({ /* ... */ });
} catch (err) {
if (err instanceof BilliumApiError) {
console.error(err.status); // HTTP status code, e.g. 400, 401, 403
console.error(err.code); // optional error code from the API
console.error(err.message); // human-readable message
}
}
// Webhook errors
try {
const event = billium.webhooks.verify(rawBody, signature);
} catch (err) {
if (err instanceof BilliumWebhookTimestampError) {
// Possible replay attack — reject
return res.status(400).send();
}
if (err instanceof BilliumWebhookSignatureError) {
// Invalid or tampered signature
return res.status(400).send();
}
throw err;
}| Class | name | Cause |
|---|---|---|
BilliumError | 'BilliumError' | Base class / configuration error (e.g. pk_* used for sk_*-only method) |
BilliumApiError | 'BilliumApiError' | Non-2xx HTTP response from the Billium API |
BilliumWebhookSignatureError | 'BilliumWebhookSignatureError' | Malformed header or HMAC mismatch |
BilliumWebhookTimestampError | 'BilliumWebhookTimestampError' | Timestamp outside tolerance window |
See Errors for the REST error envelope.