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

# Fundamentals

> API conventions for pagination, filters, IDs, caching, rate limits, and errors.

## Cursor pagination

List endpoints use cursor pagination and return `data` and `has_more`. The
default limit is 25 items and can be set from 1 to 100.

```json theme={null}
{
  "data": [
    {
      "object": "segment",
      "id": "seg_ckxyz123",
      "name": "VIP customers"
    }
  ],
  "has_more": true
}
```

To fetch the next page, send the last item's `id` as `starting_after`:

```bash theme={null}
curl --get https://api.bevits.com/v1/segments \
  --header "Authorization: Bearer $BEVITS_API_KEY" \
  --data-urlencode "limit=25" \
  --data-urlencode "starting_after=seg_ckxyz123"
```

Continue while `has_more` is `true`. Do not reuse a cursor from one resource
type in a different list.

## Public IDs

IDs are opaque strings with a prefix that identifies the resource.

| Prefix | Resource       |
| ------ | -------------- |
| `org_` | Organization   |
| `bak_` | API key        |
| `cus_` | Customer       |
| `seg_` | Segment        |
| `pur_` | Purchase       |
| `tag_` | Tag            |
| `atr_` | Attribute      |
| `flw_` | Flow           |
| `emc_` | Email campaign |

Treat IDs as strings. Do not remove the prefix or try to infer their contents.

## Filters and dates

Filters are sent as query parameters. Dates use ISO 8601 with an offset;
responses are normalized to UTC.

```bash theme={null}
curl --get https://api.bevits.com/v1/customers \
  --header "Authorization: Bearer $BEVITS_API_KEY" \
  --data-urlencode "is_subscribed=true" \
  --data-urlencode "updated_since=2026-07-01T00:00:00Z"
```

Fields without a value are omitted from responses instead of returning `null`.
Monetary values use cents, such as `total_spent_in_cents: 15990`.

## Errors

Errors are never returned with status `200`. The `error.type` field is stable
for automation; `message` is human-readable; and `request_id` identifies the
request for support.

```json theme={null}
{
  "error": {
    "type": "validation_error",
    "message": "Invalid input: expected number, received NaN",
    "param": "limit",
    "request_id": "req_07f6cdc3c20249d6afcd90b4"
  }
}
```

| HTTP  | `error.type`           | Meaning                                                         |
| ----- | ---------------------- | --------------------------------------------------------------- |
| `400` | `invalid_request`      | Invalid route, cursor, or request format.                       |
| `401` | `authentication_error` | Missing, invalid, expired, or revoked API key.                  |
| `403` | `permission_denied`    | The credential does not have the required permission.           |
| `404` | `not_found`            | The resource does not exist or belongs to another organization. |
| `409` | `conflict`             | The request conflicts with the resource's current state.        |
| `422` | `validation_error`     | A parameter or value failed validation.                         |
| `429` | `rate_limited`         | The request limit was exceeded.                                 |
| `500` | `internal_error`       | An unexpected server error occurred.                            |

## Rate limit

The initial limit is **600 requests per minute, per API key**. Authenticated
responses include:

```http theme={null}
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 2026-07-25T21:31:00.000Z
```

When you receive `429`, wait the number of seconds in `Retry-After` before
retrying. Use backoff with jitter in automated integrations.

## Conditional caching

Resource responses include an `ETag`. Send that value in `If-None-Match` to
avoid downloading unchanged content again:

```http theme={null}
If-None-Match: "v1-4ad31f5efb6b8f35"
```

When the content is identical, the API returns `304 Not Modified` without a
response body.
