Every list endpoint takes the same query options, so what you learn here applies to
all of them. POST /{resource}/query accepts the same options in the body when your
query is too long for a URL.
page starts at 1, and defaults to 1. Pages are 1-based, so page=0 is
rejected with a 400.
pageSize defaults to 100 when you send no pagination parameters at all. Send
it explicitly rather than relying on the default, which differs between the
standard endpoints and /timesheet.
- The response includes
hasMore, so page until it is false rather than counting
first.
GET /timesheet applies no default page size. Without an explicit pageSize
it returns every matching record in one response and reports hasMore: false.
Always pass pageSize there.
Sorting
Use comma-separated values (orderBy=name:asc,createdAt:desc) or repeated orderBy parameters to sort by multiple fields, in order. The bracket-array form above also works.
Returning only some fields
Use comma-separated values or repeat select once per field. These forms are equivalent:
In a POST /{resource}/query body, pass an array instead:
An explicit select restricts the resource fields returned, including computed fields on deliverables. You can request computed fields by name. Omit select to read the full record. Nested paths such as data.properties.squareFeet work too.
Full-text search
Append :column1,column2 to search specific columns. Each resource page lists its
search columns, and searching a resource that has none has no effect.
Filtering
For simple equality, use the field name directly:
Repeating a field means any of these values, so the second example returns both
phases and routes.
Add an operator with a colon:
Both forms match either value. Use repeated parameters when a value itself contains a comma; repeated values are preserved verbatim. An empty :in list is rejected with 400.
For anything with AND or OR in it, use filter:
Nested paths work in both styles, so computed.projectId and progress.status
filter the same way a top-level column does.
Joins let you resolve names in one request instead of following ids around.
The format is ?join:<table>:<fields>=<condition>. Use relation to refer to the
joined table when you have not aliased it, or give it an alias with as and use
that.
Join on the column the resource actually exposes. A deliverable has no top-level
projectId: the project it belongs to is computed.projectId,
and only order nodes carry data.projectId. Joining a column that does not
exist fails with a 500.
Three things make joins more capable than they first look:
- Pass several
join: parameters in one request to join several tables.
- A condition can reference another join’s alias, not just the base table, so
you can hop across joined tables in a single request. Declare a join before
any join that references it.
- Conditions support JSONB paths such as
d.data.stageId, and id fields are cast
for you.
Chaining is what makes hours reportable. The
timesheet attribution example
walks through going from a timesheet row to its order, station, and phase in one
call.
Joins to internal tables such as api_key are blocked.