Billium
SDK

@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/node

Requirements: 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

OptionTypeDefaultDescription
apiKeystringBillium 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.
merchantIdstringYour merchant ID (mer_...). Required together with apiKey to call billium.invoices.* and the webhook management methods.
webhookSecretstringYour webhook secret (whsec_...). When set, billium.webhooks.verify() can be called without passing the secret every time.
baseUrlstringhttps://api.billium.toOverride the API base URL (useful for self-hosted deployments or integration tests against a staging env).
maxRetriesnumber2Retry attempts on network errors, 5xx, and 429. Set to 0 to disable.
baseDelayMsnumber500Starting backoff delay. Subsequent retries use exponential backoff with full jitter.
maxDelayMsnumber30000Upper bound on the backoff delay. Retry-After headers are honored and clamped by this value.

Retry behavior

  • GET, PUT, PATCH, DELETE are always retried when maxRetries > 0.
  • POST is retried only when an Idempotency-Key header is set on the request — otherwise retrying could create a duplicate resource. invoices.create() sends this header automatically when you pass options.idempotencyKey.
  • Retryable statuses: 429, 500, 502, 503, 504. Everything else (400, 401, 403, 404, 409, 422…) throws BilliumApiError immediately.
  • Retry-After headers 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

FieldTypeRequiredDescription
namestringYesInvoice display name (max 200 chars).
rawAmountnumberYesExpected amount in the currency (min 0, max 1 000 000).
currencystringNoCurrency code. Defaults to USD.
customerEmailstringNoCustomer email.
customerNamestringNoCustomer name.
customerAddressstringNoCustomer billing address.
customerPhoneNumberstringNoCustomer phone number.
descriptionstringNoInvoice description (max 1000 chars).
redirectUrlstringNoPost-payment redirect URL.

Options

FieldTypeDescription
idempotencyKeystringSet 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 → number

billium.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

ParameterTypeDefaultDescription
rawBodyBuffer | stringRaw request body, not parsed JSON.
signaturestringValue of the x-signature header.
secretstringWebhook secret. Can be omitted if webhookSecret is set in the constructor.
options.tolerancenumber300Max 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

ErrorWhen
BilliumErrorNo secret was provided (neither in constructor nor as argument)
BilliumWebhookSignatureErrorHeader is malformed or HMAC does not match
BilliumWebhookTimestampErrorTimestamp 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

FieldTypeRequiredDescription
urlstringYesHTTPS URL Billium will POST events to.
eventsWebhookEventType[]YesSubscribed events. Use invoice.* or payment.* for wildcards.
descriptionstringNoFree-text description.
isActivebooleanNoDefaults to true.
retryCountnumberNo0–10. Defaults to 3.
timeoutnumberNoDelivery 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;
}
ClassnameCause
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.

On this page