Airtable

REST API Airtable через curl. CRUD-операции над записями, фильтры, upsert.

Метаданные навыка

ИсточникВстроенный (установлен по умолчанию)
Путьskills/productivity/airtable
Версия1.1.0
Авторcommunity
ЛицензияMIT
Платформыlinux, macos, windows
ТегиAirtable, Productivity, Database, API

Справочник: полный SKILL.md

инфо

Ниже приведено полное определение навыка, которое Hermes загружает при срабатывании этого навыка. Именно это агент видит как инструкции, пока навык активен.

Airtable — базы, таблицы и записи

Работа с REST API Airtable напрямую через curl с помощью инструмента terminal. Без MCP-сервера, без OAuth-потока, без Python SDK — только curl и персональный токен доступа.

Требования

  1. Создайте персональный токен доступа (PAT) на https://airtable.com/create/tokens (токены начинаются с pat...).
  2. Выдайте как минимум такие права (scopes):
    • data.records:read — чтение строк
    • data.records:write — создание / обновление / удаление строк
    • schema.bases:read — получение списка баз и таблиц
  3. Важно: в том же интерфейсе токена добавьте каждую нужную базу в список Access токена. PAT привязаны к конкретным базам — валидный токен на чужой базе вернёт 403.
  4. Сохраните токен в ~/.hermes/.env (или через hermes setup):
    AIRTABLE_API_KEY=pat_your_token_here

Примечание: старые API-ключи вида key... отключены в феврале 2024 года. Сейчас работают только PAT и токены OAuth.

Основы API

  • Эндпоинт: https://api.airtable.com/v0
  • Заголовок авторизации: Authorization: Bearer $AIRTABLE_API_KEY
  • Все запросы используют JSON (Content-Type: application/json для любого тела POST/PATCH/PUT).
  • Идентификаторы объектов: базы app..., таблицы tbl..., записи rec..., поля fld.... ID никогда не меняются, а имена — могут. В автоматизациях лучше опираться на ID.
  • Лимит запросов: 5 запросов/сек на базу. При 429 — снижайте темп. Всплеск нагрузки на одну базу будет ограничен.

Базовый шаблон curl:

curl -s "https://api.airtable.com/v0/$BASE_ID/$TABLE?maxRecords=5" \
  -H "Authorization: Bearer $AIRTABLE_API_KEY" | python3 -m json.tool

-s убирает индикатор прогресса curl — держите этот флаг для каждого вызова, чтобы вывод инструмента оставался чистым для Hermes. Прогоняйте ответ через python3 -m json.tool (есть всегда) или jq (если установлен) — для читаемого JSON.

Типы полей (форма тела запроса)

Тип поляФорма для записи
Single line text"Name": "hello"
Long text"Notes": "multi\nline"
Number"Score": 42
Checkbox"Done": true
Single select"Status": "Todo" (имя должно уже существовать, если не задан 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 скачивает и перехостит)
Linked record"Owner": ["recXXXXXXXXXXXXXX"] (массив ID записей)
User"AssignedTo": {"id": "usrXXXXXXXXXXXXXX"}

