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). 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.
The credential is sent only to api.airtable.com.
Store the credential once as a vault connection 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).
Hosts it reaches: api.airtable.com.
Actions
| Action | Effect | MCP tool | Title |
|---|---|---|---|
list-bases | read | airtable__list-bases | List bases |
get-base-schema | read | airtable__get-base-schema | Get base schema |
list-records | read | airtable__list-records | List records |
get-record | read | airtable__get-record | Get record |
create-records | write | airtable__create-records | Create records |
update-records | write | airtable__update-records | Update records |
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
bashcurl -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"} }'
tsimport { 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);
pythonfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/airtable/list-bases@1", "input": {}, "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},})print(result["output"])
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 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
bashcurl -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"} }'
tsimport { 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);
pythonfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/airtable/get-base-schema@1", "input": {"base_id": "appLkNDICXNqxSDhG"}, "connections": {"token": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},})print(result["output"])
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 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} < 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
bashcurl -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"} }'
tsimport { 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);
pythonfrom b3os import B3OSb3os = 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"])
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 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
bashcurl -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"} }'
tsimport { 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);
pythonfrom b3os import B3OSb3os = 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"])
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 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
bashcurl -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"} }'
tsimport { 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);
pythonimport uuidfrom b3os import B3OSb3os = 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"])
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 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
bashcurl -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"} }'
tsimport { 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);
pythonimport uuidfrom b3os import B3OSb3os = 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"])
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 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
bashcurl -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"} }'
tsimport { 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);
pythonimport uuidfrom b3os import B3OSb3os = 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"])
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 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 |