en un clic
airtable
Airtable REST API via curl. Records CRUD, filters, upserts.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Menu
Airtable REST API via curl. Records CRUD, filters, upserts.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Basé sur la classification professionnelle SOC
Use when a problem or request is underspecified and you need to decide WHAT to clarify before doing the work — the NEXT-BEST QUESTIONS to answer, ranked. Interrogates the prompt into candidate questions, projects plausible answers with probabilities, estimates each question's value of information (how much the answer would change the recommended plan, weighted by likelihood and stakes), discards low-value/redundant ones, and keeps generating until a diverse bucket of high-value questions is filled. Reports a ranked list with recommendations (pre-answer / assume-default) using role-specialized local Ollama models. Reports only — it does not ask the user or answer the questions itself. Triggers: "what should I clarify", "what questions matter here", "is this spec complete", "what am I missing before I start".
Use to run one improvement iteration on the next-best-questions skill: review its learnings journal, research opportunities, pre-register a cost-aware experiment, build off-default, evaluate Δresult AND Δcost, and journal the verdict either way. Triggers: 'improve nbq', 'run the nbq loop', 'next-best-questions iteration'. There are NO scripts; this protocol is agent-executed.
Prompt any model or alias via "ask <model> <question>". Resolves short names (deepseek, kimi, qwen, glm) to full model IDs. Captures session IDs for follow-up questions. Comparison mode: "ask deepseek kimi <question>" dispatches multiple models in parallel. Each call is a full Hermes agent with tools and multi-turn reasoning. Replies inline with a model badge.
Autonomous end-to-end build/debug engine. Use it when asked to build, implement, fix, or debug a module/feature: it runs the WHOLE loop unattended — drafts a checkable Definition of Done, fail-closed gates it (vague or ambiguous requests come back with the blocking questions instead of guesses), generates tests, then loops implement -> lint -> evidence until coverage + two distinct-model judges + real exit codes + a whole-suite regression gate all pass — and on COMPLETE AUTO-MERGES the work into the target repo's current branch (no repo named = a fresh workspace under the write-safe root is the deliverable). Prefer this over the `dev` skill for any multi-file task whose outcome should be verified, merged code rather than role-by-role assistance. NOT for trivial one-shot asks (a quick answer, a one-line edit, running a command) — just do those directly. Run: `devloop "<request>" [--repo PATH]` (or python3 ${HERMES_HOME}/skills/software-development/devloop/scripts/devloop_cli.py) — see "How to run".
An LLM authors a JSON/YAML workflow spec (states, prompts, routing, failure policy); a durable interpreter runs it, passing state from step to step, with suspend/resume, human gates, and replay that never re-runs completed steps. Use when an LLM should define-and-run a multi-step workflow that must pause for a human (approve, decide, fix something) and resume later, survive a crash and continue exactly once, or route declaratively on step errors. Rides a durable-execution-lite engine (append-only journal, deterministic replay). Triggers: LLM-authored workflow, workflow spec, prompt routing, durable resume, resumable script, pause and resume, checkpoint and continue, don't re-run completed steps.
Cron job that reviews Hermes conversation history and captures durable knowledge into the wiki. Handles triage (SKIP/NOTE/PAGE), Slack-session cleanup, and post-capture lint.
| 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/introduction","config":[{"key":"airtable.enabled","description":"Enable airtable skill behavior","default":true,"prompt":"Enable airtable skill?"}],"category":"productivity"}} |
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.
pat...).data.records:read — read rowsdata.records:write — create / update / delete rowsschema.bases:read — list bases and tables403.${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.
https://api.airtable.com/v0Authorization: Bearer $AIRTABLE_API_KEYContent-Type: application/json for any POST/PATCH/PUT body).app..., tables tbl..., records rec..., fields fld.... IDs never change; names can. Prefer IDs in automations.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" | python3 -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 python3 -m json.tool (always present) or jq (if installed) for readable JSON.
| 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).
curl -s "https://api.airtable.com/v0/meta/bases" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
curl -s "https://api.airtable.com/v0/meta/bases/$BASE_ID/tables" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
Use this BEFORE mutating — confirms exact field names and IDs, surfaces options.choices for select fields, and shows primary-field names.
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=10" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
Airtable formulas must be URL-encoded. Let Python stdlib do it — never hand-encode:
FORMULA="{Status}='Todo'"
ENC=$(python3 -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" | python3 -m json.tool
Useful formula patterns:
{Email}='user@example.com'FIND('bug', LOWER({Title}))AND({Status}='Todo', {Priority}='High')OR({Owner}='alice', {Owner}='bob')NOT({Assignee}='')IS_AFTER({Due}, TODAY())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" | python3 -m json.tool
Square brackets in query params MUST be URL-encoded (%5B / %5D).
curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?view=Grid%20view&maxRecords=50" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
Views apply their saved filter + sort server-side.
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"}}' | python3 -m json.tool
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"}}
]
}' | python3 -m json.tool
Batch endpoints are capped at 10 records per request. For larger inserts, loop in batches of 10 with a short sleep to respect 5 req/sec/base.
curl -s -X PATCH "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fields":{"Status":"Done"}}' | python3 -m json.tool
curl -s -X PATCH "https://api.airtable.com/v0/$BASE_ID/$TABLE" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"performUpsert": {"fieldsToMergeOn": ["Email"]},
"records": [
{"fields": {"Email": "user@example.com", "Status": "Active"}}
]
}' | python3 -m json.tool
performUpsert creates records whose merge-field values are new, patches records whose merge-field values already exist. Great for idempotent syncs.
curl -s -X DELETE "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
curl -s -X DELETE "https://api.airtable.com/v0/$BASE_ID/$TABLE?records%5B%5D=rec1&records%5B%5D=rec2" \
-H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
List endpoints return at most 100 records per page. If the response includes "offset": "...", pass it back on the next call. Loop until the field is absent:
OFFSET=""
while :; do
URL="https://api.airtable.com/v0/$BASE_ID/$TABLE?pageSize=100"
[ -n "$OFFSET" ] && URL="$URL&offset=$OFFSET"
RESP=$(curl -s "$URL" -H "Authorization: Bearer $AIRTABLE_API_KEY")
echo "$RESP" | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(r["id"], r["fields"].get("Name","")) for r in d["records"]]'
OFFSET=$(echo "$RESP" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("offset",""))')
[ -z "$OFFSET" ] && break
done
curl -s -o /dev/null -w "%{http_code}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLE_API_KEY" — expect 200.app... ID directly if the token lacks schema.bases:read.GET /v0/meta/bases/$BASE_ID/tables — cache the exact field names and primary-field name locally in the session before mutating anything.filterByFormula first to resolve the rec... ID, then PATCH /v0/$BASE_ID/$TABLE/$RECORD_ID. Never guess record IDs.filterByFormula MUST be URL-encoded. Field names with spaces or non-ASCII also need encoding ({My Field} → %7BMy%20Field%7D). Use Python stdlib (pattern above) — never hand-escape."Assignee" key doesn't mean the field doesn't exist — it means this record's value is empty. Check the schema (step 3) before concluding a field is missing.PATCH merges supplied fields into the record. PUT replaces the record entirely and clears any field you didn't include. Default to PATCH."Status": "Shipping" when Shipping isn't in the field's option list errors with INVALID_MULTIPLE_CHOICE_OPTIONS unless you pass "typecast": true (which auto-creates the option).403 on one base while another works means the token's Access list doesn't include that base — not a scope or auth issue. Send the user to https://airtable.com/create/tokens to grant it.baseA and 5 req/sec on baseB is fine; 6 req/sec on baseA alone will throttle. Monitor the Retry-After header on 429.terminal tool with curl. Do NOT use web_extract (it can't send auth headers) or browser_navigate (needs UI auth and is slow).AIRTABLE_API_KEY flows from ${HERMES_HOME:-~/.hermes}/.env into the subprocess automatically when this skill is loaded — no need to re-export it before each curl call.{Status} is literal. In a shell argument, {Status} is safe outside {...} brace-expansion context — but pass dynamic strings through python3 urllib.parse.quote before splicing into a URL.python3 -m json.tool (always present) rather than jq (optional). Only reach for jq when you need filtering/projection.offset until the field is absent.errors array on non-2xx responses — Airtable returns structured error codes like AUTHENTICATION_REQUIRED, INVALID_PERMISSIONS, MODEL_ID_NOT_FOUND, INVALID_MULTIPLE_CHOICE_OPTIONS that tell you exactly what's wrong.