---
title: "Add Column"
method: POST
path: "/api/bitables/{table_id}/columns"
tags: ["bitables"]
---

# Add Column

`POST /api/bitables/{table_id}/columns`

## Request

- Base URL: `https://api.tintin-x.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.tintin-x.com/api/bitables/{table_id}/columns`
- Auth: HTTP bearer

## Path parameters

- `table_id` string, uuid, required

## Request body

- SrcSchemasBitableColumnCreate: Input for column creation — `id` is auto-assigned if omitted. Positioning: pass either `after_id` or `before_id` (not both) to splice the new column next to an existing one. Default (neither) appends to the end, matching the original behavior.
  - `name` string, required
  - `type` 'text' | 'number' | 'boolean' | 'date' | 'select' | 'multi_select' | 'attachment' | 'url' | 'user' | 'agent_ref' | 'form' | 'link', required
  - `width` integer, nullable
  - `options` SelectOption[], nullable
    - `id` string, required
    - `label` string, required
    - `color` string, nullable
  - `after_id` string, nullable
  - `before_id` string, nullable
  - `recipe` ColumnRecipe: Column → skill binding with version + gate config.
    - `skill` string, nullable
    - `skill_dispatch` SkillDispatch: Variant selection: pick the skill by a sibling cell's value.
      - `on_col` string, required
      - `map` object, required
    - `inputs` ColumnInputRef[]
      - `from` string, required
      - `required` boolean
      - `use_current` boolean
      - `param` string, nullable
      - `extract` string, nullable
    - `output_to` string, nullable
    - `also_write` string[]
    - `auto_run` boolean
    - `gate` RecipeGate
      - `requires_approval` boolean
    - `row_gate` RecipeRowGate: Row-level gate keyed off a sibling status cell. Used by skills like smart-bulk-tryon where users want explicit per-stage review gates. Each recipe declares which `status_col` (sibling column on the same row) drives gating and which `allow` list of cell values unblocks the recipe. Executor flow: when row.data[status_col] is NOT in `allow`, the cell parks at `pending_approval` (same state as `requires_approval` gates). User advances the status via the bulk-update-cell endpoint, which stamps a one-shot Redis bypass marker so the next executor tick dispatches without re-gating. Cascade re-fires after that naturally pass because the status value still satisfies `allow`. Both `status_col` and `allow` are matched by either column id OR display name on the recipe's parent table — same tolerance as `inputs[].from`. Missing column → executor treats as not-in-allow (defensive).
      - `status_col` string, required
      - `allow` string[], required
    - `row_status_writes` RecipeRowStatusWrites: Row-level status auto-transitions declared by the recipe. Three hooks; each writes a literal value into row.data[status_col] (the same column referenced by `row_gate.status_col`). Missing keys are no-ops, so an intermediate cell can declare on_dispatch + on_fail while skipping on_deliver (used by video clip cells where only the final stitched video cell owns the `done` transition). Helper enforces monotonic forward-only ordering via a per-skill stage map — a late tryon_back deliver doesn't drag a row already advanced to `awaiting_pose` back to `tryon_review`.
      - `on_dispatch` string, nullable
      - `on_deliver` string, nullable
      - `on_fail` string, nullable
    - `sync` RecipeSync
      - `source` string, required
      - `poll_interval` string
    - `dep_version_mode` 'latest' | 'pinned' | 'branch'
    - `execution_mode` 'fire_and_forget' | 'async_poll' | 'sync'
    - `result_delivery` 'webhook' | 'inline' | 'external'
    - `result_merge` 'append_attachment_on_regen', nullable
    - `learned_constraints` string[]
    - `skill_version_pin` string, nullable
    - `static_params` object
    - `validator` ColumnValidator: AI-critic config for a versioned column (B8). When set, every `done` cell transitions to `validating`; the executor dispatches `skill` with the declared `inputs` and the worker posts back to `/api/internal/bitable/cell-validation`. Findings are advisory — the validator NEVER mutates the cell value. If `auto_regen_threshold` is set and the returned score falls below it, the callback enqueues one regen via the existing B3.2 re-fire path (`iteration_note = suggested_regen_note`). The per-cell `auto_regen_count` counter on `BitableCellState` caps this at `auto_regen_cap` — a broken validator cannot loop. `summary_write` is the optional row-level rollup surface — see `ColumnValidatorSummaryWrite`.
      - `skill` string, required
      - `inputs` ColumnInputRef[]
        - `from` string, required
        - `required` boolean
        - `use_current` boolean
        - `param` string, nullable
        - `extract` string, nullable
      - `rubric` object, nullable
      - `auto_regen_threshold` number, nullable
      - `auto_regen_cap` integer
      - `static_params` object
      - `summary_write` ColumnValidatorSummaryWrite: Sibling-column writes performed automatically by the cell-validation callback after a validator result lands. The validator row (`bitable_cell_validations`) is the canonical score record and the UI renders a per-cell score badge off of it. But that record isn't visible from the grid's row-level overview — users who want a filterable boolean ("is this row cleared?") + a readable log ("what did critic say, over time?") need the data surfaced into plain sibling columns. This struct declares those sibling targets. The callback computes: - `pass_col` ← bool(score >= pass_threshold) when score is non-null - `notes_col` ← a markdown fragment (timestamp + score + findings + suggested_regen_note), either appended to the existing text (`notes_mode="append"`) with a `---` horizontal-rule separator, or replacing it (`notes_mode="replace"`). Both target columns MUST be unversioned (text/boolean/select) and MUST exist on the table. Missing columns are ignored (warn log); type mismatches write and let the cell-value validator reject.
        - `pass_col` string, nullable
        - `notes_col` string, nullable
        - `pass_threshold` number
        - `notes_mode` 'append' | 'replace'
    - `rollup` ColumnRollup: Cross-table aggregation config for a rollup column (B9). A rollup column's value is derived from a sibling link column by reading a specific column on every linked child row and combining the values via `op`: - `skill` : pass `rollup_values=[v1..vN]` into the recipe's skill job; the handler produces the final cell value. Used for video stitch, summarization, or any N→1 transform that needs a worker. - `count` : number of non-null child values (inline). - `first` : first child's value (by child row order_key). - `last` : last child's value. - `concat_text` : stringified children joined with `\n\n` — handy for markdown rollups. Inline ops (everything but `skill`) compute in the executor tick and land via the existing `/cell-result` pipeline — the skill field on the recipe is still used as a label but is not dispatched over SQS.
      - `link_col` string, required
      - `source_col` string, required
      - `op` 'skill' | 'count' | 'first' | 'last' | 'concat_text'
      - `extract` string, nullable
      - `param` string, nullable
      - `require_all_children` boolean
  - `number_format` 'plain' | 'currency' | 'percent', nullable
  - `currency` 'USD' | 'EUR' | 'GBP' | 'JPY' | 'CNY', nullable
  - `min_value` number, nullable
  - `max_value` number, nullable
  - `label_style` 'checkbox' | 'yes_no' | 'toggle', nullable
  - `form_mode` 'list' | 'single', nullable
  - `form_schema` FormFieldSchema[], nullable
    - `id` string, required
    - `name` string, required
    - `type` 'text' | 'number' | 'boolean' | 'date' | 'url' | 'select' | 'multi_select', required
    - `options` string[], nullable
    - `text_format` 'plain' | 'markdown', nullable
  - `text_format` 'plain' | 'markdown', nullable
  - `link_to_table_id` string, uuid, nullable
  - `cascade_policy` 'cascade' | 'orphan' | 'restrict', nullable

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/tintin-x/apis/tintin-api.md) · [All operations](https://skmtc.dev/tintin-x/apis/tintin-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tintin-x/apis/tintin-api/revisions/8839a8d0a66c?raw)
