Skip to content
Biller

Developers

An API you can build a business on.

Biller is API-first: the console is just another client of the same public endpoints. Commands are safe to retry, previews are priced by the engine that bills, and every change you care about arrives as a signed event.

Base path
/v1
Auth
Bearer sk_test_… / sk_live_…
Money
integer minor units, ISO 4217
Ids
UUIDv7
Time
RFC 3339, UTC
Errors
RFC 9457 problem details

Conventions

Predictable by design.

Every endpoint follows the same rules, so once you have called one you know how the rest behave.

Environment from the key

A test key reads and writes test data; a live key, live data. The tenant and environment always come from the credential, never from the request.

Envelopes

One object comes back as {"data": …}. Lists add has_more and next_cursor, take a limit of 1 to 100, and accept ids[] for batch lookups.

Exact numbers

Posted amounts are integers in the currency’s minor unit, in fields ending _minor. Rates, unit prices and quantities are decimal strings, so nothing is lost to floating point.

Dates that mean something

Instants are RFC 3339 UTC. Business dates such as due dates are YYYY-MM-DD, read in the subscription’s or contract’s billing time zone.

Optimistic concurrency

Versioned resources return ETag: "vN". Send If-Match with a PATCH and a stale write is answered with 412 instead of overwriting someone else’s change.

Flat resources

Responses carry foreign-key ids such as customer_id instead of nested objects, so payloads stay small and every shape is stable.

Errors you can branch on.

Every failure is an application/problem+json document with a stable code, validation errors by field, and the request id to quote when you contact us.

POST /v1/payments
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
X-Request-Id: 01a10b11-9428-738c-a439-fdd153a72659

{
  "type": "https://docs.biller.example/problems/idempotency-key-required",
  "title": "An Idempotency-Key header is required.",
  "status": 400,
  "detail": "Send an Idempotency-Key header (1-255 visible characters) with this request.",
  "instance": "/v1/payments",
  "code": "IDEMPOTENCY_KEY_REQUIRED",
  "request_id": "01a10b11-9428-738c-a439-fdd153a72659"
}

Idempotency

Retry anything. Nothing happens twice.

Creation and money commands require an Idempotency-Key. The first request claims it; a retry with the same request gets the stored response back.

  • Keys are scoped to the environment and kept for 24 hours
  • Server errors, auth failures and rate limits aren’t stored, so they can be retried
  • Contract execution, billing periods, late fees and usage events are idempotent even without the header
  • Payment attempts carry their own reference to the gateway, so an uncertain charge is reconciled, not repeated
POST /v1/payments
curl "$BILLER_API/payments" \
  -H "Authorization: Bearer $BILLER_API_KEY" \
  -H "Idempotency-Key: 7c1f0e8a-0a51-4b9b-9d64-51f6b3a0c2d8" \
  -H "Content-Type: application/json" \
  -d @payment.json
Responsewhat comes back
# Same key, same request: the stored response, unchanged
Idempotent-Replayed: true

# Same key, different body
409  IDEMPOTENCY_KEY_REUSED

# Same key while the first request is still running
409  IDEMPOTENCY_REQUEST_IN_PROGRESS

Previews

The preview is the invoice.

Changes with money attached can be priced before they are made. A preview writes nothing, and committing the same input posts exactly what it showed.

  • POST /v1/subscriptions/{id}/preview-changeQuantity, price and item changes with their proration
  • POST /v1/contracts/{id}/amendments/{amendment}/previewThe next contract version, its totals and the exact proration per subscription

Every computed line carries a calculation_trace: the period, the fraction of it remaining, the pricing used and the policy versions that produced the number.

Webhooks

Signed CloudEvents, delivered until they land.

Each change is recorded in the same transaction as the event that announces it, then delivered to your endpoints as a CloudEvents 1.0 document.

  • Biller-SignatureHMAC-SHA256 over the timestamp and body
  • Rotationboth secrets sign for 24 hours after a rotation
  • Retries1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h
  • Replayany delivery, from the API or the console
  • FencedHTTPS only; no private, loopback or metadata addresses
{
  "specversion": "1.0",
  "id": "01a10b01-62e9-737e-8f5b-f16c3ed1ef81",
  "type": "com.biller.billing.invoice.finalized.v1",
  "source": "https://api.biller.example/tenants/01a10b01-032f-70be-9a73-460a06da1b18",
  "subject": "invoices/01a10b01-62b8-71fb-b218-faaa510fcf97",
  "time": "2026-09-25T14:00:00.000Z",
  "datacontenttype": "application/json",
  "tenantid": "01a10b01-032f-70be-9a73-460a06da1b18",
  "environment": "test",
  "aggregateversion": 4,
  "data": {
    "id": "01a10b01-62b8-71fb-b218-faaa510fcf97",
    "number": "INV-000020",
    "status": "open",
    "currency": "CAD",
    "total_minor": 551,
    "amount_due_minor": 551,
    "due_date": "2026-10-02"
  }
}

Event types

Customers
customer.created · updated · archived
Quotes
quote.sent · accepted · rejected · expired
Contracts
contract.executed · amended · activated · renewed · expired · terminated
Subscriptions
subscription.created · updated · paused · resumed · canceled · expired
Invoices
invoice.created · finalized · paid · partially_paid · voided · marked_uncollectible
Credit notes
credit_note.issued
Payments
payment.processing · succeeded · failed · returned
Refunds
refund.succeeded · failed
Payment methods
payment_method.attached · detached
Mandates
mandate.activated · canceled
Receivables
collection_action.executed · payment_plan.defaulted
Access
entitlements.updated

Entitlements

One call decides access.

Your product asks whether a customer may use a feature, optionally for a quantity. The answer combines every grant in effect, overrides and service holds.

granted
The feature is on
within_limit
The requested quantity fits the limit
limit_exceeded
It doesn’t
disabled
The feature is granted but switched off
not_granted
Nothing grants the feature
suspended
A service hold is in place
curl "$BILLER_API/entitlements/check" \
  -H "Authorization: Bearer $BILLER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "01a10b01-062f-721a-a7e8-f2630010fa44",
    "feature_key": "max_vehicles",
    "quantity": "58"
  }'
Response200 OK
{
  "data": {
    "allowed": true,
    "reason": "within_limit",
    "feature_key": "max_vehicles",
    "value_type": "quantity",
    "value": "60"
  }
}

What’s in /v1

The whole product, as endpoints.

Everything the console does, your code can do: the same endpoints, the same validation, the same audit trail.

Customers
customers, balances, payment methods, statements
Catalog
products, prices, tax rates, features, meters
Commercial
quotes, public acceptance links, contracts, amendments
Billing
subscriptions, previews, invoices, credit notes, pending charges
Payments
payments, refunds, allocations, mandates, PAD files, settlements
Receivables
collection policies and steps, payment plans, aging, communications
Access
entitlement checks, overrides, service holds, usage events and summaries
Reporting
overview, ledger accounts and journal entries
Platform
API keys, webhook endpoints and deliveries, events, audit log, team

Build on Biller.

Get a test environment and keys, or walk through your billing with us first.