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

# Limits & Error Handling

> API limits, error codes, and troubleshooting guide for the Events API.

## Rate limits and constraints

| Limit                | Value      |
| -------------------- | ---------- |
| Users per request    | 1,000      |
| Maximum payload size | 10 MB      |
| Request timeout      | 10 seconds |

<Info>
  If you need higher limits for bulk imports or high-throughput pipelines, contact us at [support@revbridge.ai](mailto:support@revbridge.ai).
</Info>

## HTTP status codes

| Status | Meaning                                                          | Action                                                  |
| ------ | ---------------------------------------------------------------- | ------------------------------------------------------- |
| `202`  | **Accepted** — at least one user was processed successfully      | Check `users_rejected` for partial failures             |
| `400`  | **Bad Request** — invalid payload or all users failed validation | Fix the errors listed in the response and retry         |
| `401`  | **Unauthorized** — missing or invalid API key / customer ID      | Verify your `Authorization` and `x-customer-id` headers |
| `405`  | **Method Not Allowed** — wrong HTTP method                       | Use `POST` for the ingest endpoint                      |
| `413`  | **Payload Too Large** — body exceeds 10 MB                       | Split your data into smaller batches                    |
| `503`  | **Service Unavailable** — temporary processing issue             | Retry with exponential backoff                          |

## Error response formats

### Validation errors (400 / 202 with rejected users)

When users fail validation, the response includes an `errors` array with the index of the failed user and a message describing the issue.

```json theme={null}
{
  "trace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "users_accepted": 1,
  "users_rejected": 2,
  "errors": [
    {
      "userIndex": 1,
      "message": "identifiers is required"
    },
    {
      "userIndex": 2,
      "message": "events[0]: event_name is required"
    }
  ]
}
```

The `userIndex` corresponds to the zero-based position in the `users` array of your request.

### Full-rejection errors (400)

When **all** users in the request fail validation, the response uses the error format:

```json theme={null}
{
  "error": "validation failed",
  "trace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "errors": [
    {
      "userIndex": 0,
      "message": "at least one identifier is required (user_id, email, phone_number, or anon_id)"
    }
  ]
}
```

### Authentication errors (401)

```json theme={null}
{
  "error": "unauthorized"
}
```

### Service errors (503)

```json theme={null}
{
  "error": "service temporarily unavailable",
  "trace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

## Troubleshooting

<Accordion title="401 — 'unauthorized'">
  **Possible causes:**

  * Missing `Authorization` header
  * API key does not start with `Bearer ` prefix
  * Invalid or revoked API key
  * Missing `x-customer-id` header
  * The API key does not belong to the specified customer ID

  **Resolution:**

  1. Verify you are sending both headers: `Authorization: Bearer <KEY>` and `x-customer-id: <ID>`
  2. Check that your API key is active in **Settings → API Keys**
  3. Ensure the customer ID matches the account that owns the API key
</Accordion>

<Accordion title="400 — 'invalid JSON body'">
  **Possible causes:**

  * Malformed JSON (missing commas, unclosed brackets, etc.)
  * Body is empty or not valid JSON
  * Content-Type header is not `application/json`

  **Resolution:**

  1. Validate your JSON with a linter before sending
  2. Ensure the `Content-Type: application/json` header is set
  3. Check that the request body is not empty
</Accordion>

<Accordion title="400 — 'users array is required and must not be empty'">
  **Possible causes:**

  * The `users` field is missing from the JSON body
  * The `users` array is empty (`[]`)

  **Resolution:**
  Include at least one user object in the `users` array.
</Accordion>

<Accordion title="400 — 'at least one identifier is required'">
  **Possible causes:**

  * The `identifiers` object is missing or empty
  * None of the four primary identifiers (`user_id`, `email`, `phone_number`, `anon_id`) are present

  **Resolution:**
  Add at least one primary identifier to the user's `identifiers` object.
</Accordion>

<Accordion title="400 — 'events array is required and must not be empty'">
  **Possible causes:**

  * The `events` field is missing from a user object
  * The `events` array is empty

  **Resolution:**
  Include at least one event for each user.
</Accordion>

<Accordion title="400 — 'event_name is required'">
  **Possible causes:**

  * An event object is missing the `event_name` field
  * The `event_name` value is an empty string

  **Resolution:**
  Every event must have a non-empty `event_name` string.
</Accordion>

<Accordion title="400 — 'revenue must be a number'">
  **Possible causes:**

  * The `revenue` field is a string instead of a number
  * The value is not a valid number

  **Resolution:**
  Send `revenue` as a numeric value: `"revenue": 49.99`, not `"revenue": "49.99"`.
</Accordion>

<Accordion title="400 — 'timestamp must be valid ISO 8601'">
  **Possible causes:**

  * The timestamp is in a non-standard format
  * Invalid date values

  **Resolution:**
  Use ISO 8601 format: `2026-01-15T14:30:00Z` or `2026-01-15T14:30:00`.
</Accordion>

<Accordion title="413 — Payload Too Large">
  **Possible causes:**

  * The request body exceeds 10 MB

  **Resolution:**
  Split your data into smaller batches. Reduce the number of users per request or remove unnecessary properties.
</Accordion>

<Accordion title="503 — Service Unavailable">
  **Possible causes:**

  * Temporary service disruption

  **Resolution:**
  Retry the request with exponential backoff. Start with a 1-second delay and double it on each retry, up to a maximum of 60 seconds. Check [status.revbridge.ai](https://status.revbridge.ai) for any ongoing incidents.
</Accordion>

## Retry strategy

For `503` responses and network errors, implement exponential backoff:

```javascript theme={null}
async function sendWithRetry(payload, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch("https://api.revbridge.ai/ingest/events", {
        method: "POST",
        headers: {
          "Authorization": "Bearer rbk_a1b2c3d4e5f6g7h8",
          "x-customer-id": "cust_abc123",
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      });

      if (response.status === 503) {
        const delay = Math.min(1000 * Math.pow(2, attempt), 60000);
        await new Promise((resolve) => setTimeout(resolve, delay));
        continue;
      }

      return await response.json();
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      const delay = Math.min(1000 * Math.pow(2, attempt), 60000);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}
```

<Warning>
  Only retry on `503` and network errors. Do **not** retry `202` responses — even if some users were rejected, the accepted users were already processed. Fix the rejected users and send them in a new request.
</Warning>
