Responding to changes in real-time
Webhooks allow you to subscribe to changes that happen in Attio and then receive real-time HTTP requests to a chosen target URL to notify you of these changes.
This pattern can be useful in a wide range of contexts, but is often implemented by those who want to build real-time data syncs (e.g. an ETL pipeline) or fire automations in a timely manner (e.g. Attio’s own Zapier integration is powered by webhooks).
There are two places you can create webhooks: in our settings pages, and the API.
We offer a range of endpoints for creating, updating, deleting and viewing webhooks. You can find these endpoints in the webhooks API reference.
Creating webhooks over the API is essential for those building integrations for Attio that will operate for many customers.
The webhook APIs also allow you to utilise our powerful filtering functionality (see “Filtering” section below).
You can create webhooks for an integration in the developer settings page.
Please note that webhooks created with tokens that were created through our OAuth sign up flow will not be shown in the developer settings page.
When receiving webhooks, you should ensure that the request came from us. We cryptographically sign every webhook request using your webhook secret, and include it as HTTP header called Attio-Signature
(this is duplicated as X-Attio-Signature
to support legacy middleware).
The Attio-Signature
value is calculated using a SHA256 HMAC of the request body using your webhook secret as the secret. The webhook’s secret is viewable inside the developer settings page and in the API response when creating the webhook.
To verify that webhooks came from us, you should also construct the signature on your side using the same algorithm, then verify that it matches the one in the request.
We encode the Attio-Signature as a hexadecimal string, and we only sign the request body, which we interpret as a UTF-8 string.
Here’s an example in NodeJS for verifying the webhook signature:
Webhooks must target URLs secured with HTTPS. This is because targeting URLs using HTTP reveals the confidential content of webhooks to the public internet. In addition to exposing your data to ‘man in the middle’ attacks, webhooks delivered via HTTP can be read at any point in the journey to your server, for example, by cloud providers or logging services. Ensuring your target URL is encrypted with HTTPS keeps your data secure.
Webhooks guarantee at-least-once message delivery. Occasionally, due to network instability, Attio may send duplicate messages. To help deduplicate messages, Attio includes an Idempotency-Key
header which will be different for each message, but the same between retries and redeliveries.
If you’re receiving many duplicate messages, it may mean you’re not acknowledging them properly. Attio will mark a delivery as successful if the response code is within the 200-299 range (for example 200
or 202
). If you answer with any other code, Attio will retry delivery of the message up to 10 times with an exponential back-off, which will happen over approximately 3 days in total; after which, the webhook will be marked as degraded and we’ll send you an email.
We recommend using the request signature to validate the request instead of relying on IP allowlisting. This will mean that your integration does not require maintenance if we add new IP addresses.
Attio delivers webhooks from a fixed set of IP addresses. In some environments with restrictive firewalls it might be necessary to allowlist these IPs, and from time to time we might need to add a new IP Address to our list. We’ll endeavour to provide you with as much notice as possible before we change these.
To avoid overwhelming your server with a large burst of requests, Attio smoothes out webhook delivery with a rate limiter. Rate limiting is implemented on a per-target URL basis. We restrict delivery per URL to a maximum of 25 requests per second. Please contact support if you would like this number adjusted for your workspace.
The developer settings page provides the ability to deliver test payloads to your webhook’s target URL.
When building an integration that uses webhooks, this lets you quickly test that your integration is functioning, without having to modify real data in your workspace.
To send a test payload:
We populate test payloads with real data from your workspace. For example, when testing the note.created
event, we’ll set the note_id
property on the payload to correspond to a real note you have created. In cases where this is not possible, for example if you have no notes in your system, we’ll fallback to randomly generated fake data.
Please note that filters are not taken into account when generating test data.
To reduce the amount of webhooks you receive and help avoid writing client-side filtering code, it’s possible to define rules to further limit the events Attio will send. Filters work by taking the payload of the generated webhook event, and running it against a set of rules that you define.
For example, you might only care about updates to a particular attribute on a particular list, or about new notes on people but not companies.
Our API will validate that the filter syntax you have provided is valid. Filters are currently only editable and viewable over the API.
The filter syntax can be broken down into the following components.
$and
filter passes when all operations match the payload.$or
filter passes when at least one operation matches the payload.field
: Specifies which property of the webhook payload to apply the filter condition on. It supports nested properties using dot notation, such as "actor.type"
and "actor.id"
.operator
: The operator property defines the comparison operation to be used in the filter operation. The currently supported operators are: "equals"
and "not_equals"
value
: The value property specifies the value to compare against the chosen payload field using the operator.Subscribe to changes on the “Sales” list or the “Hiring” list
Subscribe to changes to the value of the “Status” attribute of the Sales list
Subscribe to changes made by workspace members
Subscribe to all events
Webhooks were supported over the V1 API and have now been replaced by updated V2 Webhooks. Using V2 webhooks will allow you to use our new filtering system and receive payloads which are consistent with the rest of the V2 API (e.g. we now refer to “lists” instead of “collections”).
V1 Webhook endpoints and even types will eventually be removed. Therefore, we recommend upgrading to use V2 Webhooks at your soonest convenience.
The following V1 Webhook events should be considered deprecated:
These have been replaced by the following V2 event types which fire under exactly the same circumstances.
Your code will also need to take into account the changes in the payloads of the above events.
Below are examples of payloads with V1 events and V2.
The following guide assumes you are implementing a zero downtime migration. You are, of course, welcome to migrate without such a constraint.
"entry.created"
event type, add a new one for the "list-entry.updated"
event type. V1 subscriptions used a static "collection_id"
property to limit subscriptions to a particular List (formerly “Collection”). This functionality can be replaced using our new filter functionality.For example, below is an example of a V1 subscription and its V2 replacement. These two subscriptions will respond to exactly the same changes in the system.
Any automated subscription creation using V1 APIs should be moved over to use V2 APIs. You should also move delete and update endpoints over to the V2 endpoints.
Responding to changes in real-time
Webhooks allow you to subscribe to changes that happen in Attio and then receive real-time HTTP requests to a chosen target URL to notify you of these changes.
This pattern can be useful in a wide range of contexts, but is often implemented by those who want to build real-time data syncs (e.g. an ETL pipeline) or fire automations in a timely manner (e.g. Attio’s own Zapier integration is powered by webhooks).
There are two places you can create webhooks: in our settings pages, and the API.
We offer a range of endpoints for creating, updating, deleting and viewing webhooks. You can find these endpoints in the webhooks API reference.
Creating webhooks over the API is essential for those building integrations for Attio that will operate for many customers.
The webhook APIs also allow you to utilise our powerful filtering functionality (see “Filtering” section below).
You can create webhooks for an integration in the developer settings page.
Please note that webhooks created with tokens that were created through our OAuth sign up flow will not be shown in the developer settings page.
When receiving webhooks, you should ensure that the request came from us. We cryptographically sign every webhook request using your webhook secret, and include it as HTTP header called Attio-Signature
(this is duplicated as X-Attio-Signature
to support legacy middleware).
The Attio-Signature
value is calculated using a SHA256 HMAC of the request body using your webhook secret as the secret. The webhook’s secret is viewable inside the developer settings page and in the API response when creating the webhook.
To verify that webhooks came from us, you should also construct the signature on your side using the same algorithm, then verify that it matches the one in the request.
We encode the Attio-Signature as a hexadecimal string, and we only sign the request body, which we interpret as a UTF-8 string.
Here’s an example in NodeJS for verifying the webhook signature:
Webhooks must target URLs secured with HTTPS. This is because targeting URLs using HTTP reveals the confidential content of webhooks to the public internet. In addition to exposing your data to ‘man in the middle’ attacks, webhooks delivered via HTTP can be read at any point in the journey to your server, for example, by cloud providers or logging services. Ensuring your target URL is encrypted with HTTPS keeps your data secure.
Webhooks guarantee at-least-once message delivery. Occasionally, due to network instability, Attio may send duplicate messages. To help deduplicate messages, Attio includes an Idempotency-Key
header which will be different for each message, but the same between retries and redeliveries.
If you’re receiving many duplicate messages, it may mean you’re not acknowledging them properly. Attio will mark a delivery as successful if the response code is within the 200-299 range (for example 200
or 202
). If you answer with any other code, Attio will retry delivery of the message up to 10 times with an exponential back-off, which will happen over approximately 3 days in total; after which, the webhook will be marked as degraded and we’ll send you an email.
We recommend using the request signature to validate the request instead of relying on IP allowlisting. This will mean that your integration does not require maintenance if we add new IP addresses.
Attio delivers webhooks from a fixed set of IP addresses. In some environments with restrictive firewalls it might be necessary to allowlist these IPs, and from time to time we might need to add a new IP Address to our list. We’ll endeavour to provide you with as much notice as possible before we change these.
To avoid overwhelming your server with a large burst of requests, Attio smoothes out webhook delivery with a rate limiter. Rate limiting is implemented on a per-target URL basis. We restrict delivery per URL to a maximum of 25 requests per second. Please contact support if you would like this number adjusted for your workspace.
The developer settings page provides the ability to deliver test payloads to your webhook’s target URL.
When building an integration that uses webhooks, this lets you quickly test that your integration is functioning, without having to modify real data in your workspace.
To send a test payload:
We populate test payloads with real data from your workspace. For example, when testing the note.created
event, we’ll set the note_id
property on the payload to correspond to a real note you have created. In cases where this is not possible, for example if you have no notes in your system, we’ll fallback to randomly generated fake data.
Please note that filters are not taken into account when generating test data.
To reduce the amount of webhooks you receive and help avoid writing client-side filtering code, it’s possible to define rules to further limit the events Attio will send. Filters work by taking the payload of the generated webhook event, and running it against a set of rules that you define.
For example, you might only care about updates to a particular attribute on a particular list, or about new notes on people but not companies.
Our API will validate that the filter syntax you have provided is valid. Filters are currently only editable and viewable over the API.
The filter syntax can be broken down into the following components.
$and
filter passes when all operations match the payload.$or
filter passes when at least one operation matches the payload.field
: Specifies which property of the webhook payload to apply the filter condition on. It supports nested properties using dot notation, such as "actor.type"
and "actor.id"
.operator
: The operator property defines the comparison operation to be used in the filter operation. The currently supported operators are: "equals"
and "not_equals"
value
: The value property specifies the value to compare against the chosen payload field using the operator.Subscribe to changes on the “Sales” list or the “Hiring” list
Subscribe to changes to the value of the “Status” attribute of the Sales list
Subscribe to changes made by workspace members
Subscribe to all events
Webhooks were supported over the V1 API and have now been replaced by updated V2 Webhooks. Using V2 webhooks will allow you to use our new filtering system and receive payloads which are consistent with the rest of the V2 API (e.g. we now refer to “lists” instead of “collections”).
V1 Webhook endpoints and even types will eventually be removed. Therefore, we recommend upgrading to use V2 Webhooks at your soonest convenience.
The following V1 Webhook events should be considered deprecated:
These have been replaced by the following V2 event types which fire under exactly the same circumstances.
Your code will also need to take into account the changes in the payloads of the above events.
Below are examples of payloads with V1 events and V2.
The following guide assumes you are implementing a zero downtime migration. You are, of course, welcome to migrate without such a constraint.
"entry.created"
event type, add a new one for the "list-entry.updated"
event type. V1 subscriptions used a static "collection_id"
property to limit subscriptions to a particular List (formerly “Collection”). This functionality can be replaced using our new filter functionality.For example, below is an example of a V1 subscription and its V2 replacement. These two subscriptions will respond to exactly the same changes in the system.
Any automated subscription creation using V1 APIs should be moved over to use V2 APIs. You should also move delete and update endpoints over to the V2 endpoints.