Передайте "typecast": true на верхнем уровне тела create/update, чтобы Airtable автоматически приводил значения (например, создавал новый вариант select на лету или превращал "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

Делайте это ПЕРЕД изменениями — так подтверждаются точные имена и ID полей, отображаются options.choices для select-полей и выясняются имена первичных полей.

Список записей (первые 10)

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

Фильтрация записей (filterByFormula)

Формулы Airtable нужно кодировать в URL. Поручите это стандартной библиотеке Python — никогда не кодируйте вручную:

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

Полезные шаблоны формул:

  • Точное совпадение: {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

Квадратные скобки в параметрах запроса ОБЯЗАТЕЛЬНО кодировать в URL (%5B / %5D).

Использование именованного представления (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

Представления применяют свои сохранённые фильтр и сортировку на стороне сервера.

Частые изменения

Создать запись

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

Создать до 10 записей за один вызов

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

Пакетные эндпоинты ограничены 10 записями на запрос. Для больших вставок запускайте цикл пачками по 10 с короткой паузой, чтобы уложиться в 5 запросов/сек на базу.

Обновить запись (PATCH — сливает данные, сохраняет нетронутые поля)

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 по полю слияния (ID не нужен)

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 создаёт записи с новыми значениями поля слияния и патчит те, чьи значения этого поля уже есть. Удобно для идемпотентных синхронизаций.

Удалить запись

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

Удалить до 10 записей за один вызов

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

Пагинация

Эндпоинты со списками возвращают максимум 100 записей на страницу. Если в ответе есть "offset": "...", передайте его обратно в следующем вызове. Крутите цикл, пока поле не исчезнет:

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

Типичный рабочий процесс Hermes

  1. Проверьте авторизацию. curl -s -o /dev/null -w "%{http_code}\n" https://api.airtable.com/v0/meta/bases -H "Authorization: Bearer $AIRTABLE_API_KEY" — ожидаем 200.
  2. Найдите базу. Получите список баз (шаг выше) ИЛИ спросите у пользователя ID вида app... напрямую, если у токена нет schema.bases:read.
  3. Изучите схему. GET /v0/meta/bases/$BASE_ID/tables — закешируйте точные имена полей и имя первичного поля локально в сессии, прежде чем что-либо менять.
  4. Сначала читайте, потом пишите. Для «обнови X, где Y» сперва filterByFormula, чтобы определить ID вида rec..., затем PATCH /v0/$BASE_ID/$TABLE/$RECORD_ID. Никогда не угадывайте ID записей.
  5. Пакетная запись. Объединяйте связанные создания в один POST на 10 записей, чтобы держаться в бюджете 5 запросов/сек.
  6. Деструктивные операции. Удаления через API не отменить. Если пользователь говорит «удали все X», выведите фильтр и число найденных записей и дождитесь подтверждения перед выполнением.

Подводные камни

  • filterByFormula ОБЯЗАТЕЛЬНО кодировать в URL. Имена полей с пробелами или не-ASCII тоже нуждаются в кодировании ({My Field}%7BMy%20Field%7D). Используйте стандартную библиотеку Python (шаблон выше) — никогда не экранируйте вручную.
  • Пустые поля выпадают из ответов. Отсутствие ключа "Assignee" не значит, что поля нет, — значит, у этой записи значение пустое. Сверьтесь со схемой (шаг 3), прежде чем делать вывод об отсутствии поля.
  • PATCH против PUT. PATCH сливает переданные поля в запись. PUT заменяет запись целиком и обнуляет любое поле, которое вы не включили. По умолчанию берите PATCH.
  • Варианты single-select должны существовать. Запись "Status": "Shipping", когда Shipping нет в списке вариантов поля, вызовет ошибку INVALID_MULTIPLE_CHOICE_OPTIONS, если не передать "typecast": true (он создаст вариант автоматически).
  • Токен привязан к конкретной базе. 403 на одной базе при рабочей другой означает, что в списке Access токена нет этой базы — а не проблему со scope или авторизацией. Отправьте пользователя на https://airtable.com/create/tokens выдать доступ.
  • Лимиты считаются по базе, а не по токену. 5 запросов/сек на baseA и 5 запросов/сек на baseB — нормально; 6 запросов/сек на одной baseA уже упрётся в троттлинг. Следите за заголовком Retry-After при 429.

Важные замечания для Hermes

  • Всегда используйте инструмент terminal с curl. НЕ применяйте web_extract (он не умеет слать заголовки авторизации) и browser_navigate (требует UI-авторизации и работает медленно).
  • AIRTABLE_API_KEY подтягивается из ~/.hermes/.env в подпроцесс автоматически при загрузке навыка — переэкспортировать его перед каждым вызовом curl не нужно.
  • Аккуратно экранируйте фигурные скобки в формулах. В теле heredoc {Status} берётся буквально. В аргументе shell {Status} безопасен вне контекста brace-expansion {...} — но динамические строки прогоняйте через python3 urllib.parse.quote, прежде чем вставлять в URL.
  • Форматируйте вывод через python3 -m json.tool (есть всегда), а не jq (опционально). Тянитесь к jq только когда нужна фильтрация или выборка полей.
  • Пагинация постраничная, не глобальная. Лимит в 100 записей у Airtable жёсткий, поднять его нельзя. Крутите цикл с offset, пока поле не исчезнет.
  • Читайте массив errors в ответах не-2xx — Airtable возвращает структурированные коды ошибок вроде AUTHENTICATION_REQUIRED, INVALID_PERMISSIONS, MODEL_ID_NOT_FOUND, INVALID_MULTIPLE_CHOICE_OPTIONS, которые точно говорят, что не так.
ESC