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

# Reliability

> Error envelope, status codes, retry guidance, and limits for the Moda Ingestion API.

This page describes how the Ingestion API behaves under failure: the error envelope, which status codes to retry, and the request limits. It applies to the `/v1/ingest`, `/v1/ingest/multi`, and `/v1/traces` endpoints; the coding-agent `/v1/otel/...` routes use different response shapes and are documented on the [coding agents](/ingestion/coding-agents) page.

## Response envelope

JSON responses share one envelope:

```json theme={"dark"}
{
  "success": false,
  "count": 0,
  "message": "Queue temporarily unavailable",
  "requestId": "550e8400-e29b-41d4-a716-446655440000",
  "retryable": true
}
```

<ResponseField name="success" type="boolean">
  Whether the request was accepted.
</ResponseField>

<ResponseField name="count" type="number">
  Number of events accepted. `0` on failure.
</ResponseField>

<ResponseField name="message" type="string">
  Human-readable explanation. Present on failures and on some successes (for example `No events provided` on an empty batch).
</ResponseField>

<ResponseField name="requestId" type="string">
  UUID identifying the request — your `X-Request-ID` header value if it was a valid UUID, otherwise server-generated. Include it when contacting support.
</ResponseField>

<ResponseField name="retryable" type="boolean">
  Present and `true` on `503` responses. When set, resend the same request after a backoff.
</ResponseField>

<ResponseField name="details" type="object">
  On `/v1/ingest/multi` successes only: accepted-event counts per `messageType`.
</ResponseField>

<Note>
  Two exceptions to the JSON envelope: `404` returns the plain-text body `Not Found`, and protobuf requests to `/v1/traces` receive OTLP protobuf bodies on success and on `503`.
</Note>

## Status codes

| Status | Meaning                 | Common causes                                                                                                                                                                                                                                                           | Retry?                     |
| ------ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| 200    | Accepted                | Events stored, or an empty batch (`count: 0`)                                                                                                                                                                                                                           | —                          |
| 400    | Invalid request         | Malformed JSON; missing `conversation_id` or `role`; unparseable `timestamp`; request-level `environment` outside `development`/`staging`/`production`; more than 1,000 events in a batch; `messageType` events sent to `/v1/ingest`; invalid OTLP body on `/v1/traces` | No — fix the request first |
| 401    | Authentication failed   | Missing `Authorization` header; invalid, expired, or revoked API key                                                                                                                                                                                                    | No — fix the key first     |
| 404    | Unknown route           | Wrong path or HTTP method (plain-text `Not Found` body)                                                                                                                                                                                                                 | No                         |
| 413    | Request too large       | Body over 5 MB                                                                                                                                                                                                                                                          | No — split the batch       |
| 500    | Internal server error   | Unhandled server-side error                                                                                                                                                                                                                                             | Yes, with backoff          |
| 503    | Temporarily unavailable | Ingestion backpressure; response carries `retryable: true`                                                                                                                                                                                                              | Yes, with backoff          |

`400` validation errors name the first failing event by index (for example `Event 3: invalid 'timestamp' (must be an ISO 8601 date-time when provided)`), and nothing from the batch is stored — fix the event and resend the whole request.

## Retries

* Retry `503` and `500` responses, and network errors or timeouts, with exponential backoff and jitter.
* Honor the `retryable` field when present: `retryable: true` means resend the same request unchanged.
* Do not retry `400`, `401`, `404`, or `413` without changing the request — they fail identically every time.
* Resending an unchanged request to the endpoints on this page does not create duplicate data. Send the same `X-Request-ID` on retries to correlate attempts in your logs.

## Limits

| Limit                                               | Value                                          | Exceeded response                        |
| --------------------------------------------------- | ---------------------------------------------- | ---------------------------------------- |
| Request body size (all ingestion endpoints)         | 5 MB                                           | `413` `Request too large`                |
| Events per batch (`/v1/ingest`, `/v1/ingest/multi`) | 1,000                                          | `400` `Batch size exceeds limit of 1000` |
| Spans per request (`/v1/traces`)                    | No count limit; only the 5 MB body cap applies | —                                        |
| Request rate                                        | No rate limits are enforced                    | —                                        |

Batching guidance:

* Group events into batches instead of sending one request per event; a single request can carry up to 1,000 events within 5 MB.
* A batch is validated as a whole: one invalid event rejects the entire request with `400` and nothing is stored.
* If a batch approaches 5 MB, split it into smaller batches.

## Environments

Events are tagged with an environment: `development`, `staging`, or `production` (the default).

* The **request-level** `environment` field on `/v1/ingest` and `/v1/ingest/multi` must be exactly one of those three values; anything else returns `400`.
* **Per-event** `environment` values on `/v1/ingest`, and `moda.environment` / `deployment.environment` attributes on `/v1/traces`, are normalized case-insensitively: `dev` and `develop` map to `development`, `stg` and `stage` to `staging`, `prod` to `production`. Unrecognized values fall back to `production`.

## Key revocation

Revoking an ingestion key takes effect within 60 seconds: the server caches successful key validations for up to 60 seconds, so a just-revoked key may be accepted briefly before requests start returning `401`. Failed validations are never cached — an invalid key fails immediately.

## Next steps

* [Ingestion API reference](/ingestion/http-api) — endpoints, event fields, and exact request/response shapes.
* [Ingestion overview](/ingestion/overview) — choose between the SDKs, OpenTelemetry, and direct HTTP.
* [Authentication](/administration/authentication) — creating and revoking ingestion keys.
* [Limits](/administration/limits) — account-level limits beyond the Ingestion API.
