[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7D7TE0JSWJM4WXDVRGYTHHB":3,"skill-same:01M7D7TE0JSWJM4WXDVRGYTHHB":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":14,"repositoryHtmlUrl":46,"repositoryHomepageUrl":6,"ownerHtmlUrl":47,"ownerBlogUrl":48,"ownerDescription":49,"updatedAt":50,"install":51,"resources":64},"01M7D7TE0JSWJM4WXDVRGYTHHB","airtable",null,"Airtable REST API via curl. Records CRUD, filters, upserts.","GrupoANDevelopment-m\u002Falberto-ai","GrupoANDevelopment-m","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F257455179?v=4","01M7D69X2N2KEVN0SJF6WGM6SH","Antônio  Ndombe","01M7D69X3P5JTT8H8S904N3888",0,"MIT","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fairtable\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: airtable\ndescription: Airtable REST API via curl. Records CRUD, filters, upserts.\nversion: 1.1.0\nauthor: community\nlicense: MIT\nplatforms: [linux, macos, windows]\nprerequisites:\n  env_vars: [AIRTABLE_API_KEY]\n  commands: [curl]\nmetadata:\n  hermes:\n    tags: [Airtable, Productivity, Database, API]\n    homepage: https:\u002F\u002Fairtable.com\u002Fdevelopers\u002Fweb\u002Fapi\u002Fintroduction\n---\n\n# Airtable — Bases, Tables & Records\n\nWork 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.\n\n## Prerequisites\n\n1. Create a **Personal Access Token (PAT)** at https:\u002F\u002Fairtable.com\u002Fcreate\u002Ftokens (tokens start with `pat...`).\n2. Grant these scopes (minimum):\n   - `data.records:read` — read rows\n   - `data.records:write` — create \u002F update \u002F delete rows\n   - `schema.bases:read` — list bases and tables\n3. **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`.\n4. Store the token in `${HERMES_HOME:-~\u002F.hermes}\u002F.env` (or via `hermes setup`):\n   ```\n   AIRTABLE_API_KEY=pat_your_token_here\n   ```\n\n> Note: legacy `key...` API keys were deprecated Feb 2024. Only PATs and OAuth tokens work now.\n\n## API Basics\n\n- **Endpoint:** `https:\u002F\u002Fapi.airtable.com\u002Fv0`\n- **Auth header:** `Authorization: Bearer $AIRTABLE_API_KEY`\n- **All requests** use JSON (`Content-Type: application\u002Fjson` for any POST\u002FPATCH\u002FPUT body).\n- **Object IDs:** bases `app...`, tables `tbl...`, records `rec...`, fields `fld...`. IDs never change; names can. Prefer IDs in automations.\n- **Rate limit:** 5 requests\u002Fsec\u002Fbase. `429` → back off. Burst on a single base will be throttled.\n\nBase curl pattern:\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?maxRecords=5\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n`-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.\n\n## Field Types (request body shapes)\n\n| Field type | Write shape |\n|---|---|\n| Single line text | `\"Name\": \"hello\"` |\n| Long text | `\"Notes\": \"multi\\nline\"` |\n| Number | `\"Score\": 42` |\n| Checkbox | `\"Done\": true` |\n| Single select | `\"Status\": \"Todo\"` (name must already exist unless `typecast: true`) |\n| Multi-select | `\"Tags\": [\"urgent\", \"bug\"]` |\n| Date | `\"Due\": \"2026-04-01\"` |\n| DateTime (UTC) | `\"At\": \"2026-04-01T14:30:00.000Z\"` |\n| URL \u002F Email \u002F Phone | `\"Link\": \"https:\u002F\u002F…\"` |\n| Attachment | `\"Files\": [{\"url\": \"https:\u002F\u002F…\"}]` (Airtable fetches + rehosts) |\n| Linked record | `\"Owner\": [\"recXXXXXXXXXXXXXX\"]` (array of record IDs) |\n| User | `\"AssignedTo\": {\"id\": \"usrXXXXXXXXXXXXXX\"}` |\n\nPass `\"typecast\": true` at the top level of a create\u002Fupdate body to let Airtable auto-coerce values (e.g. create a new select option on the fly, convert `\"42\"` → `42`).\n\n## Common Queries\n\n### List bases the token can see\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002Fmeta\u002Fbases\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### List tables + schema for a base\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002Fmeta\u002Fbases\u002F$BASE_ID\u002Ftables\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\nUse this BEFORE mutating — confirms exact field names and IDs, surfaces `options.choices` for select fields, and shows primary-field names.\n\n### List records (first 10)\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?maxRecords=10\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### Get a single record\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### Filter records (filterByFormula)\nAirtable formulas must be URL-encoded. Let Python stdlib do it — never hand-encode:\n```bash\nFORMULA=\"{Status}='Todo'\"\nENC=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$FORMULA\")\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?filterByFormula=$ENC&maxRecords=20\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\nUseful formula patterns:\n- Exact match: `{Email}='user@example.com'`\n- Contains: `FIND('bug', LOWER({Title}))`\n- Multiple conditions: `AND({Status}='Todo', {Priority}='High')`\n- Or: `OR({Owner}='alice', {Owner}='bob')`\n- Not empty: `NOT({Assignee}='')`\n- Date comparison: `IS_AFTER({Due}, TODAY())`\n\n### Sort + select specific fields\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?sort%5B0%5D%5Bfield%5D=Priority&sort%5B0%5D%5Bdirection%5D=asc&fields%5B%5D=Name&fields%5B%5D=Status\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\nSquare brackets in query params MUST be URL-encoded (`%5B` \u002F `%5D`).\n\n### Use a named view\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?view=Grid%20view&maxRecords=50\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\nViews apply their saved filter + sort server-side.\n\n## Common Mutations\n\n### Create a record\n```bash\ncurl -s -X POST \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"fields\":{\"Name\":\"New task\",\"Status\":\"Todo\",\"Priority\":\"High\"}}' | python3 -m json.tool\n```\n\n### Create up to 10 records in one call\n```bash\ncurl -s -X POST \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"typecast\": true,\n    \"records\": [\n      {\"fields\": {\"Name\": \"Task A\", \"Status\": \"Todo\"}},\n      {\"fields\": {\"Name\": \"Task B\", \"Status\": \"In progress\"}}\n    ]\n  }' | python3 -m json.tool\n```\nBatch endpoints are capped at **10 records per request**. For larger inserts, loop in batches of 10 with a short sleep to respect 5 req\u002Fsec\u002Fbase.\n\n### Update a record (PATCH — merges, preserves unchanged fields)\n```bash\ncurl -s -X PATCH \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"fields\":{\"Status\":\"Done\"}}' | python3 -m json.tool\n```\n\n### Upsert by a merge field (no ID needed)\n```bash\ncurl -s -X PATCH \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"performUpsert\": {\"fieldsToMergeOn\": [\"Email\"]},\n    \"records\": [\n      {\"fields\": {\"Email\": \"user@example.com\", \"Status\": \"Active\"}}\n    ]\n  }' | python3 -m json.tool\n```\n`performUpsert` creates records whose merge-field values are new, patches records whose merge-field values already exist. Great for idempotent syncs.\n\n### Delete a record\n```bash\ncurl -s -X DELETE \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### Delete up to 10 records in one call\n```bash\ncurl -s -X DELETE \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?records%5B%5D=rec1&records%5B%5D=rec2\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n## Pagination\n\nList 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:\n\n```bash\nOFFSET=\"\"\nwhile :; do\n  URL=\"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?pageSize=100\"\n  [ -n \"$OFFSET\" ] && URL=\"$URL&offset=$OFFSET\"\n  RESP=$(curl -s \"$URL\" -H \"Authorization: Bearer $AIRTABLE_API_KEY\")\n  echo \"$RESP\" | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(r[\"id\"], r[\"fields\"].get(\"Name\",\"\")) for r in d[\"records\"]]'\n  OFFSET=$(echo \"$RESP\" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get(\"offset\",\"\"))')\n  [ -z \"$OFFSET\" ] && break\ndone\n```\n\n## Typical Hermes Workflow\n\n1. **Confirm auth.** `curl -s -o \u002Fdev\u002Fnull -w \"%{http_code}\\n\" https:\u002F\u002Fapi.airtable.com\u002Fv0\u002Fmeta\u002Fbases -H \"Authorization: Bearer $AIRTABLE_API_KEY\"` — expect `200`.\n2. **Find the base.** List bases (step above) OR ask the user for the `app...` ID directly if the token lacks `schema.bases:read`.\n3. **Inspect the schema.** `GET \u002Fv0\u002Fmeta\u002Fbases\u002F$BASE_ID\u002Ftables` — cache the exact field names and primary-field name locally in the session before mutating anything.\n4. **Read before you write.** For \"update X where Y\", `filterByFormula` first to resolve the `rec...` ID, then `PATCH \u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID`. Never guess record IDs.\n5. **Batch writes.** Combine related creates into one 10-record POST to stay under the 5 req\u002Fsec budget.\n6. **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.\n\n## Pitfalls\n\n- **`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.\n- **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.\n- **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`.\n- **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).\n- **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:\u002F\u002Fairtable.com\u002Fcreate\u002Ftokens to grant it.\n- **Rate limits are per base, not per token.** 5 req\u002Fsec on `baseA` and 5 req\u002Fsec on `baseB` is fine; 6 req\u002Fsec on `baseA` alone will throttle. Monitor the `Retry-After` header on `429`.\n\n## Important Notes for Hermes\n\n- **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).\n- **`AIRTABLE_API_KEY` flows from `${HERMES_HOME:-~\u002F.hermes}\u002F.env` into the subprocess automatically** when this skill is loaded — no need to re-export it before each `curl` call.\n- **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.\n- **Pretty-print with `python3 -m json.tool`** (always present) rather than `jq` (optional). Only reach for `jq` when you need filtering\u002Fprojection.\n- **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.\n- **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.\n","# Airtable — Bases, Tables & Records\n\nWork 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.\n\n## Prerequisites\n\n1. Create a **Personal Access Token (PAT)** at https:\u002F\u002Fairtable.com\u002Fcreate\u002Ftokens (tokens start with `pat...`).\n2. Grant these scopes (minimum):\n   - `data.records:read` — read rows\n   - `data.records:write` — create \u002F update \u002F delete rows\n   - `schema.bases:read` — list bases and tables\n3. **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`.\n4. Store the token in `${HERMES_HOME:-~\u002F.hermes}\u002F.env` (or via `hermes setup`):\n   ```\n   AIRTABLE_API_KEY=pat_your_token_here\n   ```\n\n> Note: legacy `key...` API keys were deprecated Feb 2024. Only PATs and OAuth tokens work now.\n\n## API Basics\n\n- **Endpoint:** `https:\u002F\u002Fapi.airtable.com\u002Fv0`\n- **Auth header:** `Authorization: Bearer $AIRTABLE_API_KEY`\n- **All requests** use JSON (`Content-Type: application\u002Fjson` for any POST\u002FPATCH\u002FPUT body).\n- **Object IDs:** bases `app...`, tables `tbl...`, records `rec...`, fields `fld...`. IDs never change; names can. Prefer IDs in automations.\n- **Rate limit:** 5 requests\u002Fsec\u002Fbase. `429` → back off. Burst on a single base will be throttled.\n\nBase curl pattern:\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?maxRecords=5\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n`-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.\n\n## Field Types (request body shapes)\n\n| Field type | Write shape |\n|---|---|\n| Single line text | `\"Name\": \"hello\"` |\n| Long text | `\"Notes\": \"multi\\nline\"` |\n| Number | `\"Score\": 42` |\n| Checkbox | `\"Done\": true` |\n| Single select | `\"Status\": \"Todo\"` (name must already exist unless `typecast: true`) |\n| Multi-select | `\"Tags\": [\"urgent\", \"bug\"]` |\n| Date | `\"Due\": \"2026-04-01\"` |\n| DateTime (UTC) | `\"At\": \"2026-04-01T14:30:00.000Z\"` |\n| URL \u002F Email \u002F Phone | `\"Link\": \"https:\u002F\u002F…\"` |\n| Attachment | `\"Files\": [{\"url\": \"https:\u002F\u002F…\"}]` (Airtable fetches + rehosts) |\n| Linked record | `\"Owner\": [\"recXXXXXXXXXXXXXX\"]` (array of record IDs) |\n| User | `\"AssignedTo\": {\"id\": \"usrXXXXXXXXXXXXXX\"}` |\n\nPass `\"typecast\": true` at the top level of a create\u002Fupdate body to let Airtable auto-coerce values (e.g. create a new select option on the fly, convert `\"42\"` → `42`).\n\n## Common Queries\n\n### List bases the token can see\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002Fmeta\u002Fbases\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### List tables + schema for a base\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002Fmeta\u002Fbases\u002F$BASE_ID\u002Ftables\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\nUse this BEFORE mutating — confirms exact field names and IDs, surfaces `options.choices` for select fields, and shows primary-field names.\n\n### List records (first 10)\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?maxRecords=10\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### Get a single record\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### Filter records (filterByFormula)\nAirtable formulas must be URL-encoded. Let Python stdlib do it — never hand-encode:\n```bash\nFORMULA=\"{Status}='Todo'\"\nENC=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$FORMULA\")\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?filterByFormula=$ENC&maxRecords=20\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\nUseful formula patterns:\n- Exact match: `{Email}='user@example.com'`\n- Contains: `FIND('bug', LOWER({Title}))`\n- Multiple conditions: `AND({Status}='Todo', {Priority}='High')`\n- Or: `OR({Owner}='alice', {Owner}='bob')`\n- Not empty: `NOT({Assignee}='')`\n- Date comparison: `IS_AFTER({Due}, TODAY())`\n\n### Sort + select specific fields\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?sort%5B0%5D%5Bfield%5D=Priority&sort%5B0%5D%5Bdirection%5D=asc&fields%5B%5D=Name&fields%5B%5D=Status\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\nSquare brackets in query params MUST be URL-encoded (`%5B` \u002F `%5D`).\n\n### Use a named view\n```bash\ncurl -s \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?view=Grid%20view&maxRecords=50\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\nViews apply their saved filter + sort server-side.\n\n## Common Mutations\n\n### Create a record\n```bash\ncurl -s -X POST \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"fields\":{\"Name\":\"New task\",\"Status\":\"Todo\",\"Priority\":\"High\"}}' | python3 -m json.tool\n```\n\n### Create up to 10 records in one call\n```bash\ncurl -s -X POST \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"typecast\": true,\n    \"records\": [\n      {\"fields\": {\"Name\": \"Task A\", \"Status\": \"Todo\"}},\n      {\"fields\": {\"Name\": \"Task B\", \"Status\": \"In progress\"}}\n    ]\n  }' | python3 -m json.tool\n```\nBatch endpoints are capped at **10 records per request**. For larger inserts, loop in batches of 10 with a short sleep to respect 5 req\u002Fsec\u002Fbase.\n\n### Update a record (PATCH — merges, preserves unchanged fields)\n```bash\ncurl -s -X PATCH \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"fields\":{\"Status\":\"Done\"}}' | python3 -m json.tool\n```\n\n### Upsert by a merge field (no ID needed)\n```bash\ncurl -s -X PATCH \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"performUpsert\": {\"fieldsToMergeOn\": [\"Email\"]},\n    \"records\": [\n      {\"fields\": {\"Email\": \"user@example.com\", \"Status\": \"Active\"}}\n    ]\n  }' | python3 -m json.tool\n```\n`performUpsert` creates records whose merge-field values are new, patches records whose merge-field values already exist. Great for idempotent syncs.\n\n### Delete a record\n```bash\ncurl -s -X DELETE \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n### Delete up to 10 records in one call\n```bash\ncurl -s -X DELETE \"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?records%5B%5D=rec1&records%5B%5D=rec2\" \\\n  -H \"Authorization: Bearer $AIRTABLE_API_KEY\" | python3 -m json.tool\n```\n\n## Pagination\n\nList 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:\n\n```bash\nOFFSET=\"\"\nwhile :; do\n  URL=\"https:\u002F\u002Fapi.airtable.com\u002Fv0\u002F$BASE_ID\u002F$TABLE?pageSize=100\"\n  [ -n \"$OFFSET\" ] && URL=\"$URL&offset=$OFFSET\"\n  RESP=$(curl -s \"$URL\" -H \"Authorization: Bearer $AIRTABLE_API_KEY\")\n  echo \"$RESP\" | python3 -c 'import json,sys; d=json.load(sys.stdin); [print(r[\"id\"], r[\"fields\"].get(\"Name\",\"\")) for r in d[\"records\"]]'\n  OFFSET=$(echo \"$RESP\" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get(\"offset\",\"\"))')\n  [ -z \"$OFFSET\" ] && break\ndone\n```\n\n## Typical Hermes Workflow\n\n1. **Confirm auth.** `curl -s -o \u002Fdev\u002Fnull -w \"%{http_code}\\n\" https:\u002F\u002Fapi.airtable.com\u002Fv0\u002Fmeta\u002Fbases -H \"Authorization: Bearer $AIRTABLE_API_KEY\"` — expect `200`.\n2. **Find the base.** List bases (step above) OR ask the user for the `app...` ID directly if the token lacks `schema.bases:read`.\n3. **Inspect the schema.** `GET \u002Fv0\u002Fmeta\u002Fbases\u002F$BASE_ID\u002Ftables` — cache the exact field names and primary-field name locally in the session before mutating anything.\n4. **Read before you write.** For \"update X where Y\", `filterByFormula` first to resolve the `rec...` ID, then `PATCH \u002Fv0\u002F$BASE_ID\u002F$TABLE\u002F$RECORD_ID`. Never guess record IDs.\n5. **Batch writes.** Combine related creates into one 10-record POST to stay under the 5 req\u002Fsec budget.\n6. **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.\n\n## Pitfalls\n\n- **`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.\n- **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.\n- **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`.\n- **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).\n- **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:\u002F\u002Fairtable.com\u002Fcreate\u002Ftokens to grant it.\n- **Rate limits are per base, not per token.** 5 req\u002Fsec on `baseA` and 5 req\u002Fsec on `baseB` is fine; 6 req\u002Fsec on `baseA` alone will throttle. Monitor the `Retry-After` header on `429`.\n\n## Important Notes for Hermes\n\n- **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).\n- **`AIRTABLE_API_KEY` flows from `${HERMES_HOME:-~\u002F.hermes}\u002F.env` into the subprocess automatically** when this skill is loaded — no need to re-export it before each `curl` call.\n- **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.\n- **Pretty-print with `python3 -m json.tool`** (always present) rather than `jq` (optional). Only reach for `jq` when you need filtering\u002Fprojection.\n- **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.\n- **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.\n",{"name":5,"author":24,"license":15,"version":25,"metadata":26,"platforms":34,"description":7,"prerequisites":38},"community","1.1.0",{"hermes":27},{"tags":28,"homepage":33},[29,30,31,32],"Airtable","Productivity","Database","API","https:\u002F\u002Fairtable.com\u002Fdevelopers\u002Fweb\u002Fapi\u002Fintroduction",[35,36,37],"linux","macos","windows",{"commands":39,"env_vars":41},[40],"curl",[42],"AIRTABLE_API_KEY","main","eac438ea9216a7cb8a570cab803eb01eeafc6793","skills\u002Fproductivity\u002Fairtable","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m","https:\u002F\u002Fid-preview--d5bef06d-6c9e-4563-a3d4-a855b9d61610.lovable.app\u002F","Eu sou Antonio B. B. Ndombe \r\nEu sou um Pesquisador e desenvolvedor de sistema autonomia cognitivos, fundador de sistemas ODCs ( organismos digital conscientes)","2026-11-04 06:53:39",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add GrupoANDevelopment-m\u002Falberto-ai --skill airtable",{"tool":57,"command":58},"bunx","bunx skills add GrupoANDevelopment-m\u002Falberto-ai --skill airtable",{"tool":60,"command":61},"pnpm","pnpm dlx skills add GrupoANDevelopment-m\u002Falberto-ai --skill airtable","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fskills\u002Fproductivity\u002Fairtable\u002FSKILL.md",true,[],[66,74,81,88,95,102],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":70,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":19},"01M7DASC6FSCJP49JZBSK0NY1Z","nemoclaw-user-reference","Describes the NemoClaw integration layer and blueprint architecture and how they orchestrate compatible agent sandboxes. Use when looking up architecture, agent integration, plugin structure, or blueprint design. Trigger keywords - nemoclaw architecture, nemoclaw agent architecture, nemoclaw plugin blueprint structure, nemoclaw vs openshell, which cli, nemoclaw cli, openshell cli, sandbox commands, nemoclaw cli commands, nemoclaw command reference, nemoclaw network policy, sandbox egress control operator approval, nemoclaw troubleshooting, nemoclaw debug sandbox issues.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-reference\u002FSKILL.md",[],[],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":70,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":80,"publishedAt":19},"01M7DAS5S79VK33XFGVAM2TFWF","nemoclaw-user-overview","Explains how OpenClaw, OpenShell, and NemoClaw form the ecosystem, NemoClaw's position in the stack, what NemoClaw adds beyond the community sandbox, and when to prefer NemoClaw versus integrating OpenShell and OpenClaw directly. Use when users ask about the relationship between OpenClaw, OpenShell, and NemoClaw, or when to use NemoClaw versus OpenShell. Trigger keywords - nemoclaw ecosystem, openclaw openshell, nemoclaw vs openshell, sandboxed openclaw, how nemoclaw works, nemoclaw sandbox lifecycle blueprint, nemoclaw overview, openclaw always-on assistants, nvidia openshell, nvidia nemotron, nemoclaw release notes, nemoclaw changelog.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-overview\u002FSKILL.md",[],[],{"skillNo":82,"name":83,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":70,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":19},"01M7DAS0PQEMET5MNGZ1ABSZVS","nemoclaw-user-monitor-sandbox","Inspects sandbox health, traces agent behavior, and diagnoses problems. Use when monitoring a running sandbox, debugging agent issues, or checking sandbox logs. Trigger keywords - monitor nemoclaw sandbox, debug nemoclaw agent issues.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-monitor-sandbox\u002FSKILL.md",[],[],{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":70,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":94,"publishedAt":19},"01M7DARQYGF7E9A69VH6S3CGN9","nemoclaw-user-manage-sandboxes","Explains operational tasks after the quickstart: listing sandboxes, status and health checks, logs, diagnostics, port forwards, multiple sandboxes, credential reset, rebuilds, network presets, upgrades, and uninstall. Trigger keywords - manage nemoclaw sandboxes, nemoclaw status, nemoclaw list, nemoclaw dashboard port, nemoclaw rebuild, nemoclaw upgrade sandboxes, nemoclaw uninstall, sandbox mutability, sandbox runtime configuration, sandbox rebuild, nemoclaw backup, nemoclaw restore, workspace backup, openshell sandbox download upload, nemoclaw messaging channels, nemoclaw telegram, nemoclaw discord, nemoclaw slack, nemoclaw wechat, nemoclaw whatsapp, openshell channel messaging, nemoclaw workspace files, soul.md, user.md, identity.md, agents.md, sandbox persistence.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-manage-sandboxes\u002FSKILL.md",[],[],{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":70,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":19},"01M7DARH8BRTG51TRCAB7YWDJ4","nemoclaw-user-manage-policy","Adds, removes, or modifies allowed endpoints in the sandbox policy. Use when customizing network policy, changing egress rules, or configuring sandbox endpoint access. Trigger keywords - customize nemoclaw network policy, sandbox egress policy configuration, nemoclaw integration policy examples, post-install policy setup, openshell approval workflow, policy preset, nemoclaw approve network requests, sandbox egress approval tui.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-manage-policy\u002FSKILL.md",[],[],{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":70,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":19},"01M7DARAT62M2VR37590VCZ5NS","nemoclaw-user-get-started","Installs NemoClaw, launches a sandbox, and runs the first agent prompt. Use when onboarding, installing, or launching a NemoClaw sandbox for the first time. Trigger keywords - nemoclaw quickstart, install nemoclaw openclaw sandbox, nemohermes quickstart, hermes agent nemoclaw, run hermes openshell sandbox, nemoclaw prerequisites, nemoclaw supported platforms, nemoclaw hardware software, nemoclaw windows wsl2 setup, nemoclaw install windows docker desktop.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-get-started\u002FSKILL.md",[],[]]