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

# Delivery guarantees

> How events travel from your app to the ledger: batching, queuing, idempotency, and what a 202 means.

Carbon's pipeline is built so that events are never duplicated and retries are always safe — from the SDK buffer to durable storage.

## The path of an event

```text theme={null}
your app
  → SDK buffer (batched)
  → POST /v1/ingest-events (202 Accepted)
  → durable queue
  → idempotent processing
  → archive + analytics store
```

1. The SDK buffers events and sends them in batches — by count, size, or age. See [Configuration](/documentation/sdk/configuration).
2. The ingest API authenticates the batch, validates every event, attaches your space, and enriches provider and cost.
3. Each event is placed on a durable queue and the API responds `202 Accepted`.
4. A worker processes each event exactly once in effect: the full event is archived, and a lean analytics row is written for querying.

## 202 means queued

The ingest API acknowledges once events are durably queued, not once they are written to the analytics store. Processing typically completes within seconds; events then appear in the dashboard.

## Idempotency

Every event is identified by its client-generated `id`, unique within your space. The first event to arrive with a given ID wins; any later event with the same ID is acknowledged and discarded.

This makes retries safe at every layer:

* The SDK retries failed batches without creating duplicates.
* Your own HTTP retries against the ingest API are safe as long as you reuse the same event `id`.
* Internal queue redeliveries (processing is at-least-once) are deduplicated before storage.

<Tip>
  If you implement your own delivery, generate the event `id` once when the
  call happens — not per send attempt — so retries deduplicate correctly.
</Tip>

## Ordering

Events are timestamped by your application (`startTimeMs`, `endTimeMs`), so analytics are ordered by when calls actually happened, not by when events arrived. Late delivery — a retried batch, a flushed buffer from a recovered process — lands in the right place on the timeline.

## Usage limits

Plan limits are enforced at the API boundary: once a Free space reaches its monthly allowance, or a Plus subscription is inactive, the ingest API rejects the batch with `402` and a stable error id (`usage_limit_exceeded` or `billing_not_active`). The SDK reports these rejections through `onError` (or a throttled console warning) and drops the affected events instead of retrying — see [Configuration](/documentation/sdk/configuration#delivery-errors).

Enforcement is also applied during processing as a backstop, so batches already in flight when a limit is crossed may be partially rejected after a `202`. Rejected events do not appear in the dashboard and are not billed. On Plus, events beyond the included amount are accepted and billed per event. See [Plans and limits](/documentation/dashboard/plans).
