Send webhooks when records change (outgoing webhooks)
An outgoing webhook sends an HTTP POST to a URL you choose when a record in one of your tables is created, updated or deleted. You add and manage webhooks on the Webhooks tab of Data & API, or with the REST API. Webhooks need the Business plan or above.
Add a webhook
- Open Tools in the sidebar and choose Data & API.
- Open the Webhooks tab.
- Click Add webhook.
- Enter the Endpoint URL. It must be a public HTTPS address.
- Under Events, find each table you want to hear about and tick Created, Updated or Deleted. Tick All changes to get all three.
- Click Add webhook.
- Copy the Signing secret and store it somewhere safe. It is shown only this once. Your endpoint uses it to check that a delivery really came from Unstack.
Owners and admins can add and manage webhooks. So can anyone whose role has Webhooks or API keys switched on under Data & API in Roles & permissions.
A webhook added by someone who is not an owner or admin can only send records from tables that have no access rules for roles, because it would otherwise show them rows or columns their role can't see. The same goes once they change a webhook's URL or events. If one of its tables gets access rules later, that table's records stop going to it. Owners and admins can send any table.
Test, edit, pause and remove a webhook
Each webhook in the list has its own buttons.
- Send test sends a signed test delivery with the event
webhook.testand shows the status your endpoint answered with. The test is kept under Deliveries, marked Test. A failed test is not retried and does not count toward turning the webhook off. - Edit changes the endpoint URL or the events. The signing secret stays the same. Changing the URL gives the webhook a clean start: its failed attempts are cleared, and if it was turned off for failing it is turned back on. A webhook you paused yourself stays paused.
- Pause stops deliveries until you click Resume. Changes made while a webhook is paused are not sent afterwards.
- Deliveries shows the most recent deliveries: the event, whether it was delivered, the HTTP status your endpoint answered with, the attempt number and the time. Click Refresh to update the list. Test deliveries are listed too, marked Test.
- Rotate secret makes a new signing secret and shows it once. The old secret stops working right away, so give your endpoint the new one.
- Delete removes the webhook and its delivery history.
A webhook marked Failing has had failed deliveries recently. A webhook marked Turned off was stopped after repeated failures. Fix your endpoint, then click Resume. If your endpoint moved to a new address, click Edit and save the new URL instead. See Verify webhook signatures, retries and delivery history.
When events are sent
Events are sent for every change to a record, wherever it was made: on a page in your app, by the assistant, by an import, by an automation or through the REST API.
Every changed record sends its own event. There is no limit on how many. A large change, such as an import or a bulk edit, is delivered over the following minutes rather than all at once.
Events for one app are delivered in the order the changes happened, one at a time.
Delivery normally happens within seconds of the change, but it can lag by up to about a minute. The timestamp in the payload is the time the record changed, not the time the event was delivered.
Delivery is at-least-once: now and then the same event can arrive twice. Make your endpoint safe to run twice for the same change. The record id together with the payload timestamp identifies one change, so keep the pairs you have handled and skip one you have seen before.
Some changes do not send an event:
- Saving a record without changing any of its fields does not send
updated. - Sample records the assistant adds while building a table do not send events.
- Deleting a whole table does not send
deletedfor each of its records. - Records in a table connected from an outside source, such as Airtable, Google Sheets or Snowflake, do not send events.
Events
Event names follow the pattern custom.{table-slug}.created, custom.{table-slug}.updated and custom.{table-slug}.deleted. For a table whose slug is customers, the events are custom.customers.created, custom.customers.updated and custom.customers.deleted.
When you add a webhook, each table is listed with its slug under Events. With the API, call GET /api/v1/custom to find a table's slug.
The payload
```json
{
"event": "custom.customers.created",
"timestamp": "2026-10-13T08:30:00.000Z",
"data": {
"id": "0b6f1c0e-...",
"name": "Blue Wave Pools",
"email": "office@example.com",
"created_at": "2026-10-13T08:30:00.000Z",
"updated_at": "2026-10-13T08:30:00.000Z",
"row_number": 48
}
}
```
data is the record after the change. For a deleted record, data contains only the id. timestamp is the time the record changed.
Use the REST API instead
Everything on the Webhooks tab can also be done with the REST API, where a webhook is called a subscription. You need an API key with the Webhooks (Write) scope. See Create an API key.
```bash
curl -X POST "https://unstack.us/api/v1/webhooks" \
-H "X-API-Key: usk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/unstack",
"events": ["custom.customers.created", "custom.customers.updated"]
}'
```
The response contains the subscription and its secret. The secret is returned only this once, so store it. You use it to verify signatures.
All paths are under https://unstack.us/api/v1.
GET /webhookslists your subscriptions.GET /webhooks/{id}returns one.PATCH /webhooks/{id}changesurl,eventsoris_active. Send only what you want to change. The secret stays the same, and a newurlclears the failure count.POST /webhooks/{id}/rotate-secretreplaces the signing secret and returns the new one, this once.DELETE /webhooks/{id}removes one.POST /webhooks/{id}/testsends a signed test delivery with the eventwebhook.test.GET /webhooks/{id}/deliverieslists delivery attempts. Test deliveries are included, withis_testset totrue.
Rules for the URL
- It must use HTTPS.
- Only ports 80, 443, 8080 and 8443 are accepted.
- It must be a public address. Private and reserved addresses are rejected, and the address is checked again before every delivery.
- Your endpoint must answer within 10 seconds with a 2xx status.
- Redirects are not followed. A 3xx answer counts as a failed delivery, so point the webhook at the final URL.
An app can have up to 50 webhooks. Delete the ones you no longer use.
Next: Verify webhook signatures, retries and delivery history.