# @kody/airtable — agent notes Human setup and intent live in [`README.md`](./README.md). This file is for agents: imports, smoke/dryRun execute snippets, and edge cases. Auth is the saved OAuth integration **`airtable`** / **`airtable-*`** or user secret **`airtablePat`** — never paste tokens. Do not disable live webhooks or jobs. ## Auth | Lane | Credential | Notes | | --- | --- | --- | | OAuth (recommended) | Integration `airtable` or `airtable-` | BYO OAuth; reconnect `https://kody.codes/connect/oauth?provider=airtable` | | PAT | User secret `airtablePat` (or `airtablePat-`) | Fast single-workspace lane | | Call | Resolves to | | --- | --- | | omit account | `airtable` / `airtablePat` | | `account: 'work'` | `airtable-work` / `airtablePat-work` | | `integrationName: 'airtable-work'` | that exact OAuth name | | `secretName: 'airtablePat-work'` | that exact PAT secret | | `auth: 'oauth' \| 'pat'` | force a lane when both exist | Required host: `api.airtable.com`. REST base: `https://api.airtable.com/v0`. ## Import paths | Export | Import | | --- | --- | | overview | `kody:@kody/airtable` | | accounts | `kody:@kody/airtable/accounts` | | smoke-test | `kody:@kody/airtable/smoke-test` | | viewer | `kody:@kody/airtable/viewer` | | list-bases | `kody:@kody/airtable/list-bases` | | get-base | `kody:@kody/airtable/get-base` | | list-tables | `kody:@kody/airtable/list-tables` | | get-table | `kody:@kody/airtable/get-table` | | list-records | `kody:@kody/airtable/list-records` | | get-record | `kody:@kody/airtable/get-record` | | create-record | `kody:@kody/airtable/create-record` | | update-record | `kody:@kody/airtable/update-record` | | delete-record | `kody:@kody/airtable/delete-record` | | list-comments | `kody:@kody/airtable/list-comments` | | create-comment | `kody:@kody/airtable/create-comment` | | request | `kody:@kody/airtable/request` | | types | `kody:@kody/airtable/types` | Prefer static `kody:@kody/airtable/...` imports from `execute`. Do not lead with `packages.invoke`. ## Smoke test (read-only) ```js import smokeTest from 'kody:@kody/airtable/smoke-test' export default async function main() { return await smokeTest() // without creds => { ok: true, live: false, ...setup URLs } // with OAuth/PAT => { ok: true, live: true, hasViewerId } — no email } ``` Read-only samples: ```js import listBases from 'kody:@kody/airtable/list-bases' import listTables from 'kody:@kody/airtable/list-tables' export default async function main() { const { items: bases } = await listBases() return await listTables({ baseId: bases[0]?.id ?? '', }) } ``` ```js import request from 'kody:@kody/airtable/request' export default async function main() { return await request({ path: '/meta/whoami', }) } ``` ## dryRun / confirm (writes) `create-record`, `update-record`, `delete-record`, `create-comment`, and mutating `./request` methods (POST / PUT / PATCH / DELETE) require `confirm: true`. Pass `dryRun: true` to inspect the REST payload without calling Airtable. GET via `./request` is read-only. ```js import createRecord from 'kody:@kody/airtable/create-record' export default async function main() { const preview = await createRecord({ baseId: 'appXXXXXXXXXXXXXX', tableIdOrName: 'tblXXXXXXXXXXXXXX', fields: { Name: 'Investigate checkout timeout' }, dryRun: true, }) // Only after explicit user confirmation of base, table, and fields: return await createRecord({ baseId: 'appXXXXXXXXXXXXXX', tableIdOrName: 'tblXXXXXXXXXXXXXX', fields: { Name: 'Investigate checkout timeout' }, confirm: true, }) } ``` ## Edge cases / fork notes - Prefer table ids (`tbl…`) over names when a name can collide or contain spaces. - Helpers project slim objects (no collaborator emails). Use `./request` for extra fields. - If Airtable returns 401/403 or an insufficient-scope error, helpers throw a message that names the missing scope and the next setup URL. - Do not hard-code a personal base id, table name, or alias. - No package-owned jobs are enabled. - Never paste Airtable OAuth client secrets or PAT values into chat or logs.