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

# Pagination and errors

> Common list envelope and error expectations

List endpoints use a consistent paginated envelope where practical.

```json theme={null}
{
  "items": [],
  "totalCount": 0,
  "start": 0,
  "length": 50
}
```

## Query parameters

* `start`: zero-based offset. Most services fall back to `0` for invalid or negative values.
* `length`: number of records to return. Default is `50`; maximum is usually `200`; minimum is `1`.
* `search`: optional trimmed search text where the endpoint supports it.

## Search

Searchable endpoints use case-insensitive matching across endpoint-specific columns. Search behaviour is intentionally scoped per route; do not assume every list endpoint searches every displayed field.

The Rates API treats the `search` value as literal text. Characters that carry special meaning in SQL `LIKE` expressions — `%`, `_`, and `\` — match only themselves. A search for `50%` returns items whose name contains the string `50%`, not every item in the catalogue.

## Service exceptions

Rates API list parsing currently caps `length` at `1000`. Global, Activities, and Estimate use the shared fallback/clamp parser with a `200` cap.

Documents list endpoints are stricter: `estimateId` is required, `start` must be `0` or greater, and `length` must be between `1` and `200`. Invalid Documents list query values return `400`.

## Validation errors

Invalid request bodies, params, or query strings return:

```json theme={null}
{
  "message": "Validation failed.",
  "issues": []
}
```

## Common status codes

| Status | Meaning |
| -: | - |
| `400` | Validation failed or the request is semantically invalid. |
| `401` | A protected route has a missing or invalid bearer token, or a refresh token is invalid. |
| `402` | The organisation's subscription does not permit this operation. The response body carries `code: "billing_read_only"`. Service tokens are not subject to this check. |
| `402` | The organisation's subscription has expired. Write operations that require an active subscription return this status with an error code of `billing_read_only`. |
| `403` | User is not authorised for the requested auth flow or admin route. |
| `404` | Record was not found. |
| `409` | Duplicate record, foreign-key conflict, processing conflict, or state conflict. |
| `422` | The request is valid but violates a business rule (e.g. contract corpus ownership or status constraints). |
| `500` | An unhandled server error occurred. The response body carries `code: "internal_error"` and a `reference` that matches the `x-request-id` response header. The reference can be used to locate the corresponding log entry. |
| `502` | An upstream service call failed. Retry after a short delay. If the error persists, the upstream service may be unavailable. |
| `503` | The service is temporarily unavailable, usually because the database connection pool is saturated. The response body carries `code: "service_unavailable"` and a `reference`. The `Retry-After` header gives the number of seconds to wait before retrying. |

## Unhandled error body shape

`500` and `503` responses from Stralos APIs use a consistent body shape. Raw database driver text or internal stack traces are never included.

```json theme={null}
{
  "code": "internal_error",
  "reference": "req_01j..."
}
```

For a saturated database the `code` is `service_unavailable` instead:

```json theme={null}
{
  "code": "service_unavailable",
  "reference": "req_01j..."
}
```

The `reference` value matches the `x-request-id` response header and identifies the specific request in server logs. Quote it when reporting the error.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.