How to create and configure webhooks
September 4, 2024
This guide goes over how to create and configure webhooks with Unified.to. Webhooks deliver events to your server when records change in your customers' accounts (see webhook vs API for the basics).
Looking for help with troubleshooting webhook issues? See: How to troubleshoot unhealthy webhooks.
Before you begin
This guide assumes you have a basic understanding of webhooks.
Create a webhook
Webhooks and connections are closely related; webhooks can only be created from existing connections and you can create multiple webhooks off a single connection. Webhook support is per integration and per object. If an object doesn't support webhooks on an integration, create returns an error saying so. Check the integration's Feature Support tab in the dashboard before building on a webhook. If you delete a connection, any webhooks that are associated with it will also be deleted.
There are two ways to create a webhook:
Method 1: Use the Unified API
Send a POST request to /unified/webhook(see below for configuration options). For example:
POST /unified/webhook?include_all=true
{
"hook_url": "YOUR_WEBHOOK_URL",
"connection_id": "YOUR_CONNECTION_ID",
"object_type": "ats_candidate",
"event": "updated"
}
API reference: Create a webhook
By including the optional parameter include_all in the query, the webhook will receive all historical data for the connection (see: Get all initial data for a connection).
This is the payload that your server receives when webhook data comes in:
| Name | Type | Desc |
|---|---|---|
data | object array | An array of objects specific to the webhook's connection. (eg. CRM contacts) |
webhook | Webhook | The webhook object. You can use the id to manage or delete your webhook. |
nonce | string | Random string |
external_xref | string | The external_xref you set on the connection, returned so you can identify the user or account. |
sig256 | string | A security signature generated using HMAC-SHA256. It combines your workspace secret with the payload data and nonce i.e HMAC-SHA256(workspace.secret, data + nonce) . Use this to verify the authenticity of incoming webhooks. |
type | enum | INITIAL-PARTIAL, INITIAL-COMPLETE, VIRTUAL, NATIVE |
The objects in data use the same format as the corresponding API endpoint. The fields option, including raw, raw.* and custom fields, works the same way on webhooks as on API calls, for native and virtual webhooks. |
A note on the type enums:
INITIAL-PARTIAL: included with each page of results for the initial syncINITIAL-COMPLETE: included with last page (e.g. last 5 elements for the limit 100) of initial sync or with an empty list (no more results)VIRTUAL: included with every page when reading new data from a virtual webhookNATIVE: included with every page when reading new data from a native webhook
Method 2: Use the Unified.to app
If you prefer to create webhooks through the UI, you can do so at app.unified.to.
- Navigate to Integrations > Webhooks.
- Click New Webhook.
- Click on the input and a list of your connections will appear. Select the connection from which you want to create a webhook.
- Choose how to configure your webhook (see below).
- Click Save.
Information about the other fields can be found under their respective tooltips in the UI.
Webhook configuration options
Whether you use the API or our web UI, these are the configuration options you can specify when creating a webhook:
- Object: The data you are interested in e.g. Deal, Company, Application, etc.
- Event: The events you want to subscribe to. Note: The updated event will also be sent when the data object is first created. Native webhooks forward deleted events where the vendor sends them. On virtual webhooks, deleted events are reliable only on BambooHR, Dropbox, Google Drive, Gmail, JobAdder, OneDrive, SharePoint, RingCentral, Egnyte and Box.
- Type: Virtual webhooks cover integrations whose APIs don't offer webhooks, or whose webhooks are limited. Where an integration supports both, choose native for immediate updates and virtual to set how often changes are checked and control cost. The type can't be changed after creation (including with PUT); delete and recreate the webhook to switch. For more details, see: Understanding virtual webhooks.
- Filter (optional): Specify any properties you want to filter incoming events by. See: Use filters to refine webhook events
- Fields (optional): Comma-separated list of fields to include in the webhook payload. Leave blank to include all fields. The 'raw' field is NOT included by default. If you want to include all fields as well raw, just enter 'raw'. To read more about raw fields, see: Raw and custom fields.
- Interval (virtual webhooks only): Specify how often Unified.to checks the integration for changes. The minimum is 1 minute on paid plans and 60 minutes on the trial. The interval is ignored on native webhooks and doesn't affect the initial sync.
List your webhooks
To view your webhooks, make a GET request to /unified/webhook or visit app.unified.to and navigate to Integrations > Webhooks.
API reference: Get all webhooks
Get all initial data for a connection
When you create a webhook, you can choose to fetch all existing data immediately.
- If you are using the API, add the
include_allquery parameter when creating the webhook. - If you are using the UI, check off Initially sync all data in the configuration options.
Once the initial sync is complete, you will receive a final payload with type: INITIAL-COMPLETE and the requested webhook will continue to work as normal. Initial sync works the same way for native and virtual webhooks, wherever the integration supports it. Once it completes, a native webhook receives events as the integration sends them; a virtual webhook checks for changes on its interval.
API reference: Create a webhook subscription
A note on retrieving webhook data
Our system will POST initial data to your webhook in chunk sizes up to the limit supported by the integration. The limit is shown on the integration's Feature Support tab in the dashboard.
When performing the initial retrieval, depending on the amount of data being sent, the integration's API's rate-limiting rules, and your server's performance, it could take a very long time. If you do not want to get all of the existing data (for example, you already have it or don't need it), then do not include the include_all parameter in the request.
After the initial retrieval is completed, whenever updated data is available, your webhook will be called with a list of data objects, up to the max limit. If there are more entries than the limit, then your webhook URL will be POSTed to multiple times.
For details about the webhook payload that is sent to your server, click here.
Specify which fields to receive from a webhook
If you only want a subset of fields when receiving updated data, you can define them with the fields parameter in both the API and the UI. This is useful if you don't want the entire payload being sent to your servers. For more details, see: Fields.
API reference: Create a webhook subscription
Use filters to refine webhook events
Note: Filters are supported on virtual webhooks and on native webhooks for some integrations. Check the integration's Feature Support tab for which filters apply. For more information, see: Understanding virtual webhooks
Some webhooks support filters, allowing you to filter the events you receive. For instance, if you're subscribed to Deal events from a CRM, you could filter by company_id to only receive events from that specific company.
Filter availability varies by integration type and data model. To check which filters are supported for a particular webhook, open the integration's Feature Support tab in the dashboard and check the List Options and Virtual Webhook Options sections. Parameters that end in _id or type can be used as filter options when creating a webhook, helping you tailor your subscriptions to your specific needs. For worked examples in the dashboard and the API, see How to filter webhook events.
Trigger webhooks manually
If you don't want to wait for the next scheduled check while testing a virtual webhook, you can trigger it right away. Manual triggering applies to virtual webhooks only; the API returns 400 for native webhooks.
- In the web app, navigate to Webhooks.
- Click the Trigger icon next to your webhook:

