> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ancha.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These docs describe the Ancha CLI, npm package @ancha-labs/cli, and the hosted workspace at app.ancha.io. Coding agents should start at https://docs.ancha.io/coding-agents. Run the CLI as `ancha` after `npm install -g @ancha-labs/cli`, or as `npx -y @ancha-labs/cli` without installing. Pass --json for machine-readable output, or set ANCHA_JSON=1. Every command ends with a `next:` line naming the next command. Exit codes: 0 pass, 1 a check failed or a comparison regressed, 2 usage, config or authentication error, 3 infrastructure error, 4 unimplemented endpoint with --strict.

# Airtable

> Test an agent against Airtable's Web API for bases, tables, fields, records, formulas, views and comments.

The Airtable replica implements 21 Web API v0 endpoints. You can query and edit records, follow linked records, evaluate formulas and comment on rows. Its key is `airtable`.

## Connect

| Variable | Value |
| - | - |
| `AIRTABLE_API_URL` | Base URL ending in `/v0`. Append paths such as `/meta/bases`. |
| `AIRTABLE_ENDPOINT_URL` | Base URL without `/v0`, for the `airtable.js` client. |
| `AIRTABLE_API_KEY` | Fake personal access token. Send `Authorization: Bearer <token>`. |

```ts TypeScript theme={null}
const airtable = (path: string, init: RequestInit = {}) => fetch(process.env.AIRTABLE_API_URL + path, { ...init, headers: { Authorization: 'Bearer ' + process.env.AIRTABLE_API_KEY, 'Content-Type': 'application/json' } }).then(r => r.json());
const { bases } = await airtable('/meta/bases');
const crm = bases.find((base: any) => base.name === 'Customer Success');
const { records } = await airtable('/' + crm.id + '/Accounts?filterByFormula=' + encodeURIComponent("{Health} = 'At risk'"));
```

This is the request sequence `ancha connect airtable --lang ts` prints. The `airtable.js` client reads `AIRTABLE_API_KEY` and `AIRTABLE_ENDPOINT_URL` itself. A response with `offset` has more records; repeat the request with that offset.

## Endpoints

| Method | Path | Support |
| - | - | - |
| `GET` | `/v0/meta/whoami` | partial: email with the `user.email:read` scope |
| `GET` | `/v0/meta/bases` | partial |
| `GET` | `/v0/meta/bases/{baseId}/tables` | partial: `include[]=visibleFieldIds` |
| `POST` | `/v0/meta/bases/{baseId}/tables` | partial: the first field is the primary field; a Grid view is added |
| `PATCH` | `/v0/meta/bases/{baseId}/tables/{tableIdOrName}` | partial: name and description |
| `POST` | `/v0/meta/bases/{baseId}/tables/{tableId}/fields` | partial: stored, link (with its inverse), formula, count, lookup and rollup fields; attachments, barcodes, buttons and AI fields are gaps |
| `PATCH` | `/v0/meta/bases/{baseId}/tables/{tableId}/fields/{columnId}` | partial: name and description |
| `GET` | `/v0/{baseId}/{tableIdOrName}` | partial: `view`, `filterByFormula`, `sort`, `fields`, `pageSize`, `maxRecords`, `offset`, `returnFieldsByFieldId`, `recordMetadata`; `cellFormat=string` is a gap |
| `POST` | `/v0/{baseId}/{tableIdOrName}/listRecords` | partial: the same options in a JSON body |
| `GET` | `/v0/{baseId}/{tableIdOrName}/{recordId}` | partial |
| `POST` | `/v0/{baseId}/{tableIdOrName}` | partial: one record (`fields`) or up to 10 (`records`); `typecast` |
| `PATCH` | `/v0/{baseId}/{tableIdOrName}` | partial: up to 10; `performUpsert` with `fieldsToMergeOn` |
| `PUT` | `/v0/{baseId}/{tableIdOrName}` | partial: clears the fields not given |
| `DELETE` | `/v0/{baseId}/{tableIdOrName}` | partial: `records[]`: up to 10 |
| `PATCH` | `/v0/{baseId}/{tableIdOrName}/{recordId}` | partial |
| `PUT` | `/v0/{baseId}/{tableIdOrName}/{recordId}` | partial |
| `DELETE` | `/v0/{baseId}/{tableIdOrName}/{recordId}` | partial |
| `GET` | `/v0/{baseId}/{tableIdOrName}/{recordId}/comments` | partial: newest first |
| `POST` | `/v0/{baseId}/{tableIdOrName}/{recordId}/comments` | partial: text with `@[usr…]` mentions, `parentCommentId` |
| `PATCH` | `/v0/{baseId}/{tableIdOrName}/{recordId}/comments/{rowCommentId}` | partial: the token owner’s own comments |
| `DELETE` | `/v0/{baseId}/{tableIdOrName}/{recordId}/comments/{rowCommentId}` | partial: the token owner’s own comments |

`filterByFormula`, view filters and formula, count, lookup and rollup fields evaluate against the records. Unsupported formula functions return coverage gaps. Linked records stay in step on both sides. `TODAY()` and `NOW()` follow the scene clock.

Run `ancha twins coverage airtable` for the endpoint notes. Calls outside the coverage return Airtable's error shape with `x-ancha-gap: 1`.

## Starter data

The `airtable-starter` dataset belongs to **Ancha Assistant**, `ancha@acme.example`. Laura, Mike, Tom, Anna, Ellen and Greg are collaborators.

| Base and table | Starting data |
| - | - |
| Customer Success → Accounts | Six accounts: Ridgeway Robotics, Pinecrest Health, Kestrel Credit Union, Helix Manufacturing, Summit Freight and Bluefield Logistics. Rows include health, customer success owner, renewal date and billing details, with links to Contacts and Renewals. Views include Needs attention and Renewing in 60 days. |
| Customer Success → Contacts | Nine people, with account links and Champion, Executive, Billing or Technical roles. A Billing contacts view filters the list. |
| Customer Success → Renewals | Kestrel's `PO-77120` renewal for USD 48,000, Helix at risk and Summit's upcoming renewal. Current ARR is a rollup; Uplift is a formula. |
| Product Feedback → Requests | Six requests: Okta SSO, faster CSV exports, an export audit log, scheduled reports, SCIM provisioning and dark mode. The Roadmap view shows planned work and the beta. |

Your token has `create` permission on Customer Success and `comment` permission on Product Feedback. `ancha sandbox configuration --tools airtable --json` prints every field, view, record and comment.

## Checks

Airtable has no state checks. Use [trace checks](https://docs.ancha.io/concepts/checks.md#trace-patterns), for example `{ called: "PATCH /v0/*/Accounts" }` or `{ not_called: "DELETE /v0/*/*" }`. If your client uses a table ID instead of its name, use that ID in the pattern.

## Limits

* No attachments, webhooks, sync tables or new bases.
* `cellFormat=string` is a coverage gap. Unsupported field types and formula functions are gaps too.
* Batch record writes accept up to ten records. `PUT` clears fields you leave out; use `PATCH` to keep them.
* You can edit or delete only the token owner's comments. Base permissions still apply.


## Related topics

- [Tools](https://docs.ancha.io/tools/overview.md)
- [Introduction](https://docs.ancha.io/index.md)
- [Environment variables](https://docs.ancha.io/reference/environment-variables.md)
- [Changelog](https://docs.ancha.io/changelog.md)
- [ancha connect](https://docs.ancha.io/cli/connect.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.