EERP Suite

API Reference

Base URL: https://data-brain-api.marlin-pohl.workers.dev

All endpoints (except admin and health) require tenant authentication via Bearer token.

Authentication

Include your API key in the Authorization header:

Authorization: Bearer sk_live_your_api_key_here

Admin endpoints use a separate admin API key.

Optional: Workspace Scoping

To scope requests to a specific workspace, include the X-Workspace-Id header alongside your API key:

Authorization: Bearer sk_live_your_api_key_here
X-Workspace-Id: ws_your_workspace_id

When this header is present, all table, row, column, and view operations are filtered to that workspace.

Error Format

All errors follow a consistent format:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description",
    "details": {}
  }
}

Common error codes: UNAUTHORIZED, NOT_FOUND, VALIDATION_ERROR, QUOTA_EXCEEDED, CONFLICT.


Tables

List Tables

GET /api/v1/tables

Auth: Tenant API key (Bearer token)

Example Response (200):

[
  {
    "id": "tbl_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "workspaceId": "ws_tenant-uuid",
    "name": "Contacts",
    "description": null,
    "icon": null,
    "createdAt": "2026-02-20T10:00:00.000Z",
    "updatedAt": "2026-02-20T10:00:00.000Z"
  }
]

Create Table

POST /api/v1/tables

Auth: Tenant API key (Bearer token)

Request Body:

FieldTypeRequiredDescription
namestringYesTable name (1--255 chars)
descriptionstringNoTable description (max 1000 chars)
iconstringNoIcon identifier (max 50 chars)

Example Request:

{
  "name": "Contacts",
  "description": "Customer contact information"
}

Example Response (201):

{
  "id": "tbl_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "workspaceId": "ws_tenant-uuid",
  "name": "Contacts",
  "description": "Customer contact information",
  "icon": null,
  "createdAt": "2026-02-20T10:00:00.000Z",
  "updatedAt": "2026-02-20T10:00:00.000Z"
}

Get Table

GET /api/v1/tables/:tableId

Auth: Tenant API key (Bearer token)

Error Responses:

  • 404 -- Table not found or belongs to another tenant

Update Table

PATCH /api/v1/tables/:tableId

Auth: Tenant API key (Bearer token)

Request Body:

FieldTypeRequiredDescription
namestringNoNew table name
descriptionstringNoNew description
iconstringNoNew icon

Delete Table

DELETE /api/v1/tables/:tableId

Auth: Tenant API key (Bearer token)

Example Response (200):

{ "success": true }

Columns

List Columns

GET /api/v1/tables/:tableId/columns

Auth: Tenant API key (Bearer token)

Example Response (200):

[
  {
    "id": "col_uuid",
    "tableId": "tbl_uuid",
    "name": "Full Name",
    "type": "text",
    "position": 0,
    "width": 200,
    "isPrimary": true,
    "config": {}
  }
]

Create Column

POST /api/v1/tables/:tableId/columns

Auth: Tenant API key (Bearer token)

Request Body:

FieldTypeRequiredDescription
namestringYesColumn name (1--255 chars)
typestringYesColumn type (see below)
positionnumberNoDisplay position
widthnumberNoColumn width in pixels
isPrimarybooleanNoWhether this is the primary column
configobjectNoType-specific configuration

Column types: text, number, date, boolean, select, multi_select, url, file, formula, relation, rollup, created_time, last_edited_time

Get Column

GET /api/v1/columns/:columnId

Update Column

PATCH /api/v1/columns/:columnId

Request Body:

FieldTypeRequiredDescription
namestringNoNew column name
widthnumberNoNew column width
configobjectNoNew configuration

Delete Column

DELETE /api/v1/columns/:columnId

Reorder Columns

PUT /api/v1/tables/:tableId/columns/reorder

Request Body: Array of column UUIDs in desired order.

["col_uuid_3", "col_uuid_1", "col_uuid_2"]

Rows

Query Rows

GET /api/v1/tables/:tableId/rows

Auth: Tenant API key (Bearer token)

Query Parameters:

ParameterTypeDefaultDescription
limitnumber50Results per page (1--200)
offsetnumber0Number of rows to skip
cursorstring--Pagination cursor
includeArchivedbooleanfalseInclude archived rows
filtersstring--JSON-encoded array of filter objects
sortsstring--JSON-encoded array of sort objects

Filter object: { "columnId": "col_1", "operator": "contains", "value": "Alice" }

Sort object: { "columnId": "col_1", "direction": "asc" }

Example Response (200):

{
  "rows": [
    {
      "id": "row_uuid",
      "tableId": "tbl_uuid",
      "cells": {
        "col_1": { "value": "Alice" },
        "col_2": { "value": "alice@example.com" }
      },
      "isArchived": false,
      "createdAt": "2026-02-20T10:05:00.000Z",
      "updatedAt": "2026-02-20T10:05:00.000Z"
    }
  ],
  "total": 1
}

Create Row

POST /api/v1/tables/:tableId/rows

Request Body:

FieldTypeRequiredDescription
cellsobjectNoColumn ID to cell value map
parentRowIdstringNoUUID of parent row (for sub-items)

Example Request:

{
  "cells": {
    "col_1": { "value": "Alice" },
    "col_2": { "value": "alice@example.com" }
  }
}

Get Row

GET /api/v1/rows/:rowId

Update Row

PATCH /api/v1/rows/:rowId

Request Body: Object mapping column IDs to cell values.

{
  "col_1": { "value": "Alice Johnson" }
}

Delete Row

DELETE /api/v1/rows/:rowId

Archive Row

POST /api/v1/rows/:rowId/archive

Unarchive Row

POST /api/v1/rows/:rowId/unarchive

Bulk Create Rows

POST /api/v1/tables/:tableId/rows/bulk

Request Body: Array of row objects (1--1,000).

[
  { "cells": { "col_1": { "value": "Alice" } } },
  { "cells": { "col_1": { "value": "Bob" } } }
]

Example Response (201): Array of created Row objects.

Bulk Delete Rows

DELETE /api/v1/rows/bulk

Request Body: Array of row UUIDs (1--1,000).

["row_uuid_1", "row_uuid_2", "row_uuid_3"]

Bulk Archive Rows

POST /api/v1/rows/bulk/archive

Request Body: Array of row UUIDs (1--1,000).


Views

List Views

GET /api/v1/tables/:tableId/views

Create View

POST /api/v1/tables/:tableId/views

Request Body:

FieldTypeRequiredDescription
namestringYesView name (1--255 chars)
typestringYesView type: table, board, calendar, gallery, timeline, list
isDefaultbooleanNoWhether this is the default view
positionnumberNoDisplay position
configobjectNoView configuration (filters, sorts, column visibility)

Example Request:

{
  "name": "Active Contacts",
  "type": "table",
  "config": {
    "filters": [{ "columnId": "col_3", "operator": "equals", "value": true }]
  }
}

Get View

GET /api/v1/views/:viewId

Update View

PATCH /api/v1/views/:viewId

Delete View

DELETE /api/v1/views/:viewId

Reorder Views

PUT /api/v1/tables/:tableId/views/reorder

Request Body: Array of view UUIDs in desired order.


Select Options

List Options

GET /api/v1/columns/:columnId/options

Create Option

POST /api/v1/columns/:columnId/options

Request Body:

FieldTypeRequiredDescription
namestringYesOption name (1--255 chars)
colorstringNoColor identifier (max 50 chars)
positionnumberNoDisplay position

Example Request:

{
  "name": "High Priority",
  "color": "red"
}

Update Option

PATCH /api/v1/options/:optionId

Delete Option

DELETE /api/v1/options/:optionId

Reorder Options

PUT /api/v1/columns/:columnId/options/reorder

Request Body: Array of option UUIDs in desired order.


Relations

Create Relation

POST /api/v1/relations

Request Body:

FieldTypeRequiredDescription
sourceRowIdstringYesUUID of the source row
sourceColumnIdstringYesUUID of the relation column
targetRowIdstringYesUUID of the target row

Example Request:

{
  "sourceRowId": "row_uuid_1",
  "sourceColumnId": "col_relation",
  "targetRowId": "row_uuid_2"
}

Delete Relation

DELETE /api/v1/relations

Request Body:

FieldTypeRequiredDescription
sourceRowIdstringYesUUID of the source row
columnIdstringYesUUID of the relation column
targetRowIdstringYesUUID of the target row

Get Related Rows

GET /api/v1/rows/:rowId/relations/:columnId

Example Response (200): Array of Row objects.

Get All Relations for Row

GET /api/v1/rows/:rowId/relations

Example Response (200):

[
  { "columnId": "col_relation_1", "targetRowId": "row_uuid_2" },
  { "columnId": "col_relation_1", "targetRowId": "row_uuid_3" }
]

File References

Add File Reference

POST /api/v1/file-refs

Request Body:

