Connectors
/
Airtable
Live · OAuth 2.0

Airtable Integration for AI Agents

The Airtable API is well-documented. Getting your agent to read and write it correctly — across multiple users, with tokens that never expire at the wrong moment — is the part that takes longer than it should.
Airtable
Live

Database

Collaboration

Status
Live
Tools
REST API proxy
Auth
OAuth 2.0 + PKCE
Credential storage
Zero
Sandbox support
No

OAuth 2.0

Auto token refresh

Zero credential storage

Revocation detection

Scoped permissions

The real problem

Why this is harder than it looks

Most teams look at Airtable's REST API and think the integration will take an afternoon. The read/write calls are clean. The docs are good. The problems start once you're running this for real users.

In production, each of your users connects their own Airtable workspace. That means per-user token isolation — one user's credentials must be completely invisible to another, even if they share the same base. Airtable uses OAuth 2.0 with PKCE, and access tokens have a short expiry; your refresh logic has to run proactively, before a call fails, not in response to a 401. If a user revokes your app from their Airtable account settings, you need to detect it and stop making calls on their behalf immediately — silent failures in a database context are particularly dangerous.

Then there's the scopes problem. Airtable's permission model is granular: read, write, schema access, and user metadata are all separate scopes. Ask for too little and your agent fails at runtime. Ask for too much and your users' IT admins reject the OAuth consent screen. Getting the scope set right — and keeping it right as your agent's capabilities grow — is its own ongoing maintenance task.

None of this is impossible. But it's weeks of plumbing that doesn't make your agent smarter. And you'll repeat it for every connector you add. Scalekit handles the token lifecycle, scope configuration, and revocation detection so your code only has to make the API calls.

Capabilities

What your agent can do with Airtable

Once connected, your agent can make authenticated calls across the full Airtable REST API:

  • Read and query records: list records from any table with filters, sorts, and field selection; paginate large datasets automatically
  • Create and update structured data: add new rows, patch existing records, upsert by a unique field value
  • Manage tables and fields: create tables, add or modify fields, read schema metadata at runtime
  • Inspect bases and workspaces: list all accessible bases, describe table structures, check record counts without fetching all data
  • Delete records cleanly: remove individual records or batches with full error handling per operation
Setup context

What we're building

This guide connects a project data assistant to Airtable — helping team members query records, log updates, and manage structured project data without leaving your product.

🤖
Example agent
Project assistant reading and writing Airtable bases on behalf of each team member
🔐
Auth model
B2B SaaS — each user connects their own workspace. identifier = your user ID
⚙️
Scalekit account
app.scalekit.com — Client ID, Secret, Env URL
🔑
Airtable OAuth app
Register at airtable.com/developers — required for BYOC setup
Setup

1 Setup: One SDK, One credential

Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no Airtable secrets, no user tokens, nothing belonging to your customers.

pip install scalekit-sdk-python
import scalekit.client import os from dotenv import load_dotenv load_dotenv() scalekit = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENV_URL"), ) actions = scalekit.actions
Connected Accounts

2 Per-User Auth: Creating connected accounts

Each user gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.

response = actions.get_or_create_connected_account( connection_name="airtable", identifier="user_at_456" # your internal user ID ) connected_account = response.connected_account print(f"Status: {connected_account.status}") # Status: PENDING — user hasn't authorized yet

This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.

Authorization Flow

3 The authorization flow

The user authorizes your agent once. Scalekit generates the OAuth URL with correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.

if connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name="airtable", identifier="user_at_456" ) # Redirect user → Airtable consent screen # Scalekit captures the token on callback return redirect(link.link)
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Access tokens refresh before expiry. If a user revokes access from Airtable's app settings, the account moves to REVOKED — no silent failures. Check account.status before critical write operations.
Bring Your Own Credentials — required for production
Unlike some connectors, Airtable requires you to register your own OAuth app in the Airtable Developer Hub and supply your own Client ID and Secret. This means your users see your app name on the consent screen. Register the Scalekit redirect URI in your Airtable app settings, then paste your credentials into the Scalekit dashboard. Token management stays fully handled.
Already have credentials?
Calling Airtable

4 Calling Airtable: What your agent writes

With the connected account active, your agent makes API calls using actions.request(). Pass the Airtable API path and method. Scalekit injects the correct token, handles retries on refresh, and returns the parsed response.

List records from a table

Fetch records from a specific table. Use params to pass Airtable query options like filterByFormula, sort, or fields.

result = actions.request( connection_name="airtable", identifier="user_at_456", path="/v0/appXXXXXXXX/Projects", method="GET", params={ "filterByFormula": "AND({Status}='In Progress', {Owner}='Alice')", "fields[]": ["Name", "Status", "Due Date", "Priority"], "sort[0][field]": "Due Date", "sort[0][direction]": "asc" } ) # { "records": [ { "id": "recXXX", "fields": { "Name": "...", ... } }, ... ] }

Create a record

Add a new row to any table. Field names in fields must match the column names exactly as they appear in the base.

result = actions.request( connection_name="airtable", identifier="user_at_456", path="/v0/appXXXXXXXX/Projects", method="POST", json={ "fields": { "Name": "Q3 Roadmap Review", "Status": "Not Started", "Due Date": "2026-07-15", "Priority": "High", "Owner": "Alice" } } ) # { "id": "recYYYYYYYY", "fields": { ... }, "createdTime": "..." }

Update a record

Patch an existing record by ID. Only the fields you supply are modified — other fields remain untouched.

result = actions.request( connection_name="airtable", identifier="user_at_456", path="/v0/appXXXXXXXX/Projects/recYYYYYYYY", method="PATCH", json={ "fields": { "Status": "In Progress", "Notes": "Kickoff completed, design review scheduled for next week" } } )

Inspect base schema at runtime

Your agent can read the schema of any accessible base without hardcoding field names — useful for dynamic agents that adapt to the user's actual Airtable structure.

schema = actions.request( connection_name="airtable", identifier="user_at_456", path="/v0/meta/bases/appXXXXXXXX/tables", method="GET" ) # Returns all table names, field types, and relationship metadata # Agent can use this to build dynamic queries without hardcoded field names for table in schema["tables"]: print(f"Table: {table['name']}, Fields: {[f['name'] for f in table['fields']]}")
Framework wiring

5 Wiring into your agent framework

Scalekit integrates directly with LangChain. The agent decides what to look up or write; Scalekit handles auth on every invocation. No token plumbing in your agent logic.

from langchain_anthropic import ChatAnthropic from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from scalekit.langchain import get_tools at_tools = get_tools( connection_name="airtable", identifier="user_at_456" ) prompt = ChatPromptTemplate.from_messages([ ("system", "You are a project data assistant. Use the available tools to help manage Airtable records."), MessagesPlaceholder("chat_history", optional=True), ("human", "{input}"), MessagesPlaceholder("agent_scratchpad"), ]) agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), at_tools, prompt) result = AgentExecutor(agent=agent, tools=at_tools).invoke({ "input": "Find all high-priority projects owned by Alice that are due this month" })
Other frameworks supported
API reference

Airtable REST API — Key endpoints

Scalekit proxies any Airtable REST API call. Your agent passes the path and method — here are the most useful endpoints grouped by purpose.

Records
GET /v0/{baseId}/{tableId}
List records from a table. Supports filterByFormula, sort, fields, maxRecords, and pageSize parameters
GET /v0/{baseId}/{tableId}/{recordId}
Retrieve a single record by ID with all field values
POST /v0/{baseId}/{tableId}
Create one or more records. Pass a single fields object or a records array for bulk creation
PATCH /v0/{baseId}/{tableId}/{recordId}
Update specific fields on a record. Only supplied fields are changed
PUT /v0/{baseId}/{tableId}/{recordId}
Replace all fields on a record. Unsupplied fields are cleared
DELETE /v0/{baseId}/{tableId}/{recordId}
Permanently delete a record by ID
POST /v0/{baseId}/{tableId}/upsert
Upsert records by a unique field — creates if not found, updates if it exists
Schema & Metadata
GET /v0/meta/bases
List all bases the authenticated user has access to, including base IDs and names
GET /v0/meta/bases/{baseId}/tables
Retrieve full schema for all tables in a base — field names, types, and relationships
POST /v0/meta/bases/{baseId}/tables
Create a new table in a base with specified fields and field types
PATCH /v0/meta/bases/{baseId}/tables/{tableId}
Update a table's name or description
POST /v0/meta/bases/{baseId}/tables/{tableId}/fields
Add a new field to an existing table with a specified type and configuration
PATCH .../{tableId}/fields/{fieldId}
Update an existing field's name, description, or type-specific options
User & Identity
GET /v0/meta/whoami
Return the authenticated user's ID, email, and name — useful to verify authorization succeeded
Connector notes

Airtable-specific behavior

BYOC is required — no managed app available
Unlike some Scalekit connectors, Airtable does not provide a managed Connected App. You must register your own OAuth app in the Airtable Developer Hub, configure the redirect URI, and supply your Client ID and Secret in the Scalekit dashboard before the authorization flow will work.
Scope selection affects what your agent can do
Airtable's OAuth scopes are fine-grained. data.records:read and data.records:write cover most CRUD operations, but schema access (schema.bases:read) is a separate scope. If your agent calls schema introspection endpoints without this scope, you will get a 403 — not an auth error. Ensure your scope set in the Scalekit dashboard matches your agent's full capability set before deploying.
Base IDs must be obtained at runtime
Airtable base IDs (e.g. appXXXXXXXX) are workspace-specific. For agents that work across multiple users' bases, use GET /v0/meta/bases to discover base IDs at runtime rather than hardcoding them. This is especially important in multi-tenant deployments where each user may have different bases.
Infrastructure decision

Why not build this yourself

The Airtable OAuth flow is documented. Token storage isn't technically hard. But here's what you're actually signing up for:

PROBLEM 01
Per-user token isolation across a multi-tenant system — one user's Airtable credentials must never be accessible to another
PROBLEM 02
Proactive refresh logic that runs before token expiry — not after a failed call returns a 401 mid-operation
PROBLEM 03
Revocation detection when users disconnect your app from Airtable — and graceful handling so agents stop making calls immediately
PROBLEM 04
Scope management as your agent's capabilities grow — adding new API endpoints without requiring users to re-authorize is a non-trivial token lifecycle problem

That's one connector. Your agent product will eventually need Salesforce, Gmail, Notion, HubSpot, and whatever else your customers ask for. Each has its own OAuth quirks and failure modes.

Scalekit maintains every connector. You maintain none of them.

Ready to ship

Connect your agent to Salesforce in minutes

Free to start. No Salesforce Connected App required. Token management fully handled.