> For the complete documentation index, see [llms.txt](https://whitepaper.aitech.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://whitepaper.aitech.io/agentforge/tools/airtable.md).

# airtable

[Airtable](https://airtable.com/) combines the familiarity of spreadsheets with the structure of databases. With this integration, agents can **read**, **create**, and **update** Airtable records as part of guided, reliable workflows.

**What you can do**

* Fetch multiple records from a base and filter/sort them.
* Retrieve an individual record by its ID.
* Create new entries with typed fields and attachments.
* Update existing records (single or bulk) to keep Airtable in sync with actions your agents take.

**Where this shines**

* Centralizing a “source of truth” dataset for decisions.
* Human-in-the-loop reviews (agents propose updates, teams approve in Airtable).
* Enrichment and scoring pipelines (read → enrich → write-back).
* Campaign/task orchestration across teams.

## Usage Instructions

{% stepper %}
{% step %}

#### Read

Read records to gather context (filter or sort as needed).
{% endstep %}

{% step %}

#### Decide

Decide in your logic layer (classify, verify, enrich, or route).
{% endstep %}

{% step %}

#### Write-back

Update records or create new rows to persist outcomes.
{% endstep %}
{% endstepper %}

### Example Workflow: Read → Decide → Bulk Update

* **Step 1**: Read all *open*, *high-priority* tasks.
* **Step 2**: Decide what changes are needed (e.g., mark as processed).
* **Step 3**: Write back updates in bulk using one API call.

This pattern is useful for cleaning queues, re-scoring tasks, or updating statuses.

### Example Workflow: Idempotent Create (Upsert by Email)

* **Step 1**: Check for an existing record using a unique field like `Email`.
* **Step 2**: If the record exists → update it with new values.
* **Step 3**: If no record exists → create it fresh.

This ensures no duplicates and mimics an **“upsert”** pattern that Airtable doesn’t natively provide.

## Tools

### `airtable_list_records`

Read multiple records from an Airtable table.

#### Input

| Parameter       | Type      | Required | Description                                                      |
| --------------- | --------- | -------- | ---------------------------------------------------------------- |
| `baseId`        | string    | Yes      | ID of the Airtable base (e.g., `appXXXXXXXXXXXXXX`)              |
| `tableId`       | string    | Yes      | ID or name of the table (e.g., `tblXXXXXXXXXXXXXX` or `"Tasks"`) |
| `maxRecords`    | number    | No       | Maximum number of records to return                              |
| `filterFormula` | string    | No       | Airtable formula to filter (e.g., `AND({Status}='Open')`)        |
| `view`          | string    | No       | Use a view’s filters/sorts                                       |
| `sort`          | array     | No       | Sort by fields, e.g., `[{"field":"Due","direction":"asc"}]`      |
| `fields`        | string\[] | No       | Limit returned fields                                            |

#### Output

| Parameter      | Type   | Description                                    |
| -------------- | ------ | ---------------------------------------------- |
| `records`      | json   | Array of records `{ id, fields, createdTime }` |
| `metadata`     | json   | Request/table metadata                         |
| `totalRecords` | number | Count of records retrieved                     |

```json
{
  "records": [
    {
      "id": "rec0tK7W0ICQikuee",
      "createdTime": "2025-07-29T09:49:30.000Z",
      "fields": {
        "Design Name": "Mobile App Onboarding",
        "Submission Date": "2025-08-22",
        "Designer": "Bob Smith",
        "Comments": "Reviewed by the design team."
      }
    }
  ],
  "metadata": { "totalRecords": 1 }
}
```

### `airtable_get_record`

Retrieve a single record from an Airtable table by its **record ID**.\
This is useful when you already know the record’s identifier (for example, from a previous listing) and want the full details.

#### Input

| Parameter  | Type   | Required | Description                          |
| ---------- | ------ | -------- | ------------------------------------ |
| `baseId`   | string | Yes      | ID of the Airtable base              |
| `tableId`  | string | Yes      | ID or name of the table              |
| `recordId` | string | Yes      | Unique record ID (e.g., `rec123XYZ`) |

#### Output

| Parameter  | Type | Description             |
| ---------- | ---- | ----------------------- |
| `record`   | json | Retrieved record object |
| `metadata` | json | Operation metadata      |

#### Example Output

```json
{
  "record": {
    "id": "rec0tK7W0ICQikuee",
    "createdTime": "2025-07-29T09:49:30.000Z",
    "fields": {
      "Design Name": "Mobile App Onboarding",
      "Submission Date": "2025-08-22",
      "Designer": "Bob Smith",
      "Comments": "Reviewed by the design team. Needs minor adjustments in the color scheme."
    }
  },
  "metadata": { "recordCount": 1 }
}
```

### `airtable_create_records`

Create one or more new records in a target Airtable table.\
This tool is most useful when ingesting data from external workflows, collecting user submissions, or creating rows for downstream tracking.

#### Input

| Parameter  | Type    | Required | Description                                                  |
| ---------- | ------- | -------- | ------------------------------------------------------------ |
| `baseId`   | string  | Yes      | ID of the Airtable base                                      |
| `tableId`  | string  | Yes      | ID or name of the table                                      |
| `records`  | array   | Yes      | Array of `{ fields: { ... } }` objects                       |
| `typecast` | boolean | No       | Coerce values into field types (helpful for selects/lookups) |

#### Output

| Parameter  | Type | Description                    |
| ---------- | ---- | ------------------------------ |
| `records`  | json | Newly created records with IDs |
| `metadata` | json | Response metadata              |

#### Example Input

```json
[
  {
    "fields": {
      "Design Name": "Mobile App Onboarding",
      "Submission Date": "2025-08-22",
      "Designer": "Bob Smith",
      "Comments": "Reviewed by the design team. Needs minor adjustments in the color scheme."
    }
  }
]
```

#### Example Output

```json
{
  "records": [
    {
      "id": "rec0tK7W0ICQikuee",
      "createdTime": "2025-07-29T09:49:30.000Z",
      "fields": {
        "Design Name": "Mobile App Onboarding",
        "Submission Date": "2025-08-22",
        "Designer": "Bob Smith",
        "Comments": "Reviewed by the design team. Needs minor adjustments in the color scheme."
      }
    }
  ],
  "metadata": {}
}
```

### `airtable_update_record`

Update a **single record** in Airtable by its unique `recordId`.\
This is commonly used in workflows where an agent enriches, verifies, or modifies data in-place — for example, marking a lead as *Qualified*, updating the status of a task, or adding comments to an entry.

#### Input

| Parameter  | Type    | Required | Description                                                                                              |
| ---------- | ------- | -------- | -------------------------------------------------------------------------------------------------------- |
| `baseId`   | string  | Yes      | Airtable base ID (e.g., `appXXXXXXXXXXXXXX`)                                                             |
| `tableId`  | string  | Yes      | Table ID or name (e.g., `tblXXXXXXXXXXXXXX` or `"Tasks"`)                                                |
| `recordId` | string  | Yes      | Unique ID of the record to update                                                                        |
| `fields`   | object  | Yes      | A JSON object mapping field names to new values (inner dictionary only)                                  |
| `typecast` | boolean | No       | If true, Airtable will coerce values into field types (helpful for selects, lookups, and linked records) |

#### Output

| Parameter       | Type      | Description                             |
| --------------- | --------- | --------------------------------------- |
| `record`        | json      | The updated record object               |
| `metadata`      | json      | Information about the update operation  |
| `updatedFields` | string\[] | Array of field names that were modified |

#### Example Input

```json
{
  "recordId": "rec0tK7W0ICQikuee",
  "fields": {
    "Status": "Processed",
    "Comments": "Task completed successfully.",
    "Processed At": "2025-10-01T14:32:00.000Z"
  }
}
```

#### Example Output

```json
{
  "record": {
    "id": "rec0tK7W0ICQikuee",
    "createdTime": "2025-07-29T09:49:30.000Z",
    "fields": {
      "Design Name": "Mobile App Onboarding",
      "Status": "Processed",
      "Comments": "Task completed successfully.",
      "Processed At": "2025-10-01T14:32:00.000Z"
    }
  },
  "metadata": {
    "updatedFields": ["Status", "Comments", "Processed At"]
  }
}
```

### `airtable_update_multiple_records`

Update **multiple records** in Airtable within a single request.\
Use this for batch status changes, enrichment, and syncing external systems efficiently.

#### Input

| Parameter  | Type    | Required | Description                                                                                     |
| ---------- | ------- | -------- | ----------------------------------------------------------------------------------------------- |
| `baseId`   | string  | Yes      | Airtable base ID (e.g., `appXXXXXXXXXXXXXX`).                                                   |
| `tableId`  | string  | Yes      | Table ID or name (e.g., `tblXXXXXXXXXXXXXX` or `"Tasks"`).                                      |
| `updates`  | array   | Yes      | Array of update objects. Each item must include an `id` (record ID) and a `fields` map.         |
| `typecast` | boolean | No       | If `true`, Airtable coerces values into field types (useful for select, lookup, linked fields). |

Each update object should look like:

```json
{
  "id": "recXXXXXXXXXXXXXX",
  "fields": {
    "Status": "Processed",
    "Comments": "Updated in bulk run"
  }
}
```

#### Output

| Parameter          | Type   | Required | Description                                          |
| ------------------ | ------ | -------- | ---------------------------------------------------- |
| `records`          | json   | Yes      | List of updated record objects (IDs + fields).       |
| `metadata`         | json   | Yes      | Metadata about the operation.                        |
| `updatedRecordIds` | string | Yes      | AArray of record IDs that were updated successfully. |

#### Example Input

```json
{
  "updates": [
    {
      "id": "rec0tK7W0ICQikuee",
      "fields": {
        "Status": "Processed",
        "Processed At": "2025-10-02T10:15:00.000Z"
      }
    },
    {
      "id": "recDFByLwmeFEA3VI",
      "fields": {
        "Status": "Processed",
        "Processed At": "2025-10-02T10:15:00.000Z"
      }
    }
  ]
}
```

#### Example Output

```json
{
  "records": [
    {
      "id": "rec0tK7W0ICQikuee",
      "fields": {
        "Design Name": "Mobile App Onboarding",
        "Status": "Processed",
        "Processed At": "2025-10-02T10:15:00.000Z"
      }
    },
    {
      "id": "recDFByLwmeFEA3VI",
      "fields": {
        "Design Name": "Checkout Flow Improvement",
        "Status": "Processed",
        "Processed At": "2025-10-02T10:15:00.000Z"
      }
    }
  ],
  "metadata": {
    "updatedRecordIds": ["rec0tK7W0ICQikuee", "recDFByLwmeFEA3VI"]
  }
}
```

#### When to Use

* Bulk move tasks from Open → Processed.
* Apply enrichment (scores, segments) to many leads at once.
* Sync external systems that produce batch updates.
* Implement efficient read → decide → write-back pipelines.

#### Debugging Tips

* **Check IDs**: Prefer `tableId` (`tbl...`) in production. Table names can break if renamed.
* **Validate values**: Dates should be ISO 8601 (`YYYY-MM-DDTHH:mm:ssZ`). Ensure select options match exactly.
* **Log responses**: Keep track of Airtable’s error payloads. They often include hints about which field failed.
* **Reduce payloads**: Large bulk updates can trigger timeouts; split into multiple requests.
* **Use Views**: When filtering records, views encapsulate filter logic and reduce complexity in formulas.

### Best Practices

* Use **IDs instead of names** for stability.
* Add **timestamps** like `Updated At` or `Processed At` for traceability.
* Validate inputs before sending (email formats, select values).
* For upserts: **list → check by unique field → update or create**.
* Handle retries gracefully, especially with 429 rate limits.
