Skip to main content

How a Board Works

This page explains the board data model and the mechanics behind every board operation: how tables and rows relate, how typed columns and cells work, how rich-text and file attachments behave, how row comments and visibility are scoped, how row authentication works, and how filtering, sorting, and exports are evaluated. For a quick orientation and copy-paste examples, start with the Boards introduction.

The data model

Boards form a three-level hierarchy:

  • Board — the top-level container. A board groups related tables.
  • Table — belongs to a board. A table defines a set of columns and holds rows.
  • Row — a single record in a table. A row carries a cell value for each column, plus a markdown description and optional comments.
Board
└── Table (columns: Name, Status, Owner, Notes…)
├── Row #1 (cells: "Acme", "In progress", …)
├── Row #2
└── Row #3

Each row has two identifiers: an internal _id (a 24-character object id used in most endpoints) and a human-friendly row number (the small sequential number shown in the grid, e.g. 42). You can fetch a row by either one.

Columns and cells

A table's columns each have a columnId, a label, and a type. Column types include status, dropdown, labels, checkbox, text, paragraph, link, date, email, phone, website, number, location, and users. Status/dropdown/labels columns expose their selectable options (each with an optionId, label, and color); status options also carry a statusGroup of TODO, IN_PROGRESS, or DONE.

When you read a row you get a columns array of cells, each keyed by columnId with a value. Link cells reference other rows; lookup cells surface values pulled from linked rows.

When you write a row you pass columns: [{ columnId, value }]. A few column types have special value semantics:

  • Link columns take an array of row-id strings. Passing [] clears the link. Linked rows must exist in the target table and the same workspace.
  • Rich-text (paragraph) columns take a markdown string. The content is seeded asynchronously, so it may appear a moment after the write returns.

Reading boards, tables, and rows

  • List boards — returns every board your token can access, with an optional q to filter by name or description.
  • Get a board / List tables / Get a table — drill into a board's structure and read a table's column definitions.
  • List rows — returns the rows of a table. Supports three query parameters that compose together: q (case-insensitive search across searchable columns), filter (a structured JSON filter, below), and sort.
  • Get a row — by internal id, or by row number using the dedicated row-number endpoint.

Filtering rows

The filter query parameter is a JSON string with this shape:

{
"match": "and",
"conditions": [
{ "column_id": "<columnId>", "operator": "contains", "value": "acme" },
{ "column_id": "<statusColumnId>", "operator": "includes", "value": ["<optionId>"] }
]
}
  • match is and (default) or or.
  • conditions is an array of up to 20 conditions. Each names a column_id, an operator, and (for most operators) a value.
  • The valid operators depend on the column's type:
    • Text: equals, not_equals, contains, not_contains, starts_with, ends_with, is_empty, is_not_empty.
    • Number: equals, not_equals, gt, gte, lt, lte, includes, not_includes, is_empty, is_not_empty.
    • Select (status / dropdown / labels): equals, not_equals, includes, not_includes, is_empty, is_not_empty (values are option ids).
    • Checkbox: equals, not_equals, is_empty, is_not_empty.
    • Date: equals, before, after, between (value is a [startISO, endISO] pair), plus relative no-value operators like today, last_7_days, current_month, and is_empty / is_not_empty.

An unknown column id or an operator that doesn't apply to the column type returns a 400.

Sorting rows

The sort query parameter is a comma-separated list of columnId:direction entries, where direction is asc (default) or desc:

?sort=statusColumnId:asc,createdColumnId:desc
note

The HTTP sort query string uses the columnId:direction form. Some SDK examples express sorting as an array of { column, dir } objects — both describe the same ordering, just in different shapes.

Writing rows

  • Create a rowPOST with an optional markdown description and a columns array (which may be empty). Returns the created row.
  • Update a rowPATCH with a non-empty columns array. Only the columns you send are changed; the rest are left untouched.
  • Delete a row — permanently removes the row.

Bots act like users: an action your token performs can trigger the board's automations, so programmatic writes integrate with the same workflows your team uses.

Row description and rich-text columns

Both a row's description and any rich-text column are markdown. Read them with the corresponding …/md GET endpoint, which returns { "content": "…" }.

To write them, POST an operation of replace, append, or prepend plus the markdown content. These writes are processed asynchronously and return HTTP 202 Accepted — the content lands a moment later.

File attachments

File-column cells and comment attachments can be downloaded directly. The download endpoints stream the raw file bytes (with the appropriate content-type and filename headers) rather than returning a signed URL.

Row comments

Rows support threaded comments, each with a visibility:

  • internal — visible only to workspace members.
  • external — visible to everyone with row access, including guests.

List comments supports cursor pagination (after / before) and a visibility filter of all (default), internal, or external. Create a comment takes HTML content and a visibility that defaults to internal.

Authenticating a row

Tables can act as a lightweight credential store. The authenticate endpoint takes an identifier column + value and a password column + value, and returns the matching row (with password columns masked) when the credentials are valid:

  • 400 — no row matches the identifier.
  • 401 — the password is incorrect.

This lets you build sign-in style checks against a board table without exposing the password cell.

Exporting a table view

The export endpoint renders a table view to a file. You POST the viewId and a formatCSV, XLSX, JSON, MARKDOWN, HTML, PDF, ZIP, or ICS — along with optional filters, sort, column selection, and per-format options.

Exports run synchronously or asynchronously depending on size:

  • Small exports return 200 OK with the rendered file inline (text formats as UTF-8, binary formats base64-encoded).
  • Large exports (and PDF/ZIP, which are always async, or any request with forceAsync: true) return 202 Accepted with an asyncJob describing the job and, once ready, a downloadUrl. You can also supply a webhookUrl to be notified when the file is ready.

Authentication & scope

Board endpoints accept a full Personal Access Token (cp_pat_) or an integration API key (cp_key_), and require the access_boards scope. See Authentication for token types and Error Handling for the standard error shape.

Reference