Pagination, Filtering & Sorting

All list endpoints support pagination, filtering, sorting, and relation inclusion via query parameters.

Pagination

Parameter Type Default Description
page number 1 Current page number (1-indexed)
perPage number 10 Number of items per page

curl:

curl "https://api.incrm.app/api/v1/client?page=2&perPage=25" \
  -H "X-Api-Key: incrm_your-key"

TypeScript SDK:

const { data } = await listClients({ query: { page: 2, perPage: 25 } })

Pagination Metadata

List responses include a meta object:

{
  "success": true,
  "data": [...],
  "meta": {
    "total": 150,
    "lastPage": 6,
    "currentPage": 2,
    "perPage": 25,
    "prev": 1,
    "next": 3
  }
}
Field Type Description
total number Total number of matching records
lastPage number Last available page number
currentPage number Current page number
perPage number Items per page
prev number | null Previous page number, or null if on first page
next number | null Next page number, or null if on last page

Traversing All Pages

To retrieve all records, iterate until meta.next is null:

import { InCRMClient, listClients } from '@incrm-app/sdk'

const incrm = new InCRMClient({
  baseUrl: 'https://api.incrm.app',
  apiKey: 'incrm_your-key',
})

let page = 1
const allClients = []

while (true) {
  const { data: response } = await listClients({ query: { page, perPage: 100 } })
  allClients.push(...response.data)

  if (!response.meta.next) break
  page = response.meta.next
}

console.log(`Fetched ${allClients.length} clients`)

Filtering (where)

The where parameter supports a powerful filter syntax for querying records.

Basic Equality

where=firstName:John

Operators

Use the operator syntax field:operator:value:

Operator Description Example
(none) Equals where=status:ACTIVE
gt Greater than where=amount:gt:int(100)
gte Greater than or equal where=amount:gte:int(50)
lt Less than where=amount:lt:int(1000)
lte Less than or equal where=amount:lte:int(500)
contains String contains where=name:contains:john
startsWith String starts with where=name:startsWith:Jo
endsWith String ends with where=email:endsWith:.com

Type Casting

Wrap values in type functions:

Type Syntax Example
Integer int(value) where=amount:gt:int(100)
Float float(value) where=price:lt:float(99.99)
Boolean bool(value) where=isActive:bool(true)
Date date(value) where=createdAt:gt:date(2025-01-01)

Multiple Conditions (AND)

Separate conditions with commas:

where=status:ACTIVE,firstName:contains:John

Conditions on the same field combine, which gives a range:

where=issueDate:gte:date(2025-01-01),issueDate:lte:date(2025-12-31T23:59:59.999Z)

OR Conditions

where=OR:[status:ACTIVE,status:PENDING]

NOT Conditions

where=NOT:status:ARCHIVED

JSON Filters

where also accepts a URL-encoded JSON object in Prisma's filter format. It can express what the string syntax cannot: nested AND/OR/NOT, fields of embedded objects (is), and lists of embedded objects (some, every, none).

where={"AND":[{"paymentStatus":{"in":["NotPaid","PartlyPaid"]}},{"clientInfo":{"is":{"name":{"contains":"acme","mode":"insensitive"}}}},{"lineItems":{"some":{"description":{"contains":"hosting"}}}}]}

Use Prisma operator names (equals, in, gte, contains, mode, ...). Raw MongoDB operators such as $where are not supported and return a 400 error.

Examples

curl — Active clients created after Jan 2025:

curl "https://api.incrm.app/api/v1/client?where=isActive:bool(true),createdAt:gt:date(2025-01-01)" \
  -H "X-Api-Key: incrm_your-key"

curl — Invoices over $1000:

curl "https://api.incrm.app/api/v1/invoice?where=totalAmount:gt:int(1000)" \
  -H "X-Api-Key: incrm_your-key"

Searching Files, Documents and Notes by Meaning

where matches fields. To find files, documents and notes by what they say, and your own earlier chats with Brane and subjects by what they were about, use GET /api/v1/workspace/search. It is a search by meaning, not a list endpoint: it takes q instead of where. Meaning is compared best within one language, so for documents in another language give the same phrase in that language too, separated by |: late delivery penalty | ugovorna kazna za kašnjenje finds a Croatian contract that says "ugovorna kazna".

Parameter Description
q Required. What to look for, up to 500 characters; up to 3 phrasings of it separated by |.
types Kinds of source, separated by commas: FILE, DOCUMENT, NOTE, CHAT (your chats with Brane), SUBJECT (your subjects). Default: all the caller may read.
folderId Only files and documents in this folder of Files or below it (no notes, chats or subjects).
limit Most sources to return: 10 by default, at most 25.

Only sources the caller may open are returned, as GET /files/{id}, GET /document/{id}, GET /note/{id}, GET /ai-chat/{id} and GET /brane-subject/{id} decide it, and each kind needs its own read permission (files.read, documents.read, notes.read, aichats.read, braneSubjects.read). Chats and subjects are personal: only their own user finds them, never a colleague in the same business. Each result is one source, best first: type, id, name, folderPath, updatedAt, score (0 to 1) and up to 3 snippets with the page of a file; a chat also has archivedAt and purgedAt. Where search by meaning is not available or finds nothing, the words of q are looked for in names and text (match: TEXT, score: null). A file is searchable once its text is read; a document or note about 30 seconds after it was saved; a chat once Brane reviewed it (by its title, summary and open loops, never its messages), also as a trace after its messages were deleted (purgedAt set); a subject once it is made or its summary changes.

curl "https://api.incrm.app/api/v1/workspace/search?q=late%20delivery%20penalty%20%7C%20ugovorna%20kazna&types=FILE,DOCUMENT" \
  -H "X-Api-Key: incrm_your-key"

Sorting (orderBy)

Sort results using field:direction pairs, separated by commas:

orderBy=lastName:asc,firstName:asc
Direction Description
asc Ascending
desc Descending

curl:

curl "https://api.incrm.app/api/v1/invoice?orderBy=createdAt:desc&perPage=50" \
  -H "X-Api-Key: incrm_your-key"

TypeScript SDK:

const { data } = await listInvoices({
  query: { orderBy: 'createdAt:desc', perPage: 50 },
})

Error on Invalid Sort

{
  "success": false,
  "error": {
    "code": 400011,
    "message": "Invalid sorting order"
  }
}

Relation Inclusion (include)

Expand related entities using the include parameter. Provide a comma-separated list of relation names:

include=invoices,contacts

Prefix with - to exclude a default relation:

include=-contacts

curl:

curl "https://api.incrm.app/api/v1/client/6507a1b2c3d4e5f6a7b8c9d0?include=invoices,contacts" \
  -H "X-Api-Key: incrm_your-key"

Response (with included relations):

{
  "success": true,
  "data": {
    "id": "6507a1b2c3d4e5f6a7b8c9d0",
    "firstName": "John",
    "lastName": "Doe",
    "invoices": [
      { "id": "...", "totalAmount": 500, "status": "PAID" }
    ],
    "contacts": [
      { "id": "...", "type": "EMAIL", "value": "john@example.com" }
    ]
  }
}

Available relations depend on the entity. Refer to the OpenAPI spec at GET /api/v1/openapi.json for the complete list of relations per resource.

Error on Invalid Include

{
  "success": false,
  "error": {
    "code": 400019,
    "message": "Invalid query format"
  }
}