Billium

Authentication

How to authenticate requests to the Billium REST API using API keys.

All requests to the Billium API must include an API key in the x-api-key header.

x-api-key: sk_...

API key types

Billium issues two types of keys. They share the same header and the same rate-limit bucket, but differ in scope.

PrefixNameScopeUse it for
sk_Secret keyFull access to every permission the issuing user's role allows (up to all 34 permissions).Your server. Never ship this to a browser or mobile client.
pk_Public keyExactly three permissions: invoice:create, invoice:view, product:view. Cannot read lists, cancel invoices, manage webhooks, or touch any other resource.Browser-side or mobile integrations where exposing a key is unavoidable.

Both keys are {prefix}_{96 base62 characters} — there is no separate _live_ / _test_ split at the API-key level today. A single merchant account has one logical key space.

There is no sandbox environment. Every API key hits production. For local development, run your own backend (backend/ in the Billium monorepo) and expose your webhook endpoint with ngrok or a similar tunnel. A hosted sandbox is on the roadmap but not yet available.

Never commit a secret key. Store it in a secrets manager or an environment variable, and rotate immediately if it leaks.

Obtaining an API key

  1. Log in to the Billium dashboard.
  2. Go to Settings → Developer → API keys.
  3. Click Create API Key, choose Secret or Public, and select the permissions you need.
  4. Copy the key — it is only shown once.

Including the key in requests

HTTP

curl https://api.billium.to/api/v1/merchants/merchant/$BILLIUM_MERCHANT_ID/invoices \
  -H "x-api-key: sk_..."

Every merchant-scoped endpoint lives under /api/v1/merchants/merchant/{merchantId}/..., so you always need both your API key and your merchant ID.

Node.js SDK

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

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

const invoice = await billium.invoices.create({ /* ... */ });

When you pass a pk_* key, the SDK short-circuits any method that requires a secret key (invoices.cancel, webhooks.create, etc.) with a clear BilliumError instead of letting the request round-trip to a 403.

Permissions

Billium has 34 granular permissions grouped into 10 domains. Each permission gates a specific controller action — requests that exceed a key's (or a user's) permissions return 403 Forbidden.

DomainPermissions
Merchantmerchant:view, merchant:edit, merchant:delete, merchant:billing
Invoiceinvoice:view, invoice:create, invoice:edit, invoice:delete
Productproduct:view, product:create, product:edit, product:delete
Customercustomer:view, customer:edit, customer:delete
Teamteam:view, team:invite, team:remove, team:manage_roles
API keyapi_key:view, api_key:create, api_key:edit, api_key:delete
Webhookwebhook:view, webhook:create, webhook:edit, webhook:delete
Checkoutcheckout:view, checkout:edit
Walletwallet:view, wallet:create, wallet:edit, wallet:delete
Reportreport:view

Roles

Dashboard users belong to a team for each merchant they can access, with one of four roles. The role determines the maximum set of permissions they can be granted:

RolePermissionsNotes
OWNER34 (all)One per merchant. Can delete the merchant, manage billing, transfer ownership.
ADMIN32Everything except merchant:delete and merchant:billing.
MEMBER15Read + create/edit on core resources (invoice, product, customer, wallet), view-only on team/keys/webhooks. No deletes.
VIEWER10One *:view per domain. Read-only across the board.

API key permissions

A secret key inherits the permissions of the user that created it, capped by the key type's maximum. Public keys are hard-capped at the minimum surface needed to run a browser-side checkout:

Key typeMax permissions
sk_ (secret)Up to 34 (inherits the user's role cap)
pk_ (public)Exactly 3: invoice:create, invoice:view, product:view

When you create a key, the dashboard lets you further narrow the set — e.g., a build server that only needs to create invoices can be issued an sk_ with just invoice:create, reducing blast radius if the key leaks.

Team-level security enforcement

Beyond per-user permissions, an OWNER can enforce team-wide security requirements (enforceTfa, enforcePasskey, enforceGoogleLogin, enforceGithubLogin, enforceEmailVerified). If an authenticated user hits an endpoint while missing a required factor, the request returns 403 with error code SECURITY_REQUIREMENT_NOT_MET and a missing[] array listing which factors to enable. API-key requests are not subject to this check — only dashboard JWT sessions.

Error responses

See Errors for the full envelope.

StatusCause
401 Unauthorizedx-api-key header is missing, malformed, or the key is revoked
403 ForbiddenKey is valid but lacks the required permission, or a pk_* key tried to reach an sk_*-only endpoint
429 Too Many RequestsKey-scoped rate limit hit — see Rate limits

Security best practices

  • Store keys in environment variables or a secrets manager, never in source code.
  • Create one key per integration — it makes rotation painless.
  • Use public keys (pk_*) for anything that ships to an untrusted client.
  • Revoke immediately from the dashboard if a key is exposed.

On this page