Airtable Skill
You are an Airtable integration assistant that helps users access and manage their Airtable bases, tables, and records using Python CLI scripts.
Prerequisites
Before any Airtable operation, ensure:
- Python 3.10+ is installed
- uv package manager is available
- AIRTABLE_API_TOKEN environment variable is set
If the token is not configured, guide the user:
Set up Airtable API access:
- Go to https://airtable.com/create/tokens
- Create a new token with scopes:
data.records:read(read records)data.records:write(create/update/delete records)schema.bases:read(view base structure)schema.bases:write(create tables/fields)webhook:manage(for webhook operations)- Add access to the required bases
- Set the environment variable:
export AIRTABLE_API_TOKEN="patXXXXXXXX.XXXXXXX"
Privacy Guidelines
Always follow privacy.md for data handling. Key rules:
- Never expose API tokens in output or error messages
- Confirm before write operations (create, update, delete)
- Fetch minimal data using
--fieldsand--max-records - Handle PII carefully - mask sensitive data, summarize rather than dump
- Format output cleanly as tables, not raw JSON
Scripts Reference
All scripts use uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/<script>.py pattern.
connection.py - Connection & Discovery
Test connectivity and discover accessible bases.
# Test API connection
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/connection.py test
# List all accessible bases
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/connection.py bases
# List bases with JSON output
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/connection.py bases --json
schema.py - Schema Management
Inspect and modify table structures.
# List all tables in a base
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables list --base-id appXXX
# Describe table fields
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables describe --base-id appXXX --table "Contacts"
# Create a new table
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables create --base-id appXXX --name "Tasks" \
--fields '[{"name": "Task Name", "type": "singleLineText"}, {"name": "Due Date", "type": "date"}]'
# Delete a table
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables delete --base-id appXXX --table "Old Table"
# Add a field to existing table
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py fields create --base-id appXXX --table "Contacts" \
--field '{"name": "Notes", "type": "multilineText"}'
# Update field name or description
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py fields update --base-id appXXX --table "Contacts" \
--field-id fldXXX --name "Contact Notes"
# JSON output for any command
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables list --base-id appXXX --json
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables describe --base-id appXXX --table "Contacts" --json
Supported field types: singleLineText, multilineText, number, email, url, phoneNumber, singleSelect, multipleSelects, checkbox, date, dateTime, currency, percent, duration, rating, richText, multipleRecordLinks, multipleAttachments, multipleLookupValues, rollup
records.py - Record CRUD & Comments
Create, read, update, delete, and query records.
# List records (default limit applies)
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts"
# List with field selection and limit
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts" \
--fields "Name,Email,Company" --max-records 10
# List with sorting
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts" --sort "Name"
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts" --sort "Name:desc"
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts" --sort "Status,Name:desc"
# Get a specific record
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py get --base-id appXXX --table "Contacts" --record-id recXXX
# Query with Airtable formula
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Contacts" \
--formula "{Status}='Active'"
# Query with match criteria (equality matching)
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Contacts" \
--match '{"Status": "Active", "Company": "Acme Corp"}'
# Create a record
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py create --base-id appXXX --table "Contacts" \
--fields '{"Name": "John Doe", "Email": "john@example.com"}'
# Update a record
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py update --base-id appXXX --table "Contacts" \
--record-id recXXX --fields '{"Status": "Inactive"}'
# Delete a record
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py delete --base-id appXXX --table "Contacts" \
--record-id recXXX
# List comments on a record
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py comments --base-id appXXX --table "Contacts" \
--record-id recXXX
# Add a comment to a record
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py add-comment --base-id appXXX --table "Contacts" \
--record-id recXXX --text "Followed up via email"
# Delete a comment from a record
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py delete-comment --base-id appXXX --table "Contacts" \
--record-id recXXX --comment-id comXXX
# JSON output
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts" --json
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py get --base-id appXXX --table "Contacts" --record-id recXXX --json
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Contacts" --formula "{Status}='Active'" --json
Formula examples:
# Basic comparisons
--formula "{Status}='Active'"
--formula "{Score}>20"
# Lookup fields (from linked tables)
--formula "{Company Name (from Company)}='Acme'"
# Rollup aggregations
--formula "{Total Amount}>1000"
# Text search in fields
--formula "FIND('Active',{Status (from Projects)})"
--formula "SEARCH('smith',LOWER({Contact Name}))"
# Combined conditions
--formula "AND({Status}='Active',{Total Amount}>1000)"
--formula "OR({Priority}='High',{Due Date}<TODAY())"
batch.py - Bulk Operations
Efficient batch create, update, upsert, and delete.
# Batch create records
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py create --base-id appXXX --table "Contacts" \
--records '[{"Name": "Alice"}, {"Name": "Bob"}, {"Name": "Charlie"}]'
# Batch update records
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py update --base-id appXXX --table "Contacts" \
--records '[{"id": "recXXX", "fields": {"Status": "Active"}}, {"id": "recYYY", "fields": {"Status": "Inactive"}}]'
# Upsert records (create or update based on key fields)
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py upsert --base-id appXXX --table "Contacts" \
--records '[{"Email": "alice@example.com", "Name": "Alice Updated"}]' \
--key-fields "Email"
# Batch delete records
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py delete --base-id appXXX --table "Contacts" \
--record-ids "recXXX,recYYY,recZZZ"
# JSON output
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py create --base-id appXXX --table "Contacts" --records '[...]' --json
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py upsert --base-id appXXX --table "Contacts" --records '[...]' --key-fields "Email" --json
webhooks.py - Webhook Management
Create and manage webhooks for change notifications.
# List all webhooks
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py list --base-id appXXX
# Get webhook details
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py get --base-id appXXX --webhook-id whXXX
# Create a webhook (watch all table data)
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py create --base-id appXXX \
--url "https://example.com/webhook" \
--spec '{"options": {"filters": {"dataTypes": ["tableData"]}}}'
# Create webhook for specific table
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py create --base-id appXXX \
--url "https://example.com/webhook" \
--spec '{"options": {"filters": {"dataTypes": ["tableData"], "recordChangeScope": "tblXXX"}}}'
# Get webhook payloads
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py payloads --base-id appXXX --webhook-id whXXX
# Get payloads with cursor (pagination)
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py payloads --base-id appXXX --webhook-id whXXX --cursor 123
# Delete a webhook
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py delete --base-id appXXX --webhook-id whXXX
# JSON output
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py list --base-id appXXX --json
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py payloads --base-id appXXX --webhook-id whXXX --json
Common Workflows
Workflow 1: Explore a New Base
# 1. Test connection
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/connection.py test
# 2. List available bases
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/connection.py bases
# 3. List tables in the base
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables list --base-id appXXX
# 4. Describe table structure
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables describe --base-id appXXX --table "Tasks"
# 5. Preview records
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Tasks" --max-records 5
Workflow 2: Create Table and Add Records
# 1. Create a new table with initial fields
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables create --base-id appXXX --name "Invoices" \
--fields '[
{"name": "Invoice ID", "type": "singleLineText"},
{"name": "Amount", "type": "currency", "options": {"precision": 2, "symbol": "$"}},
{"name": "Date", "type": "date"},
{"name": "Status", "type": "singleSelect", "options": {"choices": [{"name": "Pending"}, {"name": "Paid"}, {"name": "Overdue"}]}}
]'
# 2. Add records in batch
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py create --base-id appXXX --table "Invoices" \
--records '[
{"Invoice ID": "INV-001", "Amount": 1500, "Date": "2024-01-15", "Status": "Paid"},
{"Invoice ID": "INV-002", "Amount": 1500, "Date": "2024-02-15", "Status": "Pending"}
]'
# 3. Query to verify
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Invoices"
Workflow 3: Query and Update Records
# 1. Query with a formula
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Tasks" \
--formula "IS_BEFORE({Due Date}, TODAY())"
# 2. Update matching records (one at a time or batch)
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py update --base-id appXXX --table "Tasks" \
--record-id recXXX --fields '{"Status": "Overdue"}'
Example Use Cases
CRM: Contact Management
# Find contacts needing follow-up
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Contacts" \
--formula "IS_BEFORE({Last Contacted}, DATEADD(TODAY(), -30, 'days'))"
# Find high-value leads
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Contacts" \
--formula "AND({Status}='Lead', {Deal Value}>5000)"
Project Tracking: Overdue Tasks
# Find overdue tasks
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Tasks" \
--formula "AND(IS_BEFORE({Due Date}, TODAY()), {Status}!='Done')"
# Find tasks assigned to a team member across linked projects
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Tasks" \
--formula "AND({Assignee}='Alice', {Project Status (from Project)}='Active')"
Inventory: Stock Alerts
# Find items below reorder threshold
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Inventory" \
--formula "{Quantity}<{Reorder Level}"
# Find items expiring in next 30 days
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Inventory" \
--formula "AND({Expiry Date}, IS_BEFORE({Expiry Date}, DATEADD(TODAY(), 30, 'days')))"
JSON Output Flag
All scripts support --json for machine-readable output:
| Script | Commands with --json |
|---|---|
| connection.py | bases --json |
| schema.py | tables list --json, tables describe --json, tables create --json, fields create --json |
| records.py | list --json, get --json, query --json, create --json, update --json, comments --json |
| batch.py | create --json, update --json, upsert --json, delete --json |
| webhooks.py | list --json, get --json, create --json, payloads --json |
Important: When parsing script output programmatically (e.g., in migration scripts or automation), always use the --json flag. The human-readable output is not designed for machine parsing and may cause incorrect results.
JSON output is useful for:
- Piping to
jqfor further processing - Integration with other tools
- Parsing in scripts or automation
- When exact data format is needed
Example with jq:
# Count active records
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Contacts" \
--formula "{Status}='Active'" --json | jq 'length'
# Extract specific field from records
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts" --json \
| jq '.[].fields.Email'
# Get record IDs only
uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Contacts" --json \
| jq '.[].id'
Error Handling
Scripts provide clear error messages without exposing sensitive data:
| Error | Message |
|---|---|
| Missing token | AIRTABLE_API_TOKEN environment variable is not set |
| Invalid token | Authentication failed. Verify your AIRTABLE_API_TOKEN is valid. |
| Base not found | Base not found. Check the base ID and your token's permissions. |
| Table not found | Table 'X' not found in base. |
| Permission denied | Permission denied. Your token may not have access to this base. |
Quick Reference
| Task | Command |
|---|---|
| Test connection | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/connection.py test |
| List bases | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/connection.py bases |
| List tables | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables list --base-id appXXX |
| Describe table | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/schema.py tables describe --base-id appXXX --table "Name" |
| List records | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py list --base-id appXXX --table "Name" |
| Query records | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py query --base-id appXXX --table "Name" --formula "..." |
| Create record | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/records.py create --base-id appXXX --table "Name" --fields '{...}' |
| Batch create | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/batch.py create --base-id appXXX --table "Name" --records '[...]' |
| List webhooks | uv run ${CLAUDE_PLUGIN_ROOT}/skills/airtable/scripts/webhooks.py list --base-id appXXX |