Platform Reference

Records API

How to query, filter, sort, and paginate any of the 1,151 FlowOS tables through the unified REST Records API.

Base Endpoint

Every table is accessible through a single endpoint pattern:

bash
GET  /api/v1/tables/:slug/records
POST /api/v1/tables/:slug/records
GET  /api/v1/tables/:slug/records/:id
PATCH /api/v1/tables/:slug/records/:id
DELETE /api/v1/tables/:slug/records/:id

Where :slug is the table slug (e.g. incidents, itsm_incident_tasks, releases). All requests require Authorization: Bearer {token} and X-Workspace: {slug} headers.

Filtering

Use filter[field]=value query parameters to filter records. Multiple filters are ANDed together.

bash
# Exact match
GET /api/v1/tables/incidents/records?filter[status]=open

# Multiple filters (AND)
GET /api/v1/tables/incidents/records?filter[status]=open&filter[priority]=critical

# Nested reference field (dot notation)
GET /api/v1/tables/incidents/records?filter[assignedTo.email]=alice@acme.com

Filter Operators

Append an operator in brackets after the field name:

OperatorMeaningExample
filter[field]=…Exact matchfilter[status]=open
filter[field][ne]=…Not equalfilter[status][ne]=closed
filter[field][gt]=…Greater thanfilter[priority][gt]=medium
filter[field][gte]=…Greater or equalfilter[createdAt][gte]=2026-01-01
filter[field][lt]=…Less thanfilter[durationMinutes][lt]=60
filter[field][lte]=…Less or equalfilter[score][lte]=100
filter[field][in]=…In listfilter[status][in]=open,investigating
filter[field][nin]=…Not in listfilter[priority][nin]=low,planning
filter[field][like]=…LIKE matchfilter[title][like]=database%
filter[field][ilike]=…Case-insensitive LIKEfilter[title][ilike]=%outage%
filter[field][null]=…Is nullfilter[resolvedAt][null]=true
filter[field][null]=…Is not nullfilter[assignedTo][null]=false
filter[field][exists]=…Field existsfilter[metadata.runId][exists]=true

Sorting

bash
# Ascending (default)
GET /api/v1/tables/incidents/records?sort=createdAt

# Descending
GET /api/v1/tables/incidents/records?sort=-createdAt

# Multiple sort fields
GET /api/v1/tables/incidents/records?sort=-priority,createdAt

Pagination

All list responses use cursor-based pagination. The response includes a meta.nextCursor token — pass it as cursor to fetch the next page.

bash
# First page (default limit: 50, max: 500)
GET /api/v1/tables/incidents/records?limit=100

# Next page
GET /api/v1/tables/incidents/records?limit=100&cursor=eyJpZCI6Ij...
json
{
  "data": [ { "id": "inc_01...", ... }, ... ],
  "meta": {
    "total": 4821,
    "count": 100,
    "nextCursor": "eyJpZCI6Ij...",
    "hasMore": true
  }
}

Field Selection

Use fields to return only specific fields. Reduces payload size significantly on wide tables.

bash
GET /api/v1/tables/incidents/records?fields=id,number,status,assignedTo,createdAt

Expanding References

Use expand to inline reference fields instead of returning just the ID.

bash
# Expand single reference
GET /api/v1/tables/incidents/records?expand=assignedTo

# Expand multiple
GET /api/v1/tables/incidents/records?expand=assignedTo,groupId,ciId

# Expand nested (2 levels)
GET /api/v1/tables/incidents/records?expand=assignedTo.department
json
{
  "id": "inc_01...",
  "assignedTo": {
    "id": "usr_01...",
    "name": "Alice Chen",
    "email": "alice@acme.com"
  }
}

Creating Records

bash
POST /api/v1/tables/incidents/records
Content-Type: application/json

{
  "title": "Database connection pool exhausted",
  "status": "open",
  "priority": "critical",
  "category": "database",
  "assignedTo": "usr_01..."
}

The response returns the full created record including auto-set fields (id, number, createdAt, tenantId, workspaceId).

Updating Records

Use PATCH for partial updates. Only include the fields you want to change.

bash
PATCH /api/v1/tables/incidents/records/inc_01...
Content-Type: application/json

{
  "status": "resolved",
  "resolvedAt": "2026-06-02T14:30:00Z",
  "resolution": "Increased connection pool size from 10 to 50."
}

Deleting Records

bash
DELETE /api/v1/tables/incidents/records/inc_01...
Deletes are soft-deletes by default — records get a deletedAt timestamp and are excluded from queries. Pass ?hard=true for a permanent delete (requires admin role).

Bulk Operations

bash
# Bulk create (up to 500 records)
POST /api/v1/tables/incidents/records/bulk
{ "records": [ {...}, {...} ] }

# Bulk update
PATCH /api/v1/tables/incidents/records/bulk
{ "ids": ["inc_01...", "inc_02..."], "data": { "status": "closed" } }

# Bulk delete
DELETE /api/v1/tables/incidents/records/bulk
{ "ids": ["inc_01...", "inc_02..."] }

SQL Endpoint

For complex cross-table queries, use the SQL endpoint. Read-only. Returns up to 10,000 rows.

bash
POST /api/v1/sql
Content-Type: application/json

{
  "query": "SELECT i.number, i.title, u.name as assignee FROM incidents i LEFT JOIN users u ON i.assigned_to = u.id WHERE i.status = 'open' ORDER BY i.created_at DESC LIMIT 50"
}
All table references in SQL use the logical slug (e.g. incidents), not the MongoDB collection name. The query engine handles tenant/workspace scoping automatically based on your API token.

Rate Limits

TierReads/minWrites/minBulk/min
Standard60030030
Pro30001500150
EnterpriseUnlimitedUnlimitedUnlimited

Rate limit headers are returned on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.

Ticket Number Generation

Every ITSM record gets a human-readable auto-number (e.g. INC0000042) on create. Numbers are generated atomically by the generateNumber utility which increments a per-workspace counter in sys_counters. The full signature used internally is:

typescript
generateNumber(tenantId: string, workspaceId: string, prefix: string): Promise<string>

Numbers are 7-digit zero-padded with no separator between prefix and digits. All prefixes used across the platform:

PrefixExampleEntity
INCINC0000001Incidents
PRBPRB0000001Problems
CHGCHG0000001Changes
REQREQ0000001Service requests
RITMRITM0000001Request items
CTASKCTASK0000001Change tasks
KEKE0000001Known errors
UCUC0000001Underpinning contracts
RELREL0000001Releases
SVCSVC0000001Service portfolio entries
CICI0000001CMDB configuration items
EVTEVT0000001Platform events
CSCS0000001Customer support / CSM cases
ORDORD0000001Commerce orders
EMPEMP0000001HR employee IDs
HRCHRC0000001HR cases
LEVLEV0000001HR leave requests
Counters are workspace-scoped — INC0000001 in ws_prod and INC0000001 inws_staging are independent sequences. The counter state lives inroot_<tenantId>_<workspaceId>_sys_counters and is atomically incremented via MongoDB$inc with upsert: true, making it safe under concurrent creates. Counter values are never reset or reused — even if the entity is deleted.