/airtable
Airtable REST API via curl. Records CRUD, filters, upserts.
$ npx -y skills add NousResearch/hermes-agent --skill airtable --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/airtable
Context preview
The summary Claude sees to decide when to auto-load this skill.
Airtable REST API via curl. Records CRUD, filters, upserts.
SKILL.md
airtable.SKILL.mdname: airtable
description: Airtable REST API via curl. Records CRUD, filters, upserts.
version: 1.1.0
author: community
license: MIT
platforms: [linux, macos, windows]
prerequisites:
env_vars: [AIRTABLE_API_KEY]
commands: [curl]
metadata:
hermes:
tags: [Airtable, Productivity, Database, API]
homepage: https://airtable.com/developers/web/api/introductionAirtable — Bases, Tables & Records
Work with Airtable's REST API directly via `curl` using the `terminal` tool. No MCP server, no OAuth flow, no Python SDK — just `curl` and a personal access token.
Prerequisites
1. Create a **Personal Access Token (PAT)** at https://airtable.com/create/tokens (tokens start with `pat...`). 2. Grant these scopes (minimum):
- `data.records:read` — read rows
- `data.records:write` — create / update / delete rows
- `schema.bases:read` — list bases and tables
3. **Important:** in the same token UI, add each base you want to access to the token's **Access** list. PATs are scoped per-base — a valid token on the wrong base returns `403`. 4. Store the token in `${HERMES_HOME:-~/.hermes}/.env` (or via `hermes setup`):
AIRTABLE_API_KEY=pat_your_token_here
> Note: legacy `key...` API keys were deprecated Feb 2024. Only PATs and OAuth tokens work now.
API Basics
- **Endpoint:** `https://api.airtable.com/v0`
- **Auth header:** `Authorization: Bearer $AIRTABLE_API_KEY`
- **All requests** use JSON (`Content-Type: application/json` for any POST/PATCH/PUT body).
- **Object IDs:** bases `app...`, tables `tbl...`, records `rec...`, fields `fld...`. IDs never change; names can. Prefer IDs in automations.
- **Rate limit:** 5 requests/sec/base. `429` → back off. Burst on a single base will be throttled.
Base curl pattern:
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=5" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
`-s` suppresses curl's progress bar — keep it set for every call so the tool output stays clean for Hermes. Pipe through `python -m json.tool` (always present) or `jq` (if installed) for readable JSON.
Field Types (request body shapes)
| Field type | Write shape | |---|---| | Single line text | `"Name": "hello"` | | Long text | `"Notes": "multi\nline"` | | Number | `"Score": 42` | | Checkbox | `"Done": true` | | Single select | `"Status": "Todo"` (name must already exist unless `typecast: true`) | | Multi-select | `"Tags": ["urgent", "bug"]` | | Date | `"Due": "2026-04-01"` | | DateTime (UTC) | `"At": "2026-04-01T14:30:00.000Z"` | | URL / Email / Phone | `"Link": "https://…"` | | Attachment | `"Files": [{"url": "https://…"}]` (Airtable fetches + rehosts) | | Linked record | `"Owner": ["recXXXXXXXXXXXXXX"]` (array of record IDs) | | User | `"AssignedTo": {"id": "usrXXXXXXXXXXXXXX"}` |
Pass `"typecast": true` at the top level of a create/update body to let Airtable auto-coerce values (e.g. create a new select option on the fly, convert `"42"` → `42`).
Common Queries
List bases the token can see
curl -s "https://api.airtable.com/v0/meta/bases" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
List tables + schema for a base
curl -s "https://api.airtable.com/v0/meta/bases/$BASE_ID/tables" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Use this BEFORE mutating — confirms exact field names and IDs, surfaces `options.choices` for select fields, and shows primary-field names.
List records (first 10)
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=10" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Get a single record
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Filter records (filterByFormula)
Airtable formulas must be URL-encoded. Let Python stdlib do it — never hand-encode:
FORMULA="{Status}='Todo'"
ENC=$(python -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$FORMULA")
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?filterByFormula=$ENC&maxRecords=20" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.toolUseful formula patterns:
- Exact match: `{Email}='user@example.com'`
- Contains: `FIND('bug', LOWER({Title}))`
- Multiple conditions: `AND({Status}='Todo', {Priority}='High')`
- Or: `OR({Owner}='alice', {Owner}='bob')`
- Not empty: `NOT({Assignee}='')`
- Date comparison: `IS_AFTER({Due}, TODAY())`
Sort + select specific fields
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?sort%5B0%5D%5Bfield%5D=Priority&sort%5B0%5D%5Bdirection%5D=asc&fields%5B%5D=Name&fields%5B%5D=Status" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Square brackets in query params MUST be URL-encoded (`%5B` / `%5D`).
Use a named view
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?view=Grid%20view&maxRecords=50" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Views apply their saved filter + sort server-side.
Common Mutations
Create a record
curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fields":{"Name":"New task","Status":"Todo","Priority":"High"}}' | python -m json.toolCreate up to 10 records in one call
curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"typecast": true,
"records": [
{"fields": {"Name": "Task A", "Status": "Todo"}},
{"fields": {"Name": "Task B", "Status": "In progress"}}
]
}' | python -m json.toolBatch endpoints are capped at **10 records per request**. For larger inserts, loop in batches of 10 with a short sleep to resp
Read more
name: airtable
description: Airtable REST API via curl. Records CRUD, filters, upserts.
version: 1.1.0
author: community
license: MIT
platforms: [linux, macos, windows]
prerequisites:
env_vars: [AIRTABLE_API_KEY]
commands: [curl]
metadata:
hermes:
tags: [Airtable, Productivity, Database, API]
homepage: https://airtable.com/developers/web/api/introductionAirtable — Bases, Tables & Records
Work with Airtable's REST API directly via `curl` using the `terminal` tool. No MCP server, no OAuth flow, no Python SDK — just `curl` and a personal access token.
Prerequisites
1. Create a **Personal Access Token (PAT)** at https://airtable.com/create/tokens (tokens start with `pat...`). 2. Grant these scopes (minimum):
- `data.records:read` — read rows
- `data.records:write` — create / update / delete rows
- `schema.bases:read` — list bases and tables
3. **Important:** in the same token UI, add each base you want to access to the token's **Access** list. PATs are scoped per-base — a valid token on the wrong base returns `403`. 4. Store the token in `${HERMES_HOME:-~/.hermes}/.env` (or via `hermes setup`):
AIRTABLE_API_KEY=pat_your_token_here
> Note: legacy `key...` API keys were deprecated Feb 2024. Only PATs and OAuth tokens work now.
API Basics
- **Endpoint:** `https://api.airtable.com/v0`
- **Auth header:** `Authorization: Bearer $AIRTABLE_API_KEY`
- **All requests** use JSON (`Content-Type: application/json` for any POST/PATCH/PUT body).
- **Object IDs:** bases `app...`, tables `tbl...`, records `rec...`, fields `fld...`. IDs never change; names can. Prefer IDs in automations.
- **Rate limit:** 5 requests/sec/base. `429` → back off. Burst on a single base will be throttled.
Base curl pattern:
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=5" \ -H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
`-s` suppresses curl's progress bar — keep it set for every call so the tool output stays clean for Hermes. Pipe through `python -m json.tool` (always present) or `jq` (if installed) for readable JSON.
Field Types (request body shapes)
| Field type | Write shape | |---|---| | Single line text | `"Name": "hello"` | | Long text | `"Notes": "multi\nline"` | | Number | `"Score": 42` | | Checkbox | `"Done": true` | | Single select | `"Status": "Todo"` (name must already exist unless `typecast: true`) | | Multi-select | `"Tags": ["urgent", "bug"]` | | Date | `"Due": "2026-04-01"` | | DateTime (UTC) | `"At": "2026-04-01T14:30:00.000Z"` | | URL / Email / Phone | `"Link": "https://…"` | | Attachment | `"Files": [{"url": "https://…"}]` (Airtable fetches + rehosts) | | Linked record | `"Owner": ["recXXXXXXXXXXXXXX"]` (array of record IDs) | | User | `"AssignedTo": {"id": "usrXXXXXXXXXXXXXX"}` |
Pass `"typecast": true` at the top level of a create/update body to let Airtable auto-coerce values (e.g. create a new select option on the fly, convert `"42"` → `42`).
Common Queries
List bases the token can see
curl -s "https://api.airtable.com/v0/meta/bases" \ -H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
List tables + schema for a base
curl -s "https://api.airtable.com/v0/meta/bases/$BASE_ID/tables" \ -H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Use this BEFORE mutating — confirms exact field names and IDs, surfaces `options.choices` for select fields, and shows primary-field names.
List records (first 10)
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=10" \ -H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Get a single record
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \ -H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Filter records (filterByFormula)
Airtable formulas must be URL-encoded. Let Python stdlib do it — never hand-encode:
FORMULA="{Status}='Todo'"
ENC=$(python -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$FORMULA")
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?filterByFormula=$ENC&maxRecords=20" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.toolUseful formula patterns:
- Exact match: `{Email}='user@example.com'`
- Contains: `FIND('bug', LOWER({Title}))`
- Multiple conditions: `AND({Status}='Todo', {Priority}='High')`
- Or: `OR({Owner}='alice', {Owner}='bob')`
- Not empty: `NOT({Assignee}='')`
- Date comparison: `IS_AFTER({Due}, TODAY())`
Sort + select specific fields
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?sort%5B0%5D%5Bfield%5D=Priority&sort%5B0%5D%5Bdirection%5D=asc&fields%5B%5D=Name&fields%5B%5D=Status" \ -H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Square brackets in query params MUST be URL-encoded (`%5B` / `%5D`).
Use a named view
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?view=Grid%20view&maxRecords=50" \ -H "Authorization: Bearer $AIRTABLE_API_KEY" | python -m json.tool
Views apply their saved filter + sort server-side.
Common Mutations
Create a record
curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fields":{"Name":"New task","Status":"Todo","Priority":"High"}}' | python -m json.toolCreate up to 10 records in one call
curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"typecast": true,
"records": [
{"fields": {"Name": "Task A", "Status": "Todo"}},
{"fields": {"Name": "Task B", "Status": "In progress"}}
]
}' | python -m json.toolBatch endpoints are capped at **10 records per request**. For larger inserts, loop in batches of 10 with a short sleep to resp
The self-improving AI agent built by Nous Research. It's the only agent with a built-in learning loop — it creates skills from experience, improves them during use, nudges itself to persist knowledge, searches its own past conversations, and builds a
Repo: NousResearch/hermes-agent