This can also be accomplished with the API.
API reference: Trigger a webhook
Note: When you trigger a webhook, we will schedule the next dispatch event to go out right away. This is treated like a regular webhook event. Therefore, if your webhook is listening for update events and no new updates have occurred since the last run, then no new events will be sent to your server.
Frequently asked questions
Is there a page_max_limit for webhooks?
An integration's page_max_limit typically comes directly from the API provider. You can see that limit on the integration's Feature Support tab in the dashboard. You don't have to set limits for webhooks; they will use the integration's max limit.
Does changing the interval for virtual webhooks affect the initial retrieval?
Increasing frequency of virtual webhooks has no effect on the initial retrieval of historical data. Each page read is scheduled to be read one after another as soon as possible.
How do I distinguish between an updated event and a created event?
When a new data object is created, it triggers both created and updated events. If you want to track both of these events but also tell them apart, we recommend subscribing to the updated event and then compare created_at and updated_at for incoming data. If they are the same, that means the data object was newly created.
What happens if the webhook dispatch fails? Is there a retry mechanism in place?
If your endpoint doesn't respond, a native webhook is retried 3 times immediately, then marked unhealthy; the event isn't redelivered unless the integration sends it again. A virtual webhook makes up to 3 immediate attempts 1 second apart, then backs off progressively for up to about two weeks, re-reading the same page from the integration rather than resending a stored payload. Its cursor advances only after a successful delivery. When retries are exhausted, the webhook is marked unhealthy and scheduling stops. There's no way to re-enable an unhealthy webhook: fix your endpoint, then recreate the webhook. Failed delivery attempts aren't billed. Read more about it here.
Why do I receive duplicate webhook events and how should I handle them?
Duplicate webhook events can occur for several reasons:
- Some integrations (like HubSpot) trigger separate events for each property change on an object
- Rapid changes to objects can trigger multiple events that may arrive out of order due to network conditions
To handle duplicates effectively:
- Always use the object's latest state when updating your database
- Implement atomic writes to avoid race conditions. Only update records if the incoming data is newer:
-- Pseudo-code example
IF incoming_object.updated_at >= stored_object.updated_at THEN
UPDATE record
ELSE
-- Skip update as we already have newer data
END IF
Vendor-specific native webhook setup
Some integrations need native webhooks registered in the vendor's own app first. These guides cover the vendor-side steps:
- Facebook Messenger: webhook setup
- Greenhouse: native candidate webhooks
- HubSpot: configure webhooks
- LinkedIn: set up webhooks
- QuickBooks Online: native webhooks
- Slack: event subscriptions
- Thrive Learning: configure webhooks
- Toast: configure webhooks