Source profileQuality 83/100Review permissions

NousResearch/hermes-agent/skills/productivity/airtable/SKILL.md

airtable

Airtable REST API via curl. Records CRUD, filters, upserts.

Source repository stars
225,255
Declared platforms
0
Static risk flags
2
Last source update
2026-08-04
Source checked
2026-08-04

Decision brief

What it does—and where it fits

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.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/NousResearch/hermes-agent --skill "skills/productivity/airtable"
    Safe inspection promptEditorial

    Inspect the Agent Skill "airtable" from https://github.com/NousResearch/hermes-agent/blob/f5be9236e00ddf2f2a412697f267078fc4ee068e/skills/productivity/airtable/SKILL.md at commit f5be9236e00ddf2f2a412697f267078fc4ee068e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Typical Hermes Workflow

      1. Confirm auth. curl -s -o /dev/null -w "%{httpcode}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLEAPIKEY" — expect 200. 2. Find the base. List bases (step above) OR ask the user for the app... ID directly if the token lacks schema.bases:read. 3.…

      Confirm auth. curl -s -o /dev/null -w "%{httpcode}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLEAPIKEY" — expect 200.Find the base. List bases (step above) OR ask the user for the app... ID directly if the token lacks schema.bases:read.Inspect the schema. GET /v0/meta/bases/$BASEID/tables — cache the exact field names and primary-field name locally in the session before mutating anything.
    2. 02

      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. Importan…

      Create a Personal Access Token (PAT) at https://airtable.com/create/tokens (tokens start with pat...).Grant these scopes (minimum):data.records:read — read rows
    3. 03

      API Basics

      -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.

      Endpoint: https://api.airtable.com/v0Auth header: Authorization: Bearer $AIRTABLEAPIKEYAll requests use JSON (Content-Type: application/json for any POST/PATCH/PUT body).
    4. 04

      Field Types (request body shapes)

      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).

      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).
    5. 05

      Common Queries

      Use this BEFORE mutating — confirms exact field names and IDs, surfaces options.choices for select fields, and shows primary-field names.

      Exact match: {Email}='[email protected]'Contains: FIND('bug', LOWER({Title}))Multiple conditions: AND({Status}='Todo', {Priority}='High')

    Permission review

    Static risk signals and limitations

    Network access

    medium · line 31

    The documentation includes network, browsing, or remote request actions.

    curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=5" \

    Network access

    medium · line 60

    The documentation includes network, browsing, or remote request actions.

    curl -s "https://api.airtable.com/v0/meta/bases" \

    Sends data out

    high · line 118

    The documentation includes sending, uploading, or posting data to a remote service.

    curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \

    Sends data out

    high · line 126

    The documentation includes sending, uploading, or posting data to a remote service.

    curl -s -X POST "https://api.airtable.com/v0/$BASE_ID/$TABLE" \

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score83/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars225,255SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    NousResearch/hermes-agent
    Skill path
    skills/productivity/airtable/SKILL.md
    Commit
    f5be9236e00ddf2f2a412697f267078fc4ee068e
    License
    MIT
    Collected
    2026-08-04
    Default branch
    main
    View the original SKILL.md

    Airtable — 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" | 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 Types (request body shapes)

    Field typeWrite 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" | python3 -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" | 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.

    List records (first 10)

    curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=10" \
      -H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -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" | python3 -m json.tool
    

    Filter records (filterByFormula)

    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:

    • Exact match: {Email}='[email protected]'
    • 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" | python3 -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" | python3 -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"}}' | python3 -m json.tool
    

    Create 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"}}
        ]
      }' | 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.

    Update a record (PATCH — merges, preserves unchanged fields)

    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
    

    Upsert by a merge field (no ID needed)

    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": "[email protected]", "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.

    Delete a record

    curl -s -X DELETE "https://api.airtable.com/v0/$BASE_ID/$TABLE/$RECORD_ID" \
      -H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool
    

    Delete up to 10 records in one call

    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
    

    Pagination

    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
    

    Typical Hermes Workflow

    1. Confirm auth. curl -s -o /dev/null -w "%{http_code}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLE_API_KEY" — expect 200.
    2. Find the base. List bases (step above) OR ask the user for the app... ID directly if the token lacks schema.bases:read.
    3. Inspect the schema. GET /v0/meta/bases/$BASE_ID/tables — cache the exact field names and primary-field name locally in the session before mutating anything.
    4. Read before you write. For "update X where Y", filterByFormula first to resolve the rec... ID, then PATCH /v0/$BASE_ID/$TABLE/$RECORD_ID. Never guess record IDs.
    5. Batch writes. Combine related creates into one 10-record POST to stay under the 5 req/sec budget.
    6. Destructive ops. Deletions can't be undone via API. If the user says "delete all Xs", echo back the filter + record count and confirm before firing.

    Pitfalls

    • 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.
    • Empty fields are omitted from responses. A missing "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 vs PUT. PATCH merges supplied fields into the record. PUT replaces the record entirely and clears any field you didn't include. Default to PATCH.
    • Single-select options must exist. Writing "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).
    • Per-base token scoping. A 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.
    • Rate limits are per base, not per token. 5 req/sec on baseA and 5 req/sec on baseB is fine; 6 req/sec on baseA alone will throttle. Monitor the Retry-After header on 429.

    Important Notes for Hermes

    • Always use the 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.
    • Escape curly braces in formulas carefully. In a heredoc body, {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.
    • Pretty-print with python3 -m json.tool (always present) rather than jq (optional). Only reach for jq when you need filtering/projection.
    • Pagination is per-page, not global. Airtable's 100-record cap is a hard limit; there is no way to bump it. Loop with offset until the field is absent.
    • Read the 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.

    Alternatives

    Compare before choosing

    Computed 10042,968

    coreyhaines31/marketingskills

    ab-testing

    When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program

    Computed 10023,781

    alirezarezvani/claude-skills

    app-store-optimization

    App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist

    Computed 1004,922

    dotnet/skills

    migrate-vstest-to-mtp

    Migrates .NET test projects from VSTest to Microsoft.Testing.Platform (MTP). Use when user asks to "migrate to MTP", "switch from VSTest", "enable Microsoft.Testing.Platform", "use MTP runner", set OutputType=Exe only for test projects in Directory.Build.props, or mentions EnableMSTestRunner, EnableNUnitRunner, or UseMicrosoftTestingPlatformRunner. USE FOR: MTP behavioral differences vs VSTest (exit code 8, zero tests discovered, --ignore-exit-code, TESTINGPLATFORM_EXITCODE_IGNORE); centralizing

    Computed 100165

    JasonColapietro/suede-creator-skills

    suede-ab-testing

    Suede-owned experimentation discipline for hypotheses, sample sizing, test duration, significance, and repeatable experiment programs. Use when comparing variants, deciding whether a result is reliable, or building an experiment backlog and cadence. NOT FOR: analytics instrumentation (use suede-analytics), post-click conversion diagnosis (use suede-site-alchemy), or writing the variant copy itself (use suede-copy).