> ## Documentation Index
> Fetch the complete documentation index at: https://invoiceapi.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Conventions

> Auth, test vs live, money, pagination, idempotency, metadata, versioning, rate limits, and Request-Id — the rules every endpoint follows.

Every endpoint follows the same rules. Learn them once and the whole API is predictable.

## Authentication

Send `Authorization: Bearer sk_test_...` (test) or `sk_live_...` (live) on every `/v1` request;
anything else is `401 auth.unauthorized`. A revoked key is `401 auth.key_revoked`. Manage keys via
[API keys](/guides/api-keys).

## Test vs live

The key's prefix decides the mode, and the mode scopes **all** data: a `sk_test_` key never sees
live resources and vice-versa. Every resource carries `livemode`. Test mode uses the simulated
stamping service and tolerates the sample CSD; see [Testing](/guides/testing).

## Money and time

Money is always a **decimal string** (`"116.00"`) — never a float — so cents never drift. Amounts
are computed and rounded half-up at the currency's decimal places. Times are ISO-8601 UTC
(`2026-07-10T18:25:43Z`). Ids are prefixed, time-ordered, base32 ULID-style: `org_`, `key_`,
`fp_`, `inv_`, `dl_`, `we_`, `evt_`, `whd_`, `req_`.

## Pagination

List endpoints take `?limit=` (1–100, default 20) plus **one** cursor:

* `?starting_after=<id>` — the next (older) page
* `?ending_before=<id>` — the previous (newer) page

Sending both is `422 request.invalid_pagination`. Responses are newest-first:

```json theme={null}
{ "object": "list", "url": "/v1/invoices", "data": [ ... ], "has_more": true }
```

## Idempotency

Send `Idempotency-Key: <unique>` on any POST. The first response is stored for 24h and replayed
byte-for-byte for identical retries (replays add `idempotent-replayed: true`). A different body
under the same key is `409 idempotency.key_reuse`; an in-flight duplicate is
`409 idempotency.key_processing`. This makes retrying a `502` safe — no duplicate stamp.

## Metadata

Fiscal profiles, invoices, webhook endpoints, and connected accounts accept a `metadata` object of
string→string (≤50 keys, key ≤40 chars, value ≤500). It round-trips untouched and always appears
(default `{}`). On PATCH, metadata is **merged**: send a key to set it, or a key with an
empty-string value to delete it.

```json theme={null}
{ "metadata": { "order_id": "ord_1789", "obsolete_key": "" } }
```

## Expand

Where documented, `?expand=<field>` inlines a related resource. Invoices support
`?expand=fiscal_profile`. An unsupported value is `422 request.invalid_expand`.

## Versioning

Pin a dated release with the `Invoice-Version: 2026-07-15` header (Stripe-style). Omit it to use
the current version. An unknown value is `400 request.invalid_version`. A key may also carry a
pinned version. Precedence: **request header → key pin → current**. The resolved version is echoed
on every response's `Invoice-Version` header.

Versioning is **real, not cosmetic** — each dated version pins a response *shape*, so an old
integration keeps exactly the contract it was built against. Discover every version at
[`GET /v1/versions`](/api-reference) (public, no auth) and read the diff in the
[Changelog](/concepts/versioning). A version scheduled for removal is served with RFC 8594
`Deprecation: true` and a `Sunset` HTTP-date on every response.

## Rate limits

A token-bucket limiter admits a steady rate with a burst. Authed responses carry `RateLimit-Limit`,
`RateLimit-Remaining`, and `RateLimit-Reset`. A throttled request is `429 rate_limit.exceeded` with
a `Retry-After` header — back off and retry.

## Request-Id

Every response carries a `Request-Id` header; it is also the `request_id` in every error body.

<Tip>Log the `Request-Id`. Support and idempotency both key off it.</Tip>

## Usage

Every metered action is recorded on a read-only stream. [`GET /v1/usage`](/api-reference) returns
cursor-paginated `usage_record` objects for the acting org + mode — the Stripe usage-record analog.
Today a fresh CFDI stamp emits one record (`kind: "stamp"`, `quantity: 1`, `resource_id` the
`inv_…`); idempotent replays and duplicate re-stamps never double-count. Filter with `?kind=`,
`?date_from=`, and `?date_to=` (inclusive bounds on `created_at`).

```json theme={null}
{ "object": "usage_record", "id": "ur_…", "kind": "stamp", "quantity": 1,
  "resource_id": "inv_…", "livemode": false, "created_at": "2026-07-10T18:25:43Z" }
```

## Errors

Errors are `application/problem+json` (RFC 9457) with a stable `code`, a human- and agent-readable
`remediation`, an optional `pac` object, `request_id`, and a `doc_url` deep-link. Switch on `code`,
show `remediation`. The full catalog lives in the [Error reference](/concepts/errors).
