# Fynex Billing API > Public REST API for billing: invoices, subscriptions, contracts, metered > usage and credit balances. Base path `/billing-api/v1`. Authenticate with a > seller secret key (`sk_test_…` or `sk_live_…`) as `Authorization: Bearer`. ## Instructions for coding agents Facts worth having before you write a line against this API. Each one is a mistake this API has actually seen. - **Money is an integer count of minor units.** A field ending `Minor` carries `4999` for €49.99. Never send `49.99` to one. The sibling Payments API's checkout uses major units, so an agent that read that first will get this wrong by a factor of a hundred in either direction. - **Rates are integer basis points.** A field ending `Bps` carries `275` for 2.75%. - **Quantities are decimal strings** — `"1250.5"`, not a JSON number, because JSON numbers are floats and metered quantities must not drift. - **Per-unit rates are the one exception**: decimal strings in MAJOR units (`"0.004"`), because a rate is routinely finer than one minor unit. - **Contracts CAN be created and amended.** `POST /contracts` with a required `Idempotency-Key` creates one in `draft` for a `sellerCustomerId` you own (the same key answers `422`, naming the field, when the request differs in its customer, currency, start or end date, its line items — compared line by line on component type, config and quantity, not merely counted — or any customer detail field the request states). Creating a contract also carries an EXTRA per-seller quota — 20 a rolling day by default — bounding how fast one key can open contracts. `POST /contracts/{contractId}/amendments` appends a version — a status move, new dates, a replaced component set — and is a compare-and-swap: send the `expectedBaseVersion` you last read, or get `422` and re-read. Nothing on a contract is ever edited in place. Amending carries its OWN extra per-seller quota — 200 a rolling day by default — because every accepted amendment appends a version that can never be deleted. All three write quotas (create, amend, issue) fail closed — `503` + `Retry-After` while the limiter is unreachable, nothing written; see [Errors → rate limiting](/billing-api/v1/docs/errors#when-the-write-quotas-fail-closed). - **Invoices CAN be issued in one call.** `POST /invoices` with a required `Idempotency-Key` composes, numbers and — unless `send` is `false` — sends the document. It is issued and immutable the moment the call answers `201`; a delivery failure still answers `201` with `deliveryStatus: "failed"` and no `paymentLinkUrl`, and `POST /invoices/{invoiceId}/send` is how you deliver it later. `deliveryStatus` is `sent`, `not_requested` (`send: false`, or already settled from credit) or `failed` — READ IT rather than inferring delivery from a missing link; it is absent on a replay. Issuing also carries an EXTRA per-seller quota — 500 a rolling day by default — because it allocates a number from your own gapless series. The same key answers `422`, naming the field, when the request differs in its contract, customer, currency, `invoiceType`, `jurisdiction`, `dueDate`, `dateOfSupply`, a stated `exemptionReason` or `legalNotice`, `notes`, `poReference`, `paymentTerms`, the buyer block, the seller block as the engine USED it (under Fynex-collection issuance the issuer is substituted, so a differently stated seller still replays), the net line total, the line count, or any line's `description`, `quantity`, `unitPriceMinor`, `discountMinor`, `taxCategory`, `taxRatePercent` or `taxable` — the tax fields are compared because the net total cannot see them. `send` is NOT compared: a retry that flips it still replays. `invoiceType` is `standard` (default), `simplified` or `modified` — **`credit_note` is refused with `400`**, because a correction is raised against the document it corrects and cannot be composed from scratch here. `buyer.email` is required: it is where the document is delivered. There are no drafts on this API, and no `catalogItemId` on its lines: send explicit prices. - **Credit top-ups CAN be made.** `POST /contracts/{contractId}/credits/top-up` with a required `Idempotency-Key` header grants a credit lot: `creditType`, `amountMinor` (minor units), `currency` (must equal the contract's), an optional `expiresAt`, and a `reason`. Unlike the subscription create the body IS compared — the same key with a different contract, amount, currency, `creditType`, `expiresAt`, `sellerCustomerId` or `reason` answers `422` naming the field, not the earlier entry. `purchased` and `enterprise` credit was paid for and may never carry an expiry. - **Customers CAN be created.** `POST /customers` with an `email` resolves or creates one, and returns the `sellerCustomerId` contracts, credit top-ups and invoices refer to. There is NO `Idempotency-Key` here — the address is the natural key — and the answer is always `200`, never `201`: the directory cannot report whether this call is the one that minted the record. Details are fill-if-empty; an existing value is never overwritten. - **Subscriptions CAN be created and driven.** `POST /contracts/{contractId}/subscriptions` with an `Idempotency-Key` header (required — the same key replays the first result), then `POST /subscriptions/{id}/cancel | pause | resume | end-trial | change-plan`. A state the subscription cannot take answers `422`, never `409`. - **They can still appear, and settle, without you.** When the seller has the issuance passes enabled, subscription terms and closed usage are invoiced on a schedule; when they have off-session collection enabled, a sent invoice is charged against the customer's saved card by a background loop. So a list can grow and a document can reach `paid` with no call of yours. Never write code that assumes it is the only thing acting on this account. - **Metering is fully writable**: register a metric, price it, send events singly, in batches or by CSV, and preview what a configuration charges before anything is metered. - **Issued invoices are immutable.** There is no update and no delete. A correction is a new credit note; both documents stay visible. - **Age `outstandingMinor`, never `collectibleMinor`.** `collectibleMinor` is frozen at issue — grand total less credit drawn down then — so it is blind to every credit note, write-off and settlement that followed, and an AR report built on it over-states what is owed. `outstandingMinor` is derived from the adjustment ledger on every read. It is OMITTED when that ledger is unavailable: absent means unknown, never zero. - **Billing emits webhooks** through the same signed pipe as payment events — `InvoiceIssued`, `InvoiceSent`, `InvoicePaid`, `InvoiceOverdue`, `InvoiceVoided`, `InvoiceWrittenOff`, `CreditApplied`, `CreditDepleted`, and the ten `BillingSubscription*` lifecycle events. Every one is published in the OpenAPI document's `webhooks` block, so generate the body type rather than hand-writing it. Do not tell a user to poll for settlement when they can subscribe. Polling `GET /invoices` remains right for period reconciliation. - **A delivery is an envelope, not a bare payload**: `eventId`, `eventType`, `sellerAccountUuid`, `occurredAt`, and the event's fields nested under `payload`. Deduplicate on `eventId`. The subscription events are prefixed `Billing…` because the unprefixed `Subscription*` events on the same pipe belong to payment-link subscriptions — a different object. - **Every invoice needs a contract**, but a one-off does not need you to create one: an `origin: adhoc` invoice provisions the customer and contract from the bill-to. - **Credit applies after tax.** It draws down the grand total as a payment method; it is not a discount and does not reduce the taxable base. - **Paging is keyset, and the parameter is `cursor` on every list.** Pass the returned `nextCursor` back as `cursor` until `hasMore` is false. Direction varies by list and is documented per endpoint — contracts walk forward, everything else newest first — but the parameter name never varies. There is no `offset` and no `page`; do not construct one. (`beforeId` / `afterId` are the original names, still accepted and still returned.) - **Errors are `{"error": "…"}`.** `403` is an inactive seller, `404` is an unknown id *or* one belonging to another seller, `422` is an action that does not apply to an object that exists, `501` is a capability disabled on the deployment. Branch a retry on these — they mean different things. - **`sellerAccountId` is you; `sellerCustomerId` is who pays you.** One word apart, entirely different things. The party you pay OUT to is a `payee`, on the Payments API. (In builder words: merchant = seller, buyer = customer, vendor/supplier you disburse to = payee.) - **Every response carries `X-Request-Id`.** Log it; it is what support asks for. ## Reference - [Complete API reference (markdown)](/billing-api/v1/docs.md) — every operation with parameters, response shapes and failure modes, in one file. - [OpenAPI 3.1 document](/billing-api/v1/openapi.json) — the machine contract; generate a client from this rather than hand-writing one. - [Rendered documentation](/billing-api/v1/docs) Per-page markdown, when you want one topic rather than the whole file: - [Concepts](/billing-api/v1/docs/concepts.md) — the five objects and how they relate - [Quickstart](/billing-api/v1/docs/quickstart.md) - [Authentication](/billing-api/v1/docs/authentication.md) - [Endpoint reference](/billing-api/v1/docs/endpoints.md) - [Workflows](/billing-api/v1/docs/workflows.md) - [Usage ingestion](/billing-api/v1/docs/usage-ingestion.md) — the write half of metered billing - [Webhooks](/billing-api/v1/docs/webhooks.md) — the event catalog and signing - [Errors](/billing-api/v1/docs/errors.md) · [Pagination & amounts](/billing-api/v1/docs/pagination.md) · [Sandbox](/billing-api/v1/docs/sandbox.md) - [Invoices](/billing-api/v1/docs/invoices.md) · [Subscriptions](/billing-api/v1/docs/subscriptions.md) · [Usage](/billing-api/v1/docs/usage.md) · [Credits](/billing-api/v1/docs/credits.md) · [Code examples](/billing-api/v1/docs/code-examples.md) ## Related APIs - **Payments API** (`/payments-api/v1`) — payments, payouts, wallets and top-up invoices. Note its checkout takes major units where billing takes minor; check the field name before assuming. - **Payment Links API** (`/api/v1/payment-links`) — standalone links. A billing invoice raises its own link via `POST /billing-api/v1/invoices/{invoiceId}/send`.