> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hq.zone/llms.txt
> Use this file to discover all available pages before exploring further.

# Store structured data in tables

> Create typed tables, write and query rows, evolve the schema, and export to CSV — a governed, multi-tenant data store your agents and your team share.

A **table** is a durable, typed, multi-tenant store of structured data — the Airtable-class capability, built native so it composes with HQ's agents, identity chain, and audit. You define typed **fields**, then read and write **rows**; the data persists across conversations, and every write is stamped with who made it (human or agent) and recorded in the audit log.

Tables serves two audiences over one store:

* **Your team** — "save these leads", "track this pipeline" — edited through this API or the web-UI grid. Tables created here are **workspace-owned**.
* **Your agents** — a durable relational scratchpad they read and write through the `hq:tables` MCP server. Agents can own their own tables and even evolve schema under governance.

This guide covers the HTTP API. All routes live under `https://api.hq.zone/v1/api/tables` and authenticate with a personal access token: `Authorization: Bearer hq_pat_...`. Reads need the `tables:read` scope; writes need `tables:write`.

<Note>The tables subsystem is provisioned per deployment. If it isn't configured for your workspace, these routes return `503` — enable the `hq:tables` integration (it ships opt-in/off) and ensure your workspace has the tables data plane provisioned.</Note>

## Field types

Each field has a **logical type** that maps to real typed storage. Pass the type as the `type` string when you create a table or add a field:

| Logical type                      | Notes                                                               |
| --------------------------------- | ------------------------------------------------------------------- |
| `text` / `long_text`              | Short and multi-line strings.                                       |
| `number` / `currency` / `percent` | Numeric; format hints go in `options`.                              |
| `date` / `datetime`               | Calendar date / timestamp.                                          |
| `boolean`                         | True / false.                                                       |
| `single_select` / `multi_select`  | One or many values from a list; the choice list lives in `options`. |
| `attachment`                      | File references (stored as artifact links, never inline bytes).     |
| `link_to_record`                  | A relation to another table — set `link_target` to that table's id. |
| `autonumber`                      | Auto-incrementing integer.                                          |
| `json`                            | Escape hatch for sparse/nested data.                                |

<Note>`formula`, `rollup`, and `lookup` (computed fields) are a planned fast-follow, not yet available.</Note>

Every field accepts `required`, `is_unique`, and `indexed` booleans (all default `false`), plus a free-form `options` object the surfaces interpret per type (for example, a `link_to_record` field records its `target_table` there).

## Create a table

`POST /v1/api/tables` creates a workspace-owned table with its fields in one call.

<Steps>
  <Step title="Define the fields">
    Each field is `{ "name", "type", ... }`. Optional per-field flags: `required`, `is_unique`, `indexed`, `options`, and `link_target` (for `link_to_record`).
  </Step>

  <Step title="POST the table">
    The body takes `name` (required), an optional `slug` and `description`, and the `fields` array.

    ```bash theme={null}
    curl -X POST https://api.hq.zone/v1/api/tables \
      -H "Authorization: Bearer hq_pat_..." -H "Content-Type: application/json" \
      -d '{
            "name": "Leads",
            "description": "Inbound sales leads",
            "fields": [
              { "name": "Company",  "type": "text", "required": true },
              { "name": "Contact",  "type": "text" },
              { "name": "Value",    "type": "currency" },
              { "name": "Stage",    "type": "single_select",
                "options": { "choices": ["New", "Contacted", "Won", "Lost"] } },
              { "name": "Signed",   "type": "boolean" }
            ]
          }'
    # → 200 { "id": "<TABLE_ID>", "slug": "leads", "name": "Leads", "fields": [ ... ] }
    ```

    Field names are slugified into stable **field slugs** (e.g. `Company` → `company`) — you reference rows by those slugs. An invalid spec (unknown type, a `link_to_record` with no `link_target`) returns `400`.
  </Step>
</Steps>

<Tip>See the full request and response shapes on [Create a table](/api-reference/tables/create-table). To list every table in the workspace, use [List tables](/api-reference/tables/list-tables) (`GET /v1/api/tables`); to read one table with its fields, [Get a table](/api-reference/tables/get-table) (`GET /v1/api/tables/{id}`).</Tip>

## Write rows

A row is a JSON object keyed by **field slug**. `POST /v1/api/tables/{id}/rows` inserts one or many.

```bash theme={null}
curl -X POST https://api.hq.zone/v1/api/tables/<TABLE_ID>/rows \
  -H "Authorization: Bearer hq_pat_..." -H "Content-Type: application/json" \
  -d '{
        "rows": [
          { "company": "Acme",   "contact": "Dana",  "value": 12000, "stage": "New" },
          { "company": "Globex", "contact": "Sam",   "value": 48000, "stage": "Contacted" }
        ]
      }'
# → 200 { "ids": ["<ROW_ID>", "<ROW_ID>"] }
```

Update one row's fields with `PATCH /v1/api/tables/{id}/rows/{row_id}` — only the slugs you include change:

```bash theme={null}
curl -X PATCH https://api.hq.zone/v1/api/tables/<TABLE_ID>/rows/<ROW_ID> \
  -H "Authorization: Bearer hq_pat_..." -H "Content-Type: application/json" \
  -d '{ "patch": { "stage": "Won", "signed": true } }'
# → 200 { "updated": true }
```

Delete a row with `DELETE /v1/api/tables/{id}/rows/{row_id}` (`→ { "deleted": true }`). A missing row returns `404`. References: [Insert rows](/api-reference/tables/insert-rows), [Update a row](/api-reference/tables/update-row), [Delete a row](/api-reference/tables/delete-row).

## Query rows

`POST /v1/api/tables/{id}/rows/query` reads rows with filters, sorting, projection, and pagination. An empty body returns the first page of all rows.

```bash theme={null}
curl -X POST https://api.hq.zone/v1/api/tables/<TABLE_ID>/rows/query \
  -H "Authorization: Bearer hq_pat_..." -H "Content-Type: application/json" \
  -d '{
        "filters": [ { "field": "stage", "op": "eq", "value": "Won" },
                     { "field": "value", "op": "gte", "value": 10000 } ],
        "sorts":   [ { "field": "value", "desc": true } ],
        "select":  ["company", "value", "stage"],
        "limit": 50,
        "offset": 0
      }'
# → 200 { "rows": [ { "id": "<ROW_ID>", "company": "Globex", "value": 48000, "stage": "Won" } ] }
```

* **`filters`** — each `{ "field": "<slug>", "op": "<op>", "value": <any> }`. Operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `is_empty`, `is_not_empty`. Multiple filters combine with AND.
* **`sorts`** — each `{ "field": "<slug>", "desc": <bool> }`.
* **`select`** — field slugs to return; omit for all fields.
* **`limit`** / **`offset`** — page the result set.

<Tip>Full shape on [Query rows](/api-reference/tables/query-rows).</Tip>

## Evolve the schema

Add a field at any time with `POST /v1/api/tables/{id}/fields` (the same field shape as create):

```bash theme={null}
curl -X POST https://api.hq.zone/v1/api/tables/<TABLE_ID>/fields \
  -H "Authorization: Bearer hq_pat_..." -H "Content-Type: application/json" \
  -d '{ "name": "Owner", "type": "text", "indexed": true }'
# → 200 { "id": "<FIELD_ID>", "slug": "owner", ... }
```

Drop a field with `DELETE /v1/api/tables/{id}/fields/{field_id}`, and delete a whole table with `DELETE /v1/api/tables/{id}`.

<Warning>**Additive** changes (add a table, add a field) apply immediately. **Destructive** changes (drop a field, delete a table) are governed: a response of `{ "status": "applied" }` means it took effect, while `{ "status": "pending", "change_id": "..." }` means it was queued for a workspace admin to approve. Agent-initiated destructive changes on shared tables always queue; the typed confirmation in the web-UI grid is the human equivalent.</Warning>

References: [Add a field](/api-reference/tables/add-field), [Drop a field](/api-reference/tables/drop-field), [Delete a table](/api-reference/tables/delete-table).

## Export to CSV

`GET /v1/api/tables/{id}/export.csv` streams the whole table as a UTF-8 CSV attachment (RFC 4180, with a BOM for Excel and formula-injection defanged). It's keyset-paged server-side, so the download is constant-memory and never capped at a page size.

```bash theme={null}
curl -L https://api.hq.zone/v1/api/tables/<TABLE_ID>/export.csv \
  -H "Authorization: Bearer hq_pat_..." -o leads.csv
```

Requires `tables:read`. See [Export a table as CSV](/api-reference/tables/export-table-csv).

## Provenance and audit

Every table and row carries system columns — `created_by`, `updated_by`, timestamps — populated from the [identity chain](/concepts/security), so each row records whether a human or a specific agent (and which conversation) wrote it. Every mutation also emits an event to the always-on audit log. This per-row, tamper-evident provenance is what makes agent-written data accountable — see the [security concept](/concepts/security).

<Tip>Agents reach the same store through the `hq:tables` MCP server (`tables.*` / `rows.*` tools), governed by the same identity and audit. Install it from the [Integrations](/guides/integrations) page and attach it to an agent as you would any MCP server.</Tip>
