Skip to content
newThe webhook.co MCP server is live — turn any webhook into an agent event. Read the docs
test locally

Test Airtable webhooks locally

Airtable webhooks are registered through the Web API — that is the only path Airtable's docs describe — and the POST that lands on your endpoint carries no data. Both facts change how you test one.

The loop

Create an endpoint and it prints a permanent ingest URL. Everything sent there is captured whole — headers, body, the exact bytes — before anything else happens. Stream those events to a port on your machine, and replay any one of them as often as you like while you fix the handler. Forwarding runs from your own machine, so it isn't a billable delivery.

wbhkairtable
$ wbhk endpoints create airtable-dev
https://wbhk.my/whep_…
$ wbhk listen <endpoint-id> --forward http://localhost:3000/webhooks
$ wbhk replay <event-id> --forward http://localhost:3000/webhooks

What's different about Airtable

The notification body is three fields — base id, webhook id, timestamp — and nothing else. Per Airtable's docs, the recipient is then responsible for requesting the contents of the updates from the API. So what you're testing locally isn't a payload shape, it's a fetch loop: take the ping, call list webhook payloads with a cursor (omit it the first time and it defaults to 1), and keep calling while mightHaveMore is true, at most 50 payloads per request. Delivery is at-least-once and coalesced, so pings and changes don't reliably line up one to one.

Where the URL goes

Airtable's docs describe only the API path for this. You POST to the bases webhooks endpoint on api.airtable.com with a notificationUrl and a specification object, using a personal access token or OAuth token, and creator-level permissions are required to register at all. Scopes stack: webhook:manage always, plus data.records:read if the specification subscribes to tableData, and schema.bases:read for tableFields or tableMetadata. The 200 response hands back the webhook id and macSecretBase64.

No send-test button, no sample generator. The whole surface is six endpoints — create, list, delete, enable or disable notification pings, list payloads, and refresh — and none of them manufactures traffic. You provoke the first ping by making a change in the base that actually matches your specification, such as editing a record in the scoped table. Then read lastNotificationResult from the webhooks list: success, retryNumber, error, willBeRetried, alongside lastSuccessfulNotificationTime.

What Airtable sends

Nothing here is dotted or slashed. A subscription is a cross-product of flat camelCase enums inside specification.options.filters: dataTypes picks which kind of data to watch, changeTypes narrows to additions, removals or updates, and fromSources plus a recordChangeScope table or view id narrow it further.

  • tableData
  • tableFields
  • tableMetadata
  • add
  • remove
  • update

Worth knowing first

Twenty-five seconds, then thirteen retries

Your endpoint has to return 200 or 204 with an empty response body, and the connection, request and response together must finish inside 25 seconds. Miss that and Airtable retries the ping up to 13 times with exponential backoff starting at 10 seconds — roughly a day — then drops the ping and disables notifications for that webhook.

Nothing turns notifications back on for you

Disabling notifications does not stop payload generation, so the changes keep accumulating and you can catch up once the endpoint is fixed. The pings, though, stay off until you POST enable set to true to that webhook's enableNotifications endpoint. Worth knowing before you spend an afternoon wondering why a repaired handler is silent.

The webhook expires after seven days

A webhook created with a personal access token or OAuth token expires and is disabled after 7 days. Calling refresh webhook or list webhook payloads extends it another 7 days, so an active fetch loop keeps itself alive while an idle test setup quietly does not. Payloads are deleted after a week whether or not you have read them.

One ping is not one change

Pings are at-least-once, spurious notifications with no new changes are possible, and multiple changes in rapid succession may produce only a single notification. Deduplicate and order on baseTransactionNumber, which is scoped to that webhook and increases sequentially, rather than counting pings or trusting arrival order.

The MAC secret is shown once

Each ping carries an X-Airtable-Content-MAC header, a hash of the body contents using the hook's MAC secret. That secret comes back only in the create response, as macSecretBase64, and the docs say plainly there is no way to retrieve it after initial creation. Store it before you close the terminal.

Ten webhooks per base is the ceiling

A base allows 10 webhooks, and a single OAuth integration is limited to 2 per base. All of it sits under the same 5 requests per second per base limit as the rest of the API, which is worth remembering when a ping handler immediately turns around and calls list webhook payloads.

Signature checking is handled for you once the secret is registered — you can also check a captured signature by hand in the signature verifier.

Airtable webhooks: common questions

Can I point an Airtable webhook at localhost?

Airtable delivers the ping from its own infrastructure to whatever notificationUrl you registered. Register a URL that forwards to your machine and point the webhook at that. Airtable's docs state no further requirements for the URL.

Does Airtable have a button that sends a test webhook?

No. The Webhooks API is six endpoints: create, list, delete, enable or disable notification pings, list payloads, and refresh. None of them fires a sample. Make a change in the base that matches your specification and the ping follows from that.

Why is my Airtable webhook payload empty?

It is not broken. The notification body is only the base id, the webhook id and a timestamp. The changes themselves come from a separate call to list webhook payloads, which returns a maximum of 50 per request, so keep paging while mightHaveMore is true.

Which token scopes does an Airtable webhook need?

The scope webhook:manage is required for every webhooks endpoint. Beyond that, a specification subscribing to tableData also needs data.records:read, and one subscribing to tableFields or tableMetadata needs schema.bases:read. Registering a webhook additionally requires creator level permissions on the base.

Can I scope an Airtable webhook to a single table?

Yes. recordChangeScope takes a table or view id and only generates payloads for changes in it, though Form and List views are not supported as a scope. It pairs with fromSources, which filters by origin: client, publicApi, formSubmission, automation, sync and several others.

What happens to Airtable changes I miss while testing?

Payloads keep being generated even when notifications are disabled, and you read them back with the cursor, so a broken endpoint does not lose the changes outright. The limit is time: payloads are deleted from Airtable's servers after 1 week whether or not you have seen them.

Point a webhook at it. Watch it land.

Start free: a permanent URL, full inspection, one-command replay — and outbound delivery, because every feature is on every plan. Move up when you need more events.