> ## 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
```
