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

# Tables

Tables are the built-in structured data store behind `core.table.*`.
Use them when your workflows need durable, queryable records such as asset inventories, user allowlists, enrichment results, or investigation evidence.

## Common workflow pattern

1. Create the table once with a schema that fits your data and a unique index on the upsert column.
2. Insert or upsert rows as new events arrive.
3. Look up, search, or export rows later from another workflow step.

```yaml theme={null}
- ref: ensure_inventory_table
  action: core.table.create_table
  args:
    name: asset_inventory
    columns:
      - name: hostname
        type: TEXT
      - name: owner
        type: TEXT
      - name: last_seen
        type: TIMESTAMPTZ
    raise_on_duplicate: false
- ref: inventory_metadata
  action: core.table.get_table_metadata
  depends_on:
    - ensure_inventory_table
  args:
    name: asset_inventory
- ref: index_hostname
  action: core.table.update_column
  depends_on:
    - inventory_metadata
  run_if: ${{ FN.length(ACTIONS.inventory_metadata.result.columns[?(@.is_index == true)]) == 0 }}
  args:
    table: asset_inventory
    column: hostname
    update:
      is_index: true
- ref: upsert_asset
  action: core.table.insert_row
  depends_on:
    - inventory_metadata
    - index_hostname
  join_strategy: any
  args:
    table: asset_inventory
    upsert: true
    row_data:
      hostname: ${{ TRIGGER.hostname }}
      owner: ${{ TRIGGER.owner }}
      last_seen: ${{ FN.now() }}
- ref: find_asset
  action: core.table.lookup
  depends_on:
    - upsert_asset
  args:
    table: asset_inventory
    column: hostname
    value: ${{ TRIGGER.hostname }}
```

## Column schema

`core.table.create_table` takes `columns` as a JSON array of column objects.
This is the same schema you use for tables that you later link to cases.

* `name`: Required string. Use letters, numbers, and underscores, and start with a letter or underscore.
* `type`: Required uppercase string. Use `TEXT`, `INTEGER`, `NUMERIC`, `BOOLEAN`, `DATE`, `TIMESTAMPTZ`, `JSONB`, `SELECT`, or `MULTI_SELECT`.
* `nullable`: Optional boolean. Defaults to `true`.
* `default`: Optional value. It must match the column type.
* `options`: Optional array of strings. Required for `SELECT` and `MULTI_SELECT`, and invalid for other types.

The documented `type` values match the custom tables picker, and case custom fields use the same storage types.
The case field picker surfaces raw `JSONB` through the `URL` kind and layers `Long text` on top of `TEXT`.

Create a table with a `SELECT` column:

```yaml theme={null}
- ref: create_table
  action: core.table.create_table
  args:
    name: asset_inventory
    columns:
      - name: hostname
        type: TEXT
      - name: first_seen_at
        type: TIMESTAMPTZ
      - name: owner_team
        type: SELECT
        options:
          - secops
          - platform
          - it
```

## FAQ

<AccordionGroup>
  <Accordion title="How do I insert more than 1000 rows into a table?">
    Split large imports into batches upstream, then run one `insert_rows` action per batch.

    ```yaml theme={null}
    - ref: insert_batch
      action: core.table.insert_rows
      for_each: ${{ for var.batch in TRIGGER.row_batches }}
      args:
        table: asset_inventory
        rows_data: ${{ var.batch }}
    ```
  </Accordion>
</AccordionGroup>

## `core.table.create_table`

Create a new lookup table with optional columns.