FieldTypeRequiredDescription
rowIdstringYesUUID of the row
columnIdstringYesUUID of the file column
fileIdstringYesExternal file identifier
fileUrlstringYesURL to the file
originalNamestringYesOriginal filename (1--255 chars)
mimeTypestringYesMIME type (1--100 chars)
sizeBytesnumberNoFile size in bytes
positionnumberNoDisplay position
metadataobjectNoAdditional metadata

Example Request:

{
  "rowId": "row_uuid",
  "columnId": "col_file",
  "fileId": "sb_file_id",
  "fileUrl": "https://storage.example.com/files/receipt.pdf",
  "originalName": "receipt.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 245000
}

Remove File Reference

DELETE /api/v1/file-refs/:fileRefId

Get File References

GET /api/v1/rows/:rowId/files/:columnId

Example Response (200): Array of FileReference objects.

Reorder File References

PUT /api/v1/rows/:rowId/files/:columnId/reorder

Request Body: Array of file reference UUIDs in desired order.


Workspaces

Workspaces partition data within a tenant. All workspace endpoints require a tenant API key.

List Workspaces

GET /api/v1/workspaces

Auth: Tenant API key (Bearer token)

Example Response (200):

[
  {
    "id": "ws_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "tenantId": "tenant-uuid",
    "name": "Production",
    "slug": "production",
    "quotaRows": null,
    "usedRows": 0,
    "metadata": null,
    "createdAt": "2026-02-20T10:00:00.000Z",
    "updatedAt": "2026-02-20T10:00:00.000Z"
  }
]

Create Workspace

POST /api/v1/workspaces

Auth: Tenant API key (Bearer token)

Request Body:

FieldTypeRequiredDescription
namestringYesWorkspace name (1--255 chars)
slugstringYesURL-safe unique identifier (must be unique within tenant)
quotaRowsnumberNoRow limit override (null = inherits tenant default)
metadataobjectNoArbitrary JSON metadata

Example Request:

{
  "name": "Production",
  "slug": "production"
}

Example Response (201):

{
  "id": "ws_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "tenantId": "tenant-uuid",
  "name": "Production",
  "slug": "production",
  "quotaRows": null,
  "usedRows": 0,
  "metadata": null,
  "createdAt": "2026-02-20T10:00:00.000Z",
  "updatedAt": "2026-02-20T10:00:00.000Z"
}

Error Responses:

  • 409 -- A workspace with the given slug already exists for this tenant

Get Workspace

GET /api/v1/workspaces/:id

Auth: Tenant API key (Bearer token)

Error Responses:

  • 404 -- Workspace not found or belongs to another tenant

Update Workspace

PATCH /api/v1/workspaces/:id

Auth: Tenant API key (Bearer token)

Request Body:

FieldTypeRequiredDescription
namestringNoNew workspace name
quotaRowsnumberNoNew row limit
metadataobjectNoNew metadata (replaces existing)

Error Responses:

  • 404 -- Workspace not found or belongs to another tenant

Delete Workspace

DELETE /api/v1/workspaces/:id

Auth: Tenant API key (Bearer token)

Deletes the workspace and all tables, rows, columns, and views within it. This action is irreversible.

Example Response (200):

{ "success": true }

Error Responses:

  • 404 -- Workspace not found or belongs to another tenant

Tenant

Get Tenant Info

GET /api/v1/tenant/info

Auth: Tenant API key (Bearer token)

Example Response (200):

{
  "id": "tenant-uuid",
  "name": "My App",
  "quotaRows": 100000,
  "usedRows": 1234,
  "maxTables": 100,
  "createdAt": "2026-02-15T08:00:00.000Z"
}

Admin

Admin endpoints require the ADMIN_API_KEY environment variable to be configured. They use a separate API key from tenant keys.

Create Tenant

POST /api/v1/admin/tenants

Auth: Admin API key (Bearer token)

Request Body:

FieldTypeRequiredDescription
namestringYesTenant name (1--100 chars)
quotaRowsnumberNoMax rows (default: 100,000)
maxTablesnumberNoMax tables (default: 100)

Example Request:

{
  "name": "My Application",
  "quotaRows": 500000,
  "maxTables": 200
}

Example Response (201):

{
  "tenant": {
    "id": "new-tenant-uuid",
    "name": "My Application",
    "quotaRows": 500000,
    "usedRows": 0,
    "maxTables": 200,
    "createdAt": "2026-02-20T10:00:00.000Z"
  },
  "apiKey": "sk_live_abc123def456..."
}

Important: The apiKey field is only returned once at creation. Store it securely.


Health

Health Check

GET /health

Auth: None

Example Response (200):

{
  "status": "ok",
  "timestamp": "2026-02-20T10:00:00.000Z",
  "environment": "production"
}