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

# Airtable

The io.b3os/airtable pack: 7 actions with call snippets, fixture outputs, connection setup and error codes, generated from version 1.0.0.

`io.b3os/airtable` 1.0.0 · `databases`, `productivity` · MIT

Read and write Airtable bases with your own personal access token: list bases and their table schemas, list and filter records with formulas and views, get one record, and create, update or delete up to 10 records per call. The token needs data.records:read and write, and schema.bases:read for the schema actions, on the bases you grant it.

Every call's result carries `upstream_status` and `timing`: `validate_ms`, `inject_ms`, `render_ms`, `upstream_ms`, `map_ms` and `total_ms`. `upstream` is the service's time; the others are B3OS's own ([call actions](/actions/call)). The snippets below show no timing values, because a fixture records none.

## Connection

`token`, type `bearer`: Airtable personal access token.

A personal access token (pat...) from airtable.com/create/tokens with the scopes and bases the actions need, or an OAuth access token of your own Airtable integration.

How to get one: [airtable.com](https://airtable.com/developers/web/guides/personal-access-tokens).

The credential is sent only to `api.airtable.com`.

Store the credential once as a [vault connection](/vault) for this pack and key, then name its id in a call's `connections`. Leave the key out and B3OS uses the project's newest connection for the pack and key without a `subject` ([connections](/actions/call#connections)).

Hosts it reaches: `api.airtable.com`.

## Actions

| Action | Effect | MCP tool | Title |
|---|---|---|---|
| [`list-bases`](/actions/catalog/airtable#airtable-list-bases) | read | `airtable__list-bases` | List bases |
| [`get-base-schema`](/actions/catalog/airtable#airtable-get-base-schema) | read | `airtable__get-base-schema` | Get base schema |
| [`list-records`](/actions/catalog/airtable#airtable-list-records) | read | `airtable__list-records` | List records |
| [`get-record`](/actions/catalog/airtable#airtable-get-record) | read | `airtable__get-record` | Get record |
| [`create-records`](/actions/catalog/airtable#airtable-create-records) | write | `airtable__create-records` | Create records |
| [`update-records`](/actions/catalog/airtable#airtable-update-records) | write | `airtable__update-records` | Update records |
| [`delete-records`](/actions/catalog/airtable#airtable-delete-records) | irreversible | `airtable__delete-records` | Delete records |

list-bases

**List bases.** The bases the token can access, with id, name and permission level. Page with offset.

Ref `io.b3os/airtable/list-bases@1.0.0` · effect `read` · connection `token` · MCP tool `airtable__list-bases`

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
          "ref": "io.b3os/airtable/list-bases@1",
          "input": {},
          "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}
        }'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call({
      ref: "io.b3os/airtable/list-bases@1",
      input: {},
      connections: { token: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
    });
    console.log(result.output);
    ```
  **Python**

```python
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call({
        "ref": "io.b3os/airtable/list-bases@1",
        "input": {},
        "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
    })
    print(result["output"])
    ```
  **MCP**

```json
    {
      "method": "tools/call",
      "params": {"name": "airtable__list-bases", "arguments": {}}
    }
    ```

    Expose the pack as tools with `--packs airtable` on the local server or `?packs=airtable` on the remote URL, and the agent calls `airtable__list-bases` with the action's input. The tool uses your newest connection for each key without a `subject`. Without exposing the pack, `actions_call` takes the same input with `ref`.

Output, labeled `fixture`: List bases (documented response shape, example values). From the bundle's `fixtures/list-bases.json`, not a live call.

```json
{
  "bases": [
    {
      "id": "appLkNDICXNqxSDhG",
      "name": "Launch planning",
      "permission_level": "create"
    },
    {
      "id": "appSW9R5uCNmRmfl6",
      "name": "Customer feedback",
      "permission_level": "read"
    }
  ],
  "offset": null
}
```

When the service answers with an error, the call fails with [`upstream_error`](/errors#upstream_error) and `pack_error.code` is one of:

| `pack_error.code` | Service status | Retryable | Message |
|---|---|---|---|
| `not_found` | 404 | no | Base, table or record not found, or the token cannot access it. |
| `connection_unauthorized` | 401 | no | Airtable rejected the personal access token. |
| `forbidden` | 403 | no | The token lacks the scope or base access this needs. |
| `rate_limited` | 429 | yes | Airtable allows 5 requests a second per base; retry in 30 seconds. |
| `invalid_request` | 422, 400 | no | built from the service's response |

get-base-schema

**Get base schema.** Every table of a base with its id, name, primary field and fields (id, name, type such as singleLineText, number, singleSelect, multipleRecordLinks). Read it to learn field names before writing records.

Ref `io.b3os/airtable/get-base-schema@1.0.0` · effect `read` · connection `token` · MCP tool `airtable__get-base-schema`

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
          "ref": "io.b3os/airtable/get-base-schema@1",
          "input": {"base_id": "appLkNDICXNqxSDhG"},
          "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}
        }'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call({
      ref: "io.b3os/airtable/get-base-schema@1",
      input: { base_id: "appLkNDICXNqxSDhG" },
      connections: { token: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
    });
    console.log(result.output);
    ```
  **Python**

```python
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call({
        "ref": "io.b3os/airtable/get-base-schema@1",
        "input": {"base_id": "appLkNDICXNqxSDhG"},
        "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
    })
    print(result["output"])
    ```
  **MCP**

```json
    {
      "method": "tools/call",
      "params": {
        "name": "airtable__get-base-schema",
        "arguments": {"base_id": "appLkNDICXNqxSDhG"}
      }
    }
    ```

    Expose the pack as tools with `--packs airtable` on the local server or `?packs=airtable` on the remote URL, and the agent calls `airtable__get-base-schema` with the action's input. The tool uses your newest connection for each key without a `subject`. Without exposing the pack, `actions_call` takes the same input with `ref`.

Output, labeled `fixture`: Get base schema (documented response shape, example values). From the bundle's `fixtures/get-base-schema.json`, not a live call.

```json
{
  "tables": [
    {
      "id": "tbltp8DGLhqbUmjK1",
      "name": "Tasks",
      "primary_field_id": "fld1VnoyuotSTyxW1",
      "fields": [
        {
          "id": "fld1VnoyuotSTyxW1",
          "name": "Name",
          "type": "singleLineText",
          "description": null
        },
        {
          "id": "fldoaIqdn5szURHpw",
          "name": "Status",
          "type": "singleSelect",
          "description": null
        },
        {
          "id": "fldumZe00w09RYTW6",
          "name": "Due",
          "type": "date",
          "description": "Ship date"
        }
      ]
    }
  ]
}
```

When the service answers with an error, the call fails with [`upstream_error`](/errors#upstream_error) and `pack_error.code` is one of:

| `pack_error.code` | Service status | Retryable | Message |
|---|---|---|---|
| `not_found` | 404 | no | Base, table or record not found, or the token cannot access it. |
| `connection_unauthorized` | 401 | no | Airtable rejected the personal access token. |
| `forbidden` | 403 | no | The token lacks the scope or base access this needs. |
| `rate_limited` | 429 | yes | Airtable allows 5 requests a second per base; retry in 30 seconds. |
| `invalid_request` | 422, 400 | no | built from the service's response |

list-records

**List records.** Records of a table, optionally through a view and filtered with an Airtable formula such as AND(\{Status\} = 'Open', \{Due\} &lt; TODAY()), sorted by one field, limited to some fields. Up to 100 per page; pass the returned offset to continue.

Ref `io.b3os/airtable/list-records@1.0.0` · effect `read` · connection `token` · MCP tool `airtable__list-records`

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
          "ref": "io.b3os/airtable/list-records@1",
          "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "filter_formula": "{Status} = '\''Open'\''",
            "sort_field": "Due",
            "page_size": 2
          },
          "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}
        }'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call({
      ref: "io.b3os/airtable/list-records@1",
      input: {
        base_id: "appLkNDICXNqxSDhG",
        table: "Tasks",
        filter_formula: "{Status} = 'Open'",
        sort_field: "Due",
        page_size: 2,
      },
      connections: { token: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
    });
    console.log(result.output);
    ```
  **Python**

```python
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call({
        "ref": "io.b3os/airtable/list-records@1",
        "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "filter_formula": "{Status} = 'Open'",
            "sort_field": "Due",
            "page_size": 2,
        },
        "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
    })
    print(result["output"])
    ```
  **MCP**

```json
    {
      "method": "tools/call",
      "params": {
        "name": "airtable__list-records",
        "arguments": {
          "base_id": "appLkNDICXNqxSDhG",
          "table": "Tasks",
          "filter_formula": "{Status} = 'Open'",
          "sort_field": "Due",
          "page_size": 2
        }
      }
    }
    ```

    Expose the pack as tools with `--packs airtable` on the local server or `?packs=airtable` on the remote URL, and the agent calls `airtable__list-records` with the action's input. The tool uses your newest connection for each key without a `subject`. Without exposing the pack, `actions_call` takes the same input with `ref`.

Output, labeled `fixture`: List records (documented response shape, example values). From the bundle's `fixtures/list-records.json`, not a live call.

```json
{
  "records": [
    {
      "id": "recwPQIfs4wKPyc9D",
      "created_time": "2026-10-20T15:00:00.000Z",
      "fields": {
        "Name": "Write launch post",
        "Status": "Open",
        "Due": "2026-10-30"
      }
    },
    {
      "id": "rec4Ac2dR7bpeVUi5",
      "created_time": "2026-10-20T15:00:00.000Z",
      "fields": {"Name": "Record demo", "Status": "Open"}
    }
  ],
  "offset": "itrHUOz6JbUT8QYDU/rec4Ac2dR7bpeVUi5"
}
```

When the service answers with an error, the call fails with [`upstream_error`](/errors#upstream_error) and `pack_error.code` is one of:

| `pack_error.code` | Service status | Retryable | Message |
|---|---|---|---|
| `not_found` | 404 | no | Base, table or record not found, or the token cannot access it. |
| `connection_unauthorized` | 401 | no | Airtable rejected the personal access token. |
| `forbidden` | 403 | no | The token lacks the scope or base access this needs. |
| `rate_limited` | 429 | yes | Airtable allows 5 requests a second per base; retry in 30 seconds. |
| `invalid_request` | 422, 400 | no | built from the service's response |

get-record

**Get record.** One record by id with all its non-empty fields.

Ref `io.b3os/airtable/get-record@1.0.0` · effect `read` · connection `token` · MCP tool `airtable__get-record`

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
          "ref": "io.b3os/airtable/get-record@1",
          "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "record_id": "recwPQIfs4wKPyc9D"
          },
          "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}
        }'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call({
      ref: "io.b3os/airtable/get-record@1",
      input: {
        base_id: "appLkNDICXNqxSDhG",
        table: "Tasks",
        record_id: "recwPQIfs4wKPyc9D",
      },
      connections: { token: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
    });
    console.log(result.output);
    ```
  **Python**

```python
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call({
        "ref": "io.b3os/airtable/get-record@1",
        "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "record_id": "recwPQIfs4wKPyc9D",
        },
        "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
    })
    print(result["output"])
    ```
  **MCP**

```json
    {
      "method": "tools/call",
      "params": {
        "name": "airtable__get-record",
        "arguments": {
          "base_id": "appLkNDICXNqxSDhG",
          "table": "Tasks",
          "record_id": "recwPQIfs4wKPyc9D"
        }
      }
    }
    ```

    Expose the pack as tools with `--packs airtable` on the local server or `?packs=airtable` on the remote URL, and the agent calls `airtable__get-record` with the action's input. The tool uses your newest connection for each key without a `subject`. Without exposing the pack, `actions_call` takes the same input with `ref`.

Output, labeled `fixture`: Get record (documented response shape, example values). From the bundle's `fixtures/get-record.json`, not a live call.

```json
{
  "id": "recwPQIfs4wKPyc9D",
  "created_time": "2026-10-20T15:00:00.000Z",
  "fields": {"Name": "Write launch post", "Status": "Open", "Due": "2026-10-30"}
}
```

When the service answers with an error, the call fails with [`upstream_error`](/errors#upstream_error) and `pack_error.code` is one of:

| `pack_error.code` | Service status | Retryable | Message |
|---|---|---|---|
| `not_found` | 404 | no | Base, table or record not found, or the token cannot access it. |
| `connection_unauthorized` | 401 | no | Airtable rejected the personal access token. |
| `forbidden` | 403 | no | The token lacks the scope or base access this needs. |
| `rate_limited` | 429 | yes | Airtable allows 5 requests a second per base; retry in 30 seconds. |
| `invalid_request` | 422, 400 | no | built from the service's response |

create-records

**Create records.** Create 1 to 10 records in a table from field values keyed by field name. Returns the new records with their ids. Not idempotent.

Ref `io.b3os/airtable/create-records@1.0.0` · effect `write` · connection `token` · MCP tool `airtable__create-records`

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Idempotency-Key: 7c1e2f6a-1d43-4a8e-b2a5-3f0c9d5e8b11" \
      -H "Content-Type: application/json" \
      -d '{
          "ref": "io.b3os/airtable/create-records@1",
          "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "records": [{"Name": "Publish changelog", "Status": "Open"}]
          },
          "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}
        }'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call({
      ref: "io.b3os/airtable/create-records@1",
      input: {
        base_id: "appLkNDICXNqxSDhG",
        table: "Tasks",
        records: [{ Name: "Publish changelog", Status: "Open" }],
      },
      connections: { token: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
    }, { idempotencyKey: crypto.randomUUID() });
    console.log(result.output);
    ```
  **Python**

```python
    import uuid
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call({
        "ref": "io.b3os/airtable/create-records@1",
        "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "records": [{"Name": "Publish changelog", "Status": "Open"}],
        },
        "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
    }, idempotency_key=str(uuid.uuid4()))
    print(result["output"])
    ```
  **MCP**

```json
    {
      "method": "tools/call",
      "params": {
        "name": "airtable__create-records",
        "arguments": {
          "base_id": "appLkNDICXNqxSDhG",
          "table": "Tasks",
          "records": [{"Name": "Publish changelog", "Status": "Open"}]
        }
      }
    }
    ```

    Expose the pack as tools with `--packs airtable` on the local server or `?packs=airtable` on the remote URL, and the agent calls `airtable__create-records` with the action's input. The tool uses your newest connection for each key without a `subject`. Without exposing the pack, `actions_call` takes the same input with `ref`.

Output, labeled `fixture`: Create records (documented response shape, example values). From the bundle's `fixtures/create-records.json`, not a live call.

```json
{
  "records": [
    {
      "id": "recNEW0000000001a",
      "created_time": "2026-10-20T15:00:00.000Z",
      "fields": {"Name": "Publish changelog", "Status": "Open"}
    }
  ]
}
```

When the service answers with an error, the call fails with [`upstream_error`](/errors#upstream_error) and `pack_error.code` is one of:

| `pack_error.code` | Service status | Retryable | Message |
|---|---|---|---|
| `not_found` | 404 | no | Base, table or record not found, or the token cannot access it. |
| `connection_unauthorized` | 401 | no | Airtable rejected the personal access token. |
| `forbidden` | 403 | no | The token lacks the scope or base access this needs. |
| `rate_limited` | 429 | yes | Airtable allows 5 requests a second per base; retry in 30 seconds. |
| `invalid_request` | 422, 400 | no | built from the service's response |

update-records

**Update records.** Update 1 to 10 records by id: only the fields given change (PATCH semantics), others keep their values. Returns the updated records.

Ref `io.b3os/airtable/update-records@1.0.0` · effect `write` · connection `token` · MCP tool `airtable__update-records`

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Idempotency-Key: 7c1e2f6a-1d43-4a8e-b2a5-3f0c9d5e8b11" \
      -H "Content-Type: application/json" \
      -d '{
          "ref": "io.b3os/airtable/update-records@1",
          "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "records": [{"id": "recwPQIfs4wKPyc9D", "fields": {"Status": "Done"}}]
          },
          "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}
        }'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call({
      ref: "io.b3os/airtable/update-records@1",
      input: {
        base_id: "appLkNDICXNqxSDhG",
        table: "Tasks",
        records: [{ id: "recwPQIfs4wKPyc9D", fields: { Status: "Done" } }],
      },
      connections: { token: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
    }, { idempotencyKey: crypto.randomUUID() });
    console.log(result.output);
    ```
  **Python**

```python
    import uuid
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call({
        "ref": "io.b3os/airtable/update-records@1",
        "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "records": [
                {
                    "id": "recwPQIfs4wKPyc9D",
                    "fields": {"Status": "Done"},
                },
            ],
        },
        "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
    }, idempotency_key=str(uuid.uuid4()))
    print(result["output"])
    ```
  **MCP**

```json
    {
      "method": "tools/call",
      "params": {
        "name": "airtable__update-records",
        "arguments": {
          "base_id": "appLkNDICXNqxSDhG",
          "table": "Tasks",
          "records": [{"id": "recwPQIfs4wKPyc9D", "fields": {"Status": "Done"}}]
        }
      }
    }
    ```

    Expose the pack as tools with `--packs airtable` on the local server or `?packs=airtable` on the remote URL, and the agent calls `airtable__update-records` with the action's input. The tool uses your newest connection for each key without a `subject`. Without exposing the pack, `actions_call` takes the same input with `ref`.

Output, labeled `fixture`: Update records (documented response shape, example values). From the bundle's `fixtures/update-records.json`, not a live call.

```json
{
  "records": [
    {
      "id": "recwPQIfs4wKPyc9D",
      "created_time": "2026-10-20T15:00:00.000Z",
      "fields": {
        "Name": "Write launch post",
        "Status": "Done",
        "Due": "2026-10-30"
      }
    }
  ]
}
```

When the service answers with an error, the call fails with [`upstream_error`](/errors#upstream_error) and `pack_error.code` is one of:

| `pack_error.code` | Service status | Retryable | Message |
|---|---|---|---|
| `not_found` | 404 | no | Base, table or record not found, or the token cannot access it. |
| `connection_unauthorized` | 401 | no | Airtable rejected the personal access token. |
| `forbidden` | 403 | no | The token lacks the scope or base access this needs. |
| `rate_limited` | 429 | yes | Airtable allows 5 requests a second per base; retry in 30 seconds. |
| `invalid_request` | 422, 400 | no | built from the service's response |

delete-records

**Delete records.** Delete 1 to 10 records by id. Deleted records go to the base's trash for 7 days (Pro plan and above); the API cannot restore them.

Ref `io.b3os/airtable/delete-records@1.0.0` · effect `irreversible` · connection `token` · MCP tool `airtable__delete-records`

**curl**

```bash
    curl -s https://beta-api.b3os.org/v1/actions/call \
      -H "Authorization: Bearer $B3OS_API_KEY" \
      -H "Idempotency-Key: 7c1e2f6a-1d43-4a8e-b2a5-3f0c9d5e8b11" \
      -H "Content-Type: application/json" \
      -d '{
          "ref": "io.b3os/airtable/delete-records@1",
          "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "record_ids": ["rec4Ac2dR7bpeVUi5"]
          },
          "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"}
        }'
    ```
  **TypeScript**

```ts
    import { B3OS } from "@b3os/sdk";

    const b3os = new B3OS();
    const result = await b3os.actions.call({
      ref: "io.b3os/airtable/delete-records@1",
      input: {
        base_id: "appLkNDICXNqxSDhG",
        table: "Tasks",
        record_ids: ["rec4Ac2dR7bpeVUi5"],
      },
      connections: { token: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },
    }, { idempotencyKey: crypto.randomUUID() });
    console.log(result.output);
    ```
  **Python**

```python
    import uuid
    from b3os import B3OS

    b3os = B3OS()
    result = b3os.actions.call({
        "ref": "io.b3os/airtable/delete-records@1",
        "input": {
            "base_id": "appLkNDICXNqxSDhG",
            "table": "Tasks",
            "record_ids": ["rec4Ac2dR7bpeVUi5"],
        },
        "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},
    }, idempotency_key=str(uuid.uuid4()))
    print(result["output"])
    ```
  **MCP**

```json
    {
      "method": "tools/call",
      "params": {
        "name": "airtable__delete-records",
        "arguments": {
          "base_id": "appLkNDICXNqxSDhG",
          "table": "Tasks",
          "record_ids": ["rec4Ac2dR7bpeVUi5"]
        }
      }
    }
    ```

    Expose the pack as tools with `--packs airtable` on the local server or `?packs=airtable` on the remote URL, and the agent calls `airtable__delete-records` with the action's input. The tool uses your newest connection for each key without a `subject`. Without exposing the pack, `actions_call` takes the same input with `ref`.

Output, labeled `fixture`: Delete records (documented response shape, example values). From the bundle's `fixtures/delete-records.json`, not a live call.

```json
{"records": [{"id": "rec4Ac2dR7bpeVUi5", "deleted": true}]}
```

When the service answers with an error, the call fails with [`upstream_error`](/errors#upstream_error) and `pack_error.code` is one of:

| `pack_error.code` | Service status | Retryable | Message |
|---|---|---|---|
| `not_found` | 404 | no | Base, table or record not found, or the token cannot access it. |
| `connection_unauthorized` | 401 | no | Airtable rejected the personal access token. |
| `forbidden` | 403 | no | The token lacks the scope or base access this needs. |
| `rate_limited` | 429 | yes | Airtable allows 5 requests a second per base; retry in 30 seconds. |
| `invalid_request` | 422, 400 | no | built from the service's response |