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.
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.