AIRTABLE
Status: runtime adapter implementation + strict contract wiring.
AINL Airtable Adapter Contract (v1, runtime-native)
Status: runtime adapter implementation + strict contract wiring.
This document describes the airtable runtime adapter contract for AINL. It follows the same design intent as redis/dynamodb: explicit verbs, strict policy gates, and predictable result shapes.
1. Purpose
The airtable adapter provides no-code-friendly table and record operations for startup/prototype workflows.
The adapter is:
- runtime-native (registered via
ainl run --enable-adapter airtable), - explicitly gated by capability allow-lists and security profiles,
- policy-aware (
allow_write, table allow-list), - db-contract aligned where natural (
list/find/create/update/deletemap to read/create/update/delete flow).
2. Configuration
2.1 Environment variables
AINL_AIRTABLE_API_KEY(required PAT)AINL_AIRTABLE_BASE_ID(required)
2.2 CLI flags
Enable adapter:
ainl run your.ainl --enable-adapter airtable
Key flags:
--airtable-api-key--airtable-base-id--airtable-timeout-s--airtable-max-page-size--airtable-allow-write--airtable-allow-table(repeatable)--airtable-allow-attachment-host(repeatable, optional)
3. Security and privilege model
Adapter metadata in tooling/adapter_manifest.json:
privilege_tier:networkdestructive:truenetwork_facing:truesandbox_safe:false
Security profile behavior:
sandbox_compute_and_store:airtableis forbidden.sandbox_network_restricted:airtablecan be enabled.
Use PATs scoped to least-privilege base/table permissions.
4. Verbs and syntax
4.1 Read verbs
list(table, params?)->{records, offset?}find(table, formula_or_filter)->{records, offset?}get_table(table)->{table}list_tables()->{tables}list_bases()->{bases, offset?}
4.2 Write verbs
create(table, record_or_records)update(table, record_or_records)delete(table, record_id_or_ids)upsert(table, key_field, key_value, fields)->{ok, action, record}
4.3 Attachment verbs (scoped)
attachment.upload(table, record_id, field_name, file_path_or_bytes_or_url, filename?)attachment.download(table, attachment_url, output_path?)
Return shapes:
- upload ->
{ok, attachment|record} - download ->
{bytes_b64,size}or{path,size}
4.4 Webhook verbs (basic)
webhook.create(table, table_or_view, actions, notification_url)webhook.list(table)webhook.delete(table, webhook_id)
webhook.create returns registration metadata (webhook_id, expiration, MAC secret, specification). Payload ingestion/verification remains external in this pass.
Single-record shape:
{id, fields, createdTime}
Batch shape:
{records: [...]}
5. Validation and safety rules
- Mutating verbs require
allow_write=true. - Table allow-list is enforced when configured.
- Attachment and webhook verbs also respect table allow-list.
- URL-based attachment flows can be constrained by
allow_attachment_hosts. - Adapter retries transient HTTP failures and rate-limit responses with bounded backoff.
- Pass scope remains intentionally light (no schema ops, no deep webhook payload handling).
6. Connection behavior
- Uses Airtable REST API over HTTPS.
- Uses shared
httpx.Clientand async-compatiblehttpx.AsyncClientper adapter instance. - PAT and base id are required for table/record operations.
Implementation reference:
adapters/airtable/adapter.py
7. Limitations and roadmap
Current limitations:
- No schema mutation verbs in adapter.
- Webhook payload replay/verification helpers remain out-of-scope (registration lifecycle only).
Related docs:
docs/reference/ADAPTER_REGISTRY.mdtooling/adapter_manifest.jsontooling/security_profiles.json
8. Running integration tests
By default, integration tests are skipped by the standard pytest profile (-m 'not integration').
Direct API key mode:
export AINL_TEST_USE_AIRTABLE=1
export AINL_AIRTABLE_API_KEY='pat_xxx'
export AINL_AIRTABLE_BASE_ID='app_xxx'
export AINL_AIRTABLE_TEST_TABLE='users'
pytest -m integration tests/test_airtable_adapter_integration.py --airtable-api-key "$AINL_AIRTABLE_API_KEY" --airtable-base-id "$AINL_AIRTABLE_BASE_ID"
Make helper:
AINL_TEST_USE_AIRTABLE=1 \
AINL_AIRTABLE_API_KEY='pat_xxx' \
AINL_AIRTABLE_BASE_ID='app_xxx' \
AINL_AIRTABLE_TEST_TABLE='users' \
make airtable-it