<Info>
  `columns` takes the column objects described in
  [Column schema](#column-schema).
</Info>

### Inputs

<ParamField path="name" type="string" required>
  The name of the table to create.
</ParamField>

<ParamField path="columns" type="array[object] | null">
  List of column definitions. Each item is an object with required `name` and uppercase `type`, plus optional `nullable`, `default`, and `options` fields. Use `TEXT`, `INTEGER`, `NUMERIC`, `BOOLEAN`, `DATE`, `TIMESTAMPTZ`, `JSONB`, `SELECT`, or `MULTI_SELECT`. `options` is required for `SELECT` and `MULTI_SELECT`, and invalid for other types.

  Default: `null`.
</ParamField>

<ParamField path="raise_on_duplicate" type="boolean">
  If true, raise an error if the table already exists.

  Default: `true`.
</ParamField>

### Examples

**Create and inspect a table**

```yaml theme={null}
- ref: create_table
  action: core.table.create_table
  args:
    name: asset_inventory
    columns:
      - name: hostname
        type: TEXT
      - name: owner
        type: TEXT
    raise_on_duplicate: false
- ref: list_tables
  action: core.table.list_tables
- ref: table_metadata
  action: core.table.get_table_metadata
  args:
    name: asset_inventory
```

## `core.table.list_tables`

Get a list of all available tables in the workspace.

### Inputs

This action does not take input fields.

### Examples

**Create and inspect a table**

```yaml theme={null}
- ref: create_table
  action: core.table.create_table
  args:
    name: asset_inventory
    columns:
      - name: hostname
        type: TEXT
      - name: owner
        type: TEXT
    raise_on_duplicate: false
- ref: list_tables
  action: core.table.list_tables
- ref: table_metadata
  action: core.table.get_table_metadata
  args:
    name: asset_inventory
```

## `core.table.get_table_metadata`

Get a table's metadata by name. This includes the columns and whether they are indexed.

### Inputs

<ParamField path="name" type="string" required>
  The name of the table to get.
</ParamField>

### Examples

**Create and inspect a table**

```yaml theme={null}
- ref: create_table
  action: core.table.create_table
  args:
    name: asset_inventory
    columns:
      - name: hostname
        type: TEXT
      - name: owner
        type: TEXT
    raise_on_duplicate: false
- ref: list_tables
  action: core.table.list_tables
- ref: table_metadata
  action: core.table.get_table_metadata
  args:
    name: asset_inventory
```

## `core.table.update_table`

Rename a table by name.

### Inputs

<ParamField path="name" type="string" required>
  The current name of the table to update.
</ParamField>

<ParamField path="new_name" type="string" required>
  The new table name.
</ParamField>

### Examples

**Rename a table**

```yaml theme={null}
- ref: rename_table
  action: core.table.update_table
  args:
    name: asset_inventory
    new_name: assets
```

## `core.table.create_column`

Add a column to an existing table.

### Inputs

<ParamField path="column" type="object" required>
  Column definition with required `name` and uppercase `type`, plus optional `nullable`, `default`, and `options` fields. Use `TEXT`, `INTEGER`, `NUMERIC`, `BOOLEAN`, `DATE`, `TIMESTAMPTZ`, `JSONB`, `SELECT`, or `MULTI_SELECT`. `options` is required for `SELECT` and `MULTI_SELECT`.
</ParamField>

<ParamField path="table" type="string" required>
  The table to add the column to.
</ParamField>

### Examples

**Manage table schema**

```yaml theme={null}
- ref: rename_table
  action: core.table.update_table
  args:
    name: asset_inventory
    new_name: assets
- ref: add_owner_team
  action: core.table.create_column
  depends_on:
    - rename_table
  args:
    table: assets
    column:
      name: owner_team
      type: SELECT
      options:
        - secops
        - platform
- ref: drop_owner_team
  action: core.table.delete_column
  depends_on:
    - add_owner_team
  args:
    table: assets
    column: owner_team
```

## `core.table.update_column`

Update a table column's name, type, nullability, default, index, or options.

Set `update: {is_index: true}` to create the table's unique index on
the column. A table can have one single-column unique index:
creation fails if the column already contains duplicate values, and a
second `is_index: true` call raises `Table cannot have multiple
unique indexes`.

Guard the step when a workflow repeats it; see
[Tables](/automations/tables#index-and-upsert).

### Inputs

<ParamField path="column" type="string" required>
  The current column name.
</ParamField>

<ParamField path="table" type="string" required>
  The table containing the column.
</ParamField>

<ParamField path="update" type="object" required>
  Partial column update. Supported fields: `name`, `type`, `nullable`, `default`, `is_index`, and `options`.
</ParamField>

### Examples

**Create a unique index and upsert**

```yaml theme={null}
- ref: ensure_alerts_table
  action: core.table.create_table
  args:
    name: alerts
    columns:
      - name: alert_id
        type: TEXT
      - name: status
        type: TEXT
    raise_on_duplicate: false
- ref: alerts_metadata
  action: core.table.get_table_metadata
  depends_on:
    - ensure_alerts_table
  args:
    name: alerts
- ref: index_alert_id
  action: core.table.update_column
  depends_on:
    - alerts_metadata
  run_if: ${{ FN.length(ACTIONS.alerts_metadata.result.columns[?(@.is_index == true)]) == 0 }}
  args:
    table: alerts
    column: alert_id
    update:
      is_index: true
- ref: upsert_alert
  action: core.table.insert_row
  depends_on:
    - alerts_metadata
    - index_alert_id
  join_strategy: any
  args:
    table: alerts
    upsert: true
    row_data:
      alert_id: ${{ TRIGGER.alert_id }}
      status: ${{ TRIGGER.status }}
```

## `core.table.delete_column`

Delete a column from an existing table.

### Inputs

<ParamField path="column" type="string" required>
  The column name to delete.
</ParamField>

<ParamField path="table" type="string" required>
  The table containing the column.
</ParamField>

### Examples

**Delete a column**

```yaml theme={null}
- ref: drop_owner_team
  action: core.table.delete_column
  args:
    table: assets
    column: owner_team
```

## `core.table.lookup`

Get a single row from a table corresponding to the given column and value.

### Inputs

<ParamField path="column" type="string" required>
  The column to lookup the value in.
</ParamField>

<ParamField path="table" type="string" required>
  The table to lookup the value in.
</ParamField>

<ParamField path="value" type="any" required>
  The value to lookup.
</ParamField>

### Examples

**Look up rows**

```yaml theme={null}
- ref: lookup_row
  action: core.table.lookup
  args:
    table: asset_inventory
    column: hostname
    value: ${{ TRIGGER.hostname }}
- ref: row_exists
  action: core.table.is_in
  args:
    table: asset_inventory
    column: hostname
    value: ${{ TRIGGER.hostname }}
- ref: lookup_many_rows
  action: core.table.lookup_many
  args:
    table: asset_inventory
    column: owner
    value: secops
    limit: 25
```

## `core.table.is_in`

Check if a value exists in a table column.

### Inputs

<ParamField path="column" type="string" required>
  The column to check in.
</ParamField>

<ParamField path="table" type="string" required>
  The table to check.
</ParamField>

<ParamField path="value" type="any" required>
  The value to check for.
</ParamField>

### Examples

**Look up rows**

```yaml theme={null}
- ref: lookup_row
  action: core.table.lookup
  args:
    table: asset_inventory
    column: hostname
    value: ${{ TRIGGER.hostname }}
- ref: row_exists
  action: core.table.is_in
  args:
    table: asset_inventory
    column: hostname
    value: ${{ TRIGGER.hostname }}
- ref: lookup_many_rows
  action: core.table.lookup_many
  args:
    table: asset_inventory
    column: owner
    value: secops
    limit: 25
```

## `core.table.lookup_many`

Get multiple rows from a table corresponding to the given column and values.

### Inputs

<ParamField path="column" type="string" required>
  The column to lookup the value in.
</ParamField>

<ParamField path="table" type="string" required>
  The table to lookup the value in.
</ParamField>

<ParamField path="value" type="any" required>
  The value to lookup.
</ParamField>

<ParamField path="limit" type="integer">
  The maximum number of rows to return.

  Default: `100`.
</ParamField>

### Examples

**Look up rows**

```yaml theme={null}
- ref: lookup_row
  action: core.table.lookup
  args:
    table: asset_inventory
    column: hostname
    value: ${{ TRIGGER.hostname }}
- ref: row_exists
  action: core.table.is_in
  args:
    table: asset_inventory
    column: hostname
    value: ${{ TRIGGER.hostname }}
- ref: lookup_many_rows
  action: core.table.lookup_many
  args:
    table: asset_inventory
    column: owner
    value: secops
    limit: 25
```

## `core.table.search_rows`

Search for rows in a table with optional filtering.

### Inputs

<ParamField path="table" type="string" required>
  The table to search in.
</ParamField>

<ParamField path="cursor" type="string | null">
  Cursor for pagination.

  Default: `null`.
</ParamField>

<ParamField path="end_time" type="string | null">
  Filter rows created before this time.

  Default: `null`.
</ParamField>

<ParamField path="limit" type="integer">
  The maximum number of rows to return.

  Default: `100`.
</ParamField>

<ParamField path="paginate" type="boolean">
  If true, return cursor pagination metadata along with items.

  Default: `false`.
</ParamField>

<ParamField path="reverse" type="boolean">
  Reverse pagination direction.

  Default: `false`.
</ParamField>

<ParamField path="search_term" type="string | null">
  Text to search for across all text and JSONB columns.

  Default: `null`.
</ParamField>

<ParamField path="start_time" type="string | null">
  Filter rows created after this time.

  Default: `null`.
</ParamField>

<ParamField path="updated_after" type="string | null">
  Filter rows updated after this time.

  Default: `null`.
</ParamField>

<ParamField path="updated_before" type="string | null">
  Filter rows updated before this time.

  Default: `null`.
</ParamField>

### Examples

**Search table rows**

```yaml theme={null}
- ref: search_rows
  action: core.table.search_rows
  args:
    table: asset_inventory
    search_term: database
    limit: 50
    paginate: true
```

## `core.table.insert_row`

Insert a row into a table.

### Inputs

<ParamField path="row_data" type="object" required>
  The data to insert into the row.
</ParamField>

<ParamField path="table" type="string" required>
  The table to insert the row into.
</ParamField>

<ParamField path="upsert" type="boolean">
  If true, update the row if it already exists (based on primary key).

  Default: `false`.
</ParamField>

### Examples

**Insert, update, and delete rows**

```yaml theme={null}
- ref: insert_row
  action: core.table.insert_row
  args:
    table: asset_inventory
    row_data:
      hostname: app-01
      owner: secops
- ref: insert_rows
  action: core.table.insert_rows
  args:
    table: asset_inventory
    rows_data:
      - hostname: app-02
        owner: secops
      - hostname: app-03
        owner: platform
- ref: update_row
  action: core.table.update_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
    row_data:
      owner: incident-response
- ref: delete_row
  action: core.table.delete_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
```

## `core.table.insert_rows`

Insert multiple rows into a table.

### Inputs

<ParamField path="rows_data" type="array[object]" required>
  The list of data to insert into the table.
</ParamField>

<ParamField path="table" type="string" required>
  The table to insert the rows into.
</ParamField>

<ParamField path="upsert" type="boolean">
  If true, update the rows if they already exist (based on primary key).

  Default: `false`.
</ParamField>

### Examples

**Insert, update, and delete rows**

```yaml theme={null}
- ref: insert_row
  action: core.table.insert_row
  args:
    table: asset_inventory
    row_data:
      hostname: app-01
      owner: secops
- ref: insert_rows
  action: core.table.insert_rows
  args:
    table: asset_inventory
    rows_data:
      - hostname: app-02
        owner: secops
      - hostname: app-03
        owner: platform
- ref: update_row
  action: core.table.update_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
    row_data:
      owner: incident-response
- ref: delete_row
  action: core.table.delete_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
```

## `core.table.update_row`

Update a row in a table.

### Inputs

<ParamField path="row_data" type="object" required>
  The new data for the row.
</ParamField>

<ParamField path="row_id" type="string" required>
  The ID of the row to update.
</ParamField>

<ParamField path="table" type="string" required>
  The table to update the row in.
</ParamField>

### Examples

**Insert, update, and delete rows**

```yaml theme={null}
- ref: insert_row
  action: core.table.insert_row
  args:
    table: asset_inventory
    row_data:
      hostname: app-01
      owner: secops
- ref: insert_rows
  action: core.table.insert_rows
  args:
    table: asset_inventory
    rows_data:
      - hostname: app-02
        owner: secops
      - hostname: app-03
        owner: platform
- ref: update_row
  action: core.table.update_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
    row_data:
      owner: incident-response
- ref: delete_row
  action: core.table.delete_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
```

## `core.table.delete_row`

Delete a row from a table.

### Inputs

<ParamField path="row_id" type="string" required>
  The ID of the row to delete.
</ParamField>

<ParamField path="table" type="string" required>
  The table to delete the row from.
</ParamField>

### Examples

**Insert, update, and delete rows**

```yaml theme={null}
- ref: insert_row
  action: core.table.insert_row
  args:
    table: asset_inventory
    row_data:
      hostname: app-01
      owner: secops
- ref: insert_rows
  action: core.table.insert_rows
  args:
    table: asset_inventory
    rows_data:
      - hostname: app-02
        owner: secops
      - hostname: app-03
        owner: platform
- ref: update_row
  action: core.table.update_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
    row_data:
      owner: incident-response
- ref: delete_row
  action: core.table.delete_row
  args:
    table: asset_inventory
    row_id: ${{ TRIGGER.row_id }}
```

## `core.table.download`

Download a table's data by name as list of dicts, JSON string, NDJSON string, CSV or Markdown.

### Inputs

<ParamField path="name" type="string" required>
  The name of the table to download.
</ParamField>

<ParamField path="format" type="string | null">
  The format to download the table data in.

  Default: `null`.
</ParamField>

<ParamField path="limit" type="integer">
  The maximum number of rows to download.

  Default: `1000`.
</ParamField>

### Examples

**Export table data**

```yaml theme={null}
- ref: export_table
  action: core.table.download
  args:
    name: asset_inventory
    format: csv
    limit: 1000
```
