billerapi
v0.1.0
Published
Official BillerAPI Node.js server SDK — connect users to their billers, retrieve bills, and automate billing workflows.
Maintainers
Readme
billerapi
The official BillerAPI Node.js server SDK. Connect your users to their billers, retrieve bills, and automate billing workflows — with typed responses, automatic retries, idempotency, cursor pagination, and webhook verification built in.
Early access (
0.x). The client core, webhook verification, and resources —billers,bills,links,accountLinks,webhookEndpoints,feedback,pay— are implemented. Response shapes are being finalized against the live API before1.0; pin a version for production.
Install
npm install billerapiRequires Node.js ≥ 18 (uses the built-in fetch and crypto — no dependencies).
Quickstart
import { BillerApi } from 'billerapi';
const billerapi = new BillerApi(process.env.BILLERAPI_API_KEY!);
// List billers — auto-paginates across every page
for await (const biller of billerapi.billers.list({ search_term: 'electric' })) {
console.log(biller.id, biller.name);
}Works from CommonJS too:
const { BillerApi } = require('billerapi');Resources
| Accessor | Key methods |
|---|---|
| billerapi.billers | list, retrieve |
| billerapi.links | createToken, exchangeToken, retrieve |
| billerapi.bills | list, retrieve, getStatement, triggerSync |
| billerapi.accountLinks | syncBills, refresh, getRefreshStatus |
| billerapi.webhookEndpoints | create, list, retrieve, update, del, rotateSecret |
| billerapi.feedback | submit |
| billerapi.pay | createToken, storePaymentMethod, listPaymentMethods, deletePaymentMethod, initiatePayment, retrievePayment |
| billerapi.webhooks | constructEvent |
Authentication & environments
Pass your secret key. The environment is inferred from the key prefix, so you don't configure a base URL:
bak_test_…→ sandbox (https://sandbox.api.billerapi.com)bak_live_…→ production (https://api.billerapi.com)
const billerapi = new BillerApi('bak_test_…', {
maxRetries: 3, // transient failures (429 / 5xx / network). Default 2.
timeout: 30_000, // ms. Default 30s.
});
billerapi.mode; // 'sandbox' | 'live'Pagination
list() returns the first page in .data and is async-iterable to walk every
page without managing cursors:
const page = await billerapi.bills.list({ account_link_id });
page.data; // first page
page.hasMore; // is there another page?
for await (const biller of billerapi.billers.list()) {
// …every biller across all pages
}
const all = await billerapi.billers.list().then((p) => p.toArray());Errors
Every failure throws a typed error carrying the API's coded envelope. Branch by
class or by .code; .requestId is always available for support:
import { RateLimitError, InvalidRequestError, BillerApiError } from 'billerapi';
try {
await billerapi.billers.retrieve('biller_missing');
} catch (err) {
if (err instanceof RateLimitError) {
await sleep((err.retryAfter ?? 1) * 1000);
} else if (err instanceof InvalidRequestError) {
console.error(err.fieldErrors); // [{ param, code, message }]
} else if (err instanceof BillerApiError) {
console.error(err.code, err.requestId, err.docsUrl);
}
}Error classes: AuthenticationError, PermissionError, InvalidRequestError,
RateLimitError, UpstreamError, ApiError, ConnectionError — all extend
BillerApiError.
Idempotency
Mutating requests (POST) are automatically sent with an Idempotency-Key so a
transparent retry never double-creates. Every resource method takes a
RequestOptions second argument — pass your own idempotencyKey there to make a
retry idempotent across process restarts:
await billerapi.links.createToken(params, { idempotencyKey: 'order_42' });Verifying webhooks
Verify the BillerAPI-Signature header against your endpoint's signing secret.
Always pass the raw request body:
import { BillerApi } from 'billerapi';
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = billerapi.webhooks.constructEvent(
req.body, // raw Buffer/string
req.headers['billerapi-signature'],
process.env.BILLERAPI_WEBHOOK_SECRET!,
);
// handle event.type …
res.sendStatus(200);
} catch {
res.sendStatus(400); // signature invalid or replayed
}
});License
MIT
