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

# Querying

> Pagination, sorting, field selection, search, filtering, and joins.

## Pagination

```http theme={null}
GET /project?page=1&pageSize=50
```

* `page` — one-based; the first page is `1` (default `1`). `page=0` is invalid and returns an error.
* `pageSize` — results per page (default `50`)
* Response includes `hasMore: boolean`

## Sorting

```http theme={null}
GET /project?orderBy=name:asc
GET /project?orderBy[]=name:asc&orderBy[]=createdAt:desc
```

## Select specific fields

A comma-separated value in a single `select` parameter is **not** supported and is silently ignored. Repeat the parameter instead:

```http theme={null}
GET /project?select=id&select=name&select=identifier
```

Or, for `POST /project/query`, pass `select` as a JSON array in the body:

```json theme={null}
{ "select": ["id", "name", "identifier"] }
```

## Full-text search

```http theme={null}
GET /project?search=warehouse
GET /project?search=warehouse:name,identifier
```

Append `:column1,column2` to limit search to specific columns.

## Filtering

Simple equality:

```http theme={null}
GET /project?name=Warehouse A
GET /deliverable?type=phase&type=route
```

Multiple values for the same field mean **IN**.

With operators:

```http theme={null}
GET /deliverable?createdAt:gt=2024-01-01
```

| Operator | Meaning               |
| -------- | --------------------- |
| `:eq`    | Equals                |
| `:ne`    | Not equals            |
| `:gt`    | Greater than          |
| `:ge`    | Greater than or equal |
| `:lt`    | Less than             |
| `:le`    | Less than or equal    |
| `:isn`   | Is null               |
| `:isnn`  | Is not null           |

<Warning>
  There is no `:in` operator — a filter like `progress.status:in=wip,rework` is accepted by the query parser but fails at the database layer (`500`). To match a field against multiple values, repeat the plain field param (see **Multiple values for the same field mean IN** above): `?progress.status=wip&progress.status=rework`.
</Warning>

Complex AND/OR:

```http theme={null}
GET /deliverable?filter=type eq 'phase' and isArchived eq false
GET /deliverable?filter=(type eq 'stage' or type eq 'step') and progress.status ne 'done'
```

## Joining related data

```http theme={null}
GET /deliverable?join:project:id,name=projectId eq relation.id
GET /deliverable?join:project as p:name=projectId eq p.id
```

Format: `?join:<table>:<fields>=<condition>`

* **Multiple joins:** pass several `join:` params in one request to join several tables.
* **Chained joins:** a condition may reference **another join's alias** (not only the base table), so you can hop across joined tables — e.g. join `deliverable as d`, then `deliverable as o` on `o.id eq d.rootParentId`. Declare a join **before** any join that references it. Conditions support JSONB paths (`d.data.stageId`) and id-fields are cast automatically. See the [timesheet](/resources/timesheet) attribution example.

<Warning>
  Joins to sensitive internal tables (e.g. `api_key`) are blocked.
</Warning>
