Table queries
The user and team list endpoints work like a spreadsheet export: you say which columns you want, how to filter and sort, and which page to return. The same request body is used by:
| Endpoint | Rows |
|---|---|
POST /users/table | Users of the account |
POST /team/table | Teams |
POST /team/{team_id}/members/table | Members of one team |
POST /campaigns/table | Campaigns of the account |
POST /campaigns/{campaign_id}/users/table | Participants of one campaign; select users.invite_campaign_user.token for personal invite links |
POST /team/{team_id}/members/table/count | Just total and pages for the same filters |
1. Discover the columns
Column keys differ per endpoint, so fetch them first:
GET /users/columnsfor user tables (also used by participant tables)GET /team/columnsfor team tablesGET /campaigns/columnsfor campaign tables
The response lists every queryable column with its type (keyword, numeric, date, name, email) and, for users, ready-made table views (All users, Core Drivers, Core Values, Competencies) whose payload you can send as-is.
{
"table_views": [
{
"name": "All users",
"payload": {
"columns": ["users.email", "users.first_name", "users.last_name", "users.display_status", "users.usergroups.name"],
"filters": []
}
}
],
"columns": {
"users.id": { "name": "User id", "type": "numeric", "key": "users.id" },
"users.email": { "name": "Email", "type": "email", "key": "users.email" },
"users.created_at": { "name": "Created", "type": "date", "key": "users.created_at" }
}
}
Assessment scores and HRIS fields are columns too (for example users.core_drivers.drive or a field you imported such as hris.business_unit), which is how you export results in bulk.
2. Query
curl -X POST https://api.deepersignals.com/api/external/users/table \
-H "X-TOKEN: $DS_API_TOKEN" -H "Content-Type: application/json" \
-d '{
"columns": ["users.id", "users.email", "users.first_name", "users.last_name", "users.created_at"],
"filters": [
{ "field": "users.created_at", "type": "between", "value": ["2026-01-01 00:00:00", "2026-03-31 23:59:59"] },
{ "field": "users.usergroups.name", "type": "in", "value": ["Sales", "Marketing"] }
],
"sort": { "users.created_at": "desc" },
"pagination": { "limit": 100, "page": 1 },
"q": "doe"
}'
{
"items": [
{ "users.id": 523108, "users.email": "jane.doe@example.com", "users.first_name": "Jane", "users.last_name": "Doe", "users.created_at": "2026-02-16 08:41:47" }
],
"total": 1,
"pages": 1
}
Request body
| Key | Required | Description |
|---|---|---|
columns | yes | 1 to 100 column keys from the columns endpoint. Rows are objects keyed by these. |
filters | no | Up to 5 conditions, combined with AND. See operators below. |
sort | no | One entry, { "<column>": "asc" | "desc" }. |
pagination | no | { "limit": <1..500>, "page": <1..> }. Defaults to the first page of 25. |
q | no | Free-text search over first name, last name and e-mail. |
Filter operators
Each filter is { "field": "<column>", "type": "<operator>", "value": ... }.
| Operator | Value | Column types |
|---|---|---|
eq, neq | single value | all |
in, not_in | list of values | all |
contains, not_contains, starts_with, ends_with | string | text-like columns |
gt, gte, lt, lte | number or date | numeric, date |
between, not_between | [low, high] | numeric, date |
empty, not_empty | no value | all |
An entry of the form { "type": "or", "filters": [ ... ] } groups conditions with OR.
Dates are YYYY-MM-DD HH:MM:SS in UTC.
Limits
See Rate limits → Request shape limits. A request that breaks a limit is answered with 400 and a details object naming the constraint.
Paging through everything
Loop over page until page * limit >= total. Keep the same filters and sort for every page; sort by a stable column such as users.id so rows do not shift between pages when users are created while you export.