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

# Billing

> How billable requests are charged, idempotency, and DEGRADED policy.

This page describes how the API meters and charges requests. It applies
when your Organization has an active subscription and uses an `atk_*`
API key.

## What counts as a billable request

| Counts                   | Does not count                  |
| ------------------------ | ------------------------------- |
| `POST /v1/evaluate`      | `GET /v1/sources`               |
| `POST /v1/intersections` | `GET /v1/rulesets` (list/get)   |
| `POST /v1/distance`      | `POST /v1/rulesets` (create)    |
| `POST /v1/subjects`      | `?explain=true` (metadata only) |

Catalog reads, ruleset writes, and `?explain=true` calls never consume
quota and are never charged.

## Per-request gates

Every billable request passes three gates, in order:

1. **Authentication** — the bearer token must be a valid `atk_*` key
   issued to your Organization.
2. **Subscription** — your subscription must be active. An expired or
   suspended subscription returns `402 SUBSCRIPTION_INACTIVE` and the
   request is not executed.
3. **Quota** — you must be within your contracted monthly cap for the
   current billing period. Over the cap returns `429 QUOTA_EXCEEDED`
   and the request is not executed.

A request that fails any of these gates is **not charged**.

## Billing period

Quota is counted against your **Stripe billing period**, not against
the calendar month. The boundaries come from your subscription —
typically the day of the month you signed up — and are returned in
the `period_started_at` / `period_ends_at` fields on a `429`
response. See [Quotas & rate limits](/reference/quotas).

## Idempotency

Send an `Idempotency-Key` header on every billable request:

```http theme={null}
POST /v1/evaluate
Idempotency-Key: client-job-2026-04-18-7842
Content-Type: application/json
Authorization: Bearer atk_live_...
```

Format: `[A-Za-z0-9_:.-]{8,128}`. Anything else returns
`422 IDEMPOTENCY_KEY_INVALID`.

Keys are scoped per Organization. The contract:

| Outcome                      | HTTP | Meaning                                                         |
| ---------------------------- | ---- | --------------------------------------------------------------- |
| Same key, same body, charged | 200  | Replay — original response snapshot is returned.                |
| Same key, different body     | 422  | `IDEMPOTENCY_KEY_CONFLICT` — pick a new key.                    |
| Same key, too many retries   | 429  | `IDEMPOTENCY_KEY_EXHAUSTED` — pick a new key.                   |
| Same key, snapshot expired   | 410  | `IDEMPOTENCY_REPLAY_EXPIRED` — key reusable for a fresh charge. |

<Note>
  Replay returns the original response **without re-running the
  evaluation and without charging again**. This is the safe way to retry
  on network errors: the second request either replays the prior result
  (no extra charge) or runs fresh (one charge total).
</Note>

## DEGRADED responses are not charged

A response with `status: "degraded"` means at least one source could
not be evaluated for an infrastructure reason (upstream provider
timeout, transient error). The response is **incomplete**, not wrong.

By default, **DEGRADED responses do not consume quota and are not
charged**. The `Idempotency-Key` stays reusable, so you can retry the
same request once the upstream source recovers and the retry will
either succeed (one charge total) or come back DEGRADED again (still
zero charges).

For the wrappers `/v1/intersections`, `/v1/distance`, and
`/v1/subjects`, a single source that fails is reported with
`status: "failed"` on its `SourceOutcome`. The overall response can
still be useful — the other sources returned data — but the request
is not charged.

See [Verdicts](/concepts/verdicts) for the full status/outcome
semantics.

## Recommended retry strategy

1. Generate a fresh `Idempotency-Key` per logical job (a UUID, or your
   own job id).
2. On `5xx`, network error, or `status: "degraded"`: retry with the
   **same** key. You will either replay a charged result, or run fresh.
3. On `429 QUOTA_EXCEEDED`: stop until `period_ends_at` or contact us
   to bump the cap.
4. On `429 RATE_LIMIT_EXCEEDED`: back off for `Retry-After` seconds
   with exponential backoff + jitter.
5. On `422 IDEMPOTENCY_KEY_CONFLICT` or `429 IDEMPOTENCY_KEY_EXHAUSTED`:
   pick a new key (the original key is locked to a different payload).

## Errors

All billing-related errors follow the standard envelope. Match against
the `code` field, not against `title` or `detail`. See
[Errors](/reference/errors) for the complete code list.
