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

# Billing

> Read the public price catalogue, month-to-date usage, invoices, credits and payments. Paying happens in the console.

Billing answers what you have consumed and what you have been charged. It is
**global** — one endpoint, one bill, no region segment:

```
https://billing.basaltic.sh
```

One bill covers your whole organization. Usage from every account inside it
rolls up into a single monthly invoice, so there is no `X-Account-Id` on these
calls — the organization you authenticated as is the whole scope.

<Warning>
  **The billing API is read-only, deliberately.** Every operation here answers
  a question; none of them moves money. Settling an invoice, adding or changing
  a payment method, and billing configuration are console flows at
  [console.basaltic.sh](https://console.basaltic.sh) and have no API
  equivalent. If you are looking for an endpoint to pay an invoice
  programmatically, there is not one.
</Warning>

<CardGroup cols={2}>
  <Card title="Prices" icon="tag" href="#the-public-price-catalogue">
    Public and unauthenticated — the whole catalogue, identical for everyone.
  </Card>

  <Card title="Usage and invoices" icon="receipt" href="#month-to-date-usage">
    What is accruing now, and what has already been billed.
  </Card>

  <Card title="Credits" icon="gift" href="#credits">
    How grants are consumed and where they show up on an invoice.
  </Card>

  <Card title="The collection timeline" icon="clock" href="#what-happens-to-an-unpaid-invoice">
    Retry days, past due, and what non-payment eventually costs you.
  </Card>
</CardGroup>

## The public price catalogue

`GET /v1/prices` takes **no credentials**. It is one of only two unauthenticated
endpoints on the platform — the other is `GET https://iam.basaltic.sh/v1/regions`.

```bash theme={null}
curl https://billing.basaltic.sh/v1/prices?service=compute
```

```json theme={null}
{
  "prices": [
    {
      "sku": "compute.instance.s1.medium",
      "service": "compute",
      "resource_type": "instance",
      "name": "s1.medium",
      "description": "2 vCPU, 4 GB RAM",
      "unit": "hour",
      "unit_price": "0.085",
      "currency": "BRL",
      "metadata": { "class": "shared", "vcpus": 2, "memory_gb": 4 },
      "valid_from": "2026-01-01T00:00:00Z",
      "valid_to": null
    }
  ],
  "as_of": "2026-08-31T14:02:11Z"
}
```

<Info>
  It is public because there is nothing tenant-specific in it. There are no
  account-level rates, discounts or committed-use terms in this table — every
  caller gets the same numbers, which is precisely why it is safe to publish
  and useful to read. It exists so that a pricing page or a cost estimator
  reads the rate billing will actually charge, instead of keeping its own copy
  that drifts the next time something is repriced.
</Info>

Because it takes no credentials, the budget is counted **per client IP**:
100 requests per minute. Read `X-RateLimit-Remaining` and `X-RateLimit-Reset`
rather than hard-coding that; on a `429`, wait `Retry-After` seconds, since
retrying early extends the window. Responses carry
`Cache-Control: public, max-age=300` — the catalogue changes when something is
repriced, not per request, so caching it for five minutes costs you nothing.

### Filters

| Parameter       | Effect                                                      |
| --------------- | ----------------------------------------------------------- |
| `service`       | Only SKUs billed by one service, e.g. `compute`             |
| `resource_type` | Only one resource type, e.g. `instance`                     |
| `sku`           | Exactly one SKU                                             |
| `family`        | Only SKUs whose `metadata.family` matches                   |
| `at`            | Read the catalogue as of an RFC 3339 instant instead of now |

`family` is how the managed products are told apart from the general compute
flavors they share a `resource_type` with — load balancer replicas and database
cluster nodes are billed as instances, but they are their own family.

`at` is what you use to explain a past invoice: pass the invoice's
`period_start` and you get the rates that were in force then. `as_of` in the
response echoes the instant the rows were selected as of, so a client can say
which revision of the catalogue it is holding.

<Note>
  Money is a **decimal string**, never a JSON number, everywhere in this API.
  `"0.085"` survives a round trip through any language's JSON parser exactly;
  a float does not. The quoted rate is the one that will be charged, so it
  cannot be allowed to round differently on the way out.
</Note>

There is no pagination on this endpoint. The catalogue is the whole answer —
a client that had to page it could observe half of one revision and half of the
next.

## Month-to-date usage

```bash theme={null}
GET /v1/usage
```

Returns unbilled usage accrued so far in the current **UTC** month, with a
per-SKU breakdown ordered by cost:

```json theme={null}
{
  "amount": "42.87",
  "period_start": "2026-08-01T00:00:00Z",
  "items": [
    { "sku": "compute.instance.m1.small", "description": "m1.small",
      "quantity": "412.5", "unit": "hour", "amount": "26.8125" }
  ]
}
```

Line `amount` carries four decimal places while the total carries two. That is
not inconsistency — early in a month a line can be worth a fraction of a cent,
and rounding it to two places would render it as `0.00` and make it look like
nothing is accruing. The total, and every figure on an invoice, stays at the
ledger's two.

## Invoices

An invoice is generated on the **1st of each month**, covering the previous UTC
calendar month, one per organization.

```bash theme={null}
GET /v1/invoices              # one page, no line items
GET /v1/invoices/{invoice_id} # the invoice with its line items
```

`period_start` is the first day of the billed month and `period_end` is
**exclusive** — the first day of the following month. Late-arriving usage from
older months sweeps into the next invoice generated rather than reopening a
closed one, so an invoice's line items are not always confined to its labelled
period.

The arithmetic is `subtotal - credits_applied = total`. Usage lines carry
`kind: "usage"`; credit lines carry `kind: "credit"` and a negative `amount`.

<Note>
  `items` is populated only on the detail endpoint. `GET /v1/invoices` returns
  the invoice documents without line items, because a list of a year's invoices
  with every line expanded is a large response nobody asked for.
</Note>

### Statuses

| Status          | Meaning                                                    |
| --------------- | ---------------------------------------------------------- |
| `open`          | Issued and unpaid. Collection is in progress.              |
| `paid`          | Settled. Also how a written-off invoice reads — see below. |
| `past_due`      | The retry schedule ran out without collecting.             |
| `uncollectible` | Given up on.                                               |
| `void`          | Cancelled; nothing is owed.                                |

`due_at` equals `issued_at`. An invoice is due when it is issued, and the first
charge attempt happens immediately — the days that follow are retries, not a
grace period.

<Info>
  **Small invoices are written off rather than charged.** A total below **1.00**
  in the invoice currency is written off at generation, and the invoice reads
  `paid` without any payment ever having been attempted. The cost of collecting
  a sub-unit amount exceeds the amount.
</Info>

### The PDF statement

```bash theme={null}
GET /v1/invoices/{invoice_id}/pdf
```

Rendered on demand from the invoice's current state, under the same
authorization as the invoice document — there is no stored file to drift out of
sync with the status it shows. The `pdf_url` field on an invoice is the path to
this endpoint, not a pre-signed link you can hand to someone else.

## Credits

```bash theme={null}
GET /v1/credits
```

A credit grant carries the `amount` it was issued for and the `remaining`
balance, plus a `source` — `promo`, `coupon`, `adjustment` or `migration` — and
an optional `expires_at`.

Grants are consumed at invoice generation, **soonest-expiring first**, until the
subtotal is covered. Each consumed slice becomes its own negative line on the
invoice and its own `credit_applied` entry in the ledger, so you can always
trace which grant paid for what.

<Note>
  Credits are applied automatically. There is no endpoint to apply one to a
  particular invoice, and none to redeem a code — a code is redeemed in the
  console, which is what creates the grant.
</Note>

## Transactions and payments

```bash theme={null}
GET /v1/transactions
GET /v1/payments
```

`GET /v1/transactions` is the ledger: `payment`, `refund`, `adjustment`,
`credit_grant` and `credit_applied` entries.

<Warning>
  Transaction `amount` is **always positive**. The direction lives in `type`,
  not in the sign. Summing amounts without reading types gives you a number
  that means nothing.
</Warning>

`GET /v1/payments` lists charge attempts. Each row carries `attempt`, a 1-based
counter within the collection schedule for its invoice, and a `status` of
`pending`, `processing`, `succeeded`, `failed` or `refunded`. Several rows
against one invoice is the normal shape of a retry sequence, not a sign of
duplicate charges.

## What happens to an unpaid invoice

Collection runs on a fixed schedule from the moment the invoice is issued:

```mermaid theme={null}
flowchart LR
    A["Day 0<br/>issued<br/>attempt 1"] --> B["Day 3<br/>attempt 2"]
    B --> C["Day 5<br/>attempt 3"]
    C --> D["past_due"]
    D --> E["Day 7<br/>suspended"]
    E --> F["Day 15<br/>terminated"]
    A -.paid.-> G["settled"]
    B -.paid.-> G
    C -.paid.-> G
    D -.paid.-> G
```

If none of the three attempts collects, the invoice moves to `past_due`.

<Warning>
  **Non-payment eventually costs you your resources.** At day 7 the
  organization is suspended. At day 15 it is terminated, and its resources are
  deleted after that. Every step re-checks the invoice first, so settling it at
  any point stops the sequence immediately.
</Warning>

Settle a `past_due` invoice from the console. That is also where you fix the
payment method that caused the declines — the API has no path to either.

## Pagination

`GET /v1/invoices`, `/v1/credits`, `/v1/transactions` and `/v1/payments` all
page the same way: pass `limit` (default 20, maximum 100) and echo back
`meta.marker` from the previous page.

<Warning>
  A `limit` above the maximum is clamped, not rejected, so a page shorter than
  the one you asked for is normal. Page until `meta.has_more` is `false` — not
  until a page looks short.
</Warning>

## Permissions

| Action                     | Endpoint                            |
| -------------------------- | ----------------------------------- |
| `billing:GetCurrentUsage`  | `GET /v1/usage`                     |
| `billing:ListInvoices`     | `GET /v1/invoices`                  |
| `billing:GetInvoice`       | `GET /v1/invoices/{id}` and its PDF |
| `billing:ListCredits`      | `GET /v1/credits`                   |
| `billing:ListTransactions` | `GET /v1/transactions`              |
| `billing:ListPayments`     | `GET /v1/payments`                  |

`GET /v1/prices` needs no permission, because it needs no identity.

<Warning>
  **Billing policies cannot be scoped to a resource.** Every billing action
  authorizes against `*` — the organization boundary is the whole tenant fence
  here, since there is one bill and it belongs to the organization rather than
  to any account inside it. A policy granting `billing:GetInvoice` grants it
  for every invoice; there is no way to narrow it to one.

  Grant billing read access at the group level to the people who need it, not
  broadly. See [policies](/iam/policies).
</Warning>

## Next

<CardGroup cols={2}>
  <Card title="Rate limits" icon="gauge" href="/authentication">
    How the `X-RateLimit-*` headers work, and signing every other request.
  </Card>

  <Card title="Policies" icon="shield" href="/iam/policies">
    Who in your organization can read the bill.
  </Card>

  <Card title="Regions" icon="globe" href="/regions">
    Why billing has no region segment.
  </Card>

  <Card title="API reference" icon="code" href="/api-reference/introduction">
    Every billing operation, with request and response schemas.
  </Card>
</CardGroup>
