Steerd API

Pagination

Keyset cursor pagination for all list endpoints.

List endpoints return a uniform envelope: the rows under data, and pagination state under page.

{ "data": [ /* … */ ], "page": { "nextCursor": "…", "hasMore": true } }

Query parameters

ParamMeaning
limitPage size, default 50, max 500.
cursorOpaque cursor from a previous page.nextCursor. Omit for the first page.
sortA whitelisted sort key for the resource (see below).
orderasc or desc.
qFree-text filter over resource-specific columns.

Paging through results

Follow page.nextCursor until page.hasMore is false (at which point nextCursor is null):

let cursor = undefined;
const all = [];
do {
  const url = new URL("https://app.steerd.io/api/public/v1/contacts");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, { headers: { Authorization: "Bearer strd_YOUR_KEY" } });
  const { data, page } = await res.json();

  all.push(...data);
  cursor = page.hasMore ? page.nextCursor : null;
} while (cursor);

The cursor is keyset-based, so it is stable under inserts and deletes; you will not skip or repeat rows because the underlying data changed between pages. A cursor is tied to the sort/order it was issued for: passing it with a different sort or order returns 400 bad_request.

Pass only a cursor Steerd issued. A cursor value that does not decode is also 400 bad_request. It is not read as "start from the beginning", so a cursor your own plumbing truncated fails loudly instead of quietly restarting the loop at page one. To ask for the first page, omit cursor (or send it empty).

Sort keys per resource

Defaults are shown in bold.

ResourceSort keys
organizationsname, created_at
contactsname (last, first), email, created_at
employeesname (last, first), title, created_at
cvsname, updated_at, created_at
projectsupdated_at, created_at, title, status, archived_at
activitiesactivity_date, created_at
taskscreated_at
assignmentscreated_at
time_entriesentry_date
tripscreated_at, approved_at

Default order is asc for the name-defaulted resources and desc for the others.

trips takes no q; it filters with status instead, as an enum, so a misspelled value is a 400 rather than a silently empty page.

Projections

Responses are public projections: internal-only fields are never exposed. Every resource omits team_id, and several omit more than that.

The API Reference is the authoritative field list, generated from the OpenAPI document, so a resource's schema there is exactly what you get. What follows is the reasoning behind the omissions, not a second copy of the list:

  • Attribution and tenancy internals. user_id on activities and employees, assigned_user_id on tasks, created_by_other / edited_by_other on time entries. These identify the acting Steerd user, which the public surface does not model.
  • UI state. board_rank and attention on projects; the visibility flag on files.
  • Nested internal objects. A time entry omits the embedded employee; read it through /employees/{id} with employee_id.
  • Fields whose public surface does not exist yet. An organization omits requires_xrechnung and recipient_kind, and each of its addresses omits electronic_address, electronic_address_scheme and establishment_country. These pair with invoicing, and there is no public invoice resource for them to pair with; every public field is a compatibility commitment, so they stay internal until there is.
  • Reshaped rather than removed. A trip replaces sealed_totals with totals plus totals_sealed, and the detail omits computation (an untyped blob).

On this page