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

# Filtering and expanding

> Filter, sort, cursor-paginate, and expand any list endpoint with one shared query syntax.

## Filtering lists

List endpoints share one query mini-language: append an operator to a
filterable field in square brackets. A bare field means exact match.

```bash theme={null}
GET /v1/companies?status=active
GET /v1/companies?name[contains]=acme&created_at[gte]=2026-01-01
GET /v1/companies?status[in]=active,in_progress&sort=-created_at&limit=25
```

<AccordionGroup>
  <Accordion title="Operators">
    | Operator                      | Meaning                                                             | Example                         |
    | ----------------------------- | ------------------------------------------------------------------- | ------------------------------- |
    | *(none)* / `[eq]`             | exact match                                                         | `status=active`                 |
    | `[neq]`                       | not equal                                                           | `status[neq]=cancelled`         |
    | `[gt]` `[gte]` `[lt]` `[lte]` | range (numbers, dates)                                              | `created_at[gte]=2026-01-01`    |
    | `[in]` / `[nin]`              | in / not in a comma-separated list                                  | `status[in]=active,in_progress` |
    | `[contains]`                  | case-insensitive substring, only on fields documented as searchable | `name[contains]=acme`           |

    A field sent more than once (`?status=a&status=b`) is rejected - pass
    multiple values with `[in]` instead. A query holds at most 16 conditions,
    a list at most 50 values, and a value at most 256 characters.
  </Accordion>

  <Accordion title="Sorting">
    `sort`: comma-separated fields, minus prefix for descending:
    `sort=-created_at,name`. Each endpoint's description lists its sortable
    fields. Page size and cursors are covered under Pagination.
  </Accordion>

  <Accordion title="Errors">
    Filtering, sorting or searching on an undeclared field returns
    `invalid_request` with one entry per violation in the `errors` array. The
    queryable surface of every endpoint is exactly its documented parameter
    list, nothing more.
  </Accordion>
</AccordionGroup>

## Paginating lists

List responses are cursor-paginated. Every list carries a `page` block. Walk
forward by passing `end_cursor` as `after`, backward by passing
`start_cursor` as `before`:

```bash theme={null}
GET /v1/companies?limit=25
GET /v1/companies?limit=25&after=WyIyMDI2LTA4LTEzVDE0OjAyOjExWiIsIm1lbV8wMzQ2c0ZQRXZd   # next page
GET /v1/companies?limit=25&before=WyIyMDI2LTA4LTEyVDA5OjMwOjAwWiIsIm1lbV8weFF2TmNYZWJd # previous page
```

```json The page block theme={null}
{
  "object": "list",
  "page": {
    "start_cursor": "WyIyMDI2LTA4LTEzVDE0OjAyOjExWiIsIm1lbV8wMzQ2c0ZQRXZd",
    "end_cursor": "WyIyMDI2LTA4LTEyVDA5OjMwOjAwWiIsIm1lbV8weFF2TmNYZWJd",
    "has_next_page": true,
    "has_prev_page": false,
    "limit": 25,
    "total": 112
  },
  "items": []
}
```

<AccordionGroup>
  <Accordion title="Rules">
    * Cursors are **opaque**. Never parse or construct one, and their internal
      structure changes without notice.
    * A cursor is only valid for the **same listing** (same sort) that minted
      it. Anything else returns `invalid_request`.
    * `after` and `before` cannot be combined.
    * Paging is position-based, not offset-based: page 100 costs the same as
      page 1, and rows created between requests never shift your window.
    * `limit` is 1-100, default 25. Both cursors are `null` on an empty page.
  </Accordion>
</AccordionGroup>

## Expanding responses

Many objects let you request related resources inline instead of making a
second call, using the `expand[]` query parameter. Unexpanded responses keep
the payload small. Each expansion is loaded only when you ask for it.

```bash theme={null}
# One relation
GET /v1/companies/cmp_0346sFPEvSkJvY8vt14NNw?expand[]=account

# Several relations
GET /v1/companies/cmp_0346sFPEvSkJvY8vt14NNw?expand[]=account&expand[]=service_orders
```

Requesting a property that is not expandable on that endpoint returns an
`invalid_request` error naming the offending path in its `errors` array,
expandable properties are a fixed, per-endpoint list, always documented on
the endpoint's page.

### Example: company with its account

`GET /v1/companies/{id}?expand[]=account` embeds the full account beside the
`account_id` it would otherwise return alone.

<ParamField query="expand[]" type="array of strings">
  Relations to embed in the response. Not requested, the property is absent.

  <Expandable title="expandable properties">
    <ResponseField name="account" type="object">
      The account this company was created under.

      <Expandable title="child attributes">
        <ResponseField name="id" type="string">Account identifier, `acct_`.</ResponseField>
        <ResponseField name="first_name" type="string">The account holder's first name.</ResponseField>
        <ResponseField name="last_name" type="string">The account holder's last name.</ResponseField>
        <ResponseField name="email" type="string">The account holder's email.</ResponseField>
        <ResponseField name="created_at" type="string">RFC 3339 creation time.</ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ParamField>

```json Expanded response theme={null}
{
  "object": "company",
  "id": "cmp_0346sFPEvSkJvY8vt14NNw",
  "name": "Acme Platforms",
  "status": "active",
  "account_id": "acct_0346sFPEvXWkHRYHG5g9tN",
  "account": {
    "object": "account",
    "id": "acct_0346sFPEvXWkHRYHG5g9tN",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "email": "ada@acme.example",
    "created_at": "2026-08-13T14:02:11Z"
  }
}
```
