Responding to Platform Events using Webhooks
The Outreach API normally requires your action to retrieve, create or update resources. But with Webhooks, the Outreach API will automatically notify you whenever events that you are interested in occur. You can create webhooks just like any other resource. You must provide an HTTPS URL, and you can optionally specify the resources and actions that this webhook will respond to. By default, those values will be an asterisk*
, which indicates that all applicable
resources and actions will be delivered. Possible values include:Resource | Actions |
---|---|
* | * created updated destroyed |
account | * created updated destroyed |
call | * created updated destroyed |
mailing | * created updated destroyed bounced delivered opened replied |
opportunity | * created updated destroyed |
prospect | * created updated destroyed |
sequence | * created updated destroyed |
sequenceState | * created updated destroyed advanced finished |
task | * created updated destroyed completed |
import | * created finished |
user | * created updated |
When an active webhook sees a change to an applicable resource-action combination, the Outreach API will POST a JSON-API-styled request to the webhook's URL. Please note that create and update actions will contain only created/changed attributes, while deleted actions will contain the last known set of attributes and relationships before the resource was deleted.
Webhooks creation
For example, to create a webhook that will trigger after each "task completed" event:
Request
POST https://api.outreach.io/api/v2/webhooks
{
data: {
type: "webhook",
attributes: {
action: "completed",
resource: "task",
url: "https://foo.bar/webhooks",
},
},
}
Response
{
data: {
type: "webhook",
id: 1,
attributes: {
action: "completed",
active: true,
cleanupToken: "eyJh....",
createdAt: "2025-10-21T22:54:57.000Z",
creatorAppId: "<client id>",
creatorAppName: "<integration name>",
disabledReason: null,
disabledSince: null,
disabledUntil: null,
payloadVersion: 1,
resource: "task",
secret: "abcd1234",
updatedAt: "2025-10-21T22:54:57.000Z",
url: "https://foo.bar/webhooks",
},
},
}
Payload sent
When a task-completed event occurs, a JSON-API-styled request will be sent to the webhook's URL that includes the changed attributes and meta information about the webhook event.
The data payload of the webhook request comes in two different versions. The default is version 1:
POST https://foo.bar/webhooks
{
data: {
type: "task",
id: 1,
attributes: {
completed: true,
completedAt: "2017-01-01T00:00:00",
state: "completed",
}
},
meta: {
deliveredAt: "2017-01-01T00:00:01",
eventName: "task.completed",
}
}
Version 2 of the webhook request payload comes with additional information, including the values of the changed attributes before the change:
POST https://foo.bar/webhooks
{
data: {
type: "task",
id: 1,
attributes: {
completed: true,
completedAt: "2017-01-01T00:00:00",
state: "completed",
}
},
beforeUpdate: {
type: "task",
id: 1
attributes: {
completed: false,
completedAt: null,
state: "in progress",
},
},
meta: {
deliveredAt: "2017-01-01T00:00:01",
eventName: "task.completed",
}
}
payloadVersion
attribute set to 2
:POST https://api.outreach.io/api/v2/webhooks
{
data: {
type: "webhook",
attributes: {
action: "completed",
resource: "task",
payloadVersion: 2,
url: "https://foo.bar/webhooks",
},
},
}
Authenticity validation
For added protection, you can provide the webhook with asecret
attribute. If that value is present, we'll include
an Outreach-Webhook-Signature
header that is the HMAC hexdigest of the secret and the payload body, which you can use
to verify the integrity of the request.For example, to verify the integrity of an incoming webhook in Ruby:
SECRET = "foo" # Same as is saved in the Outreach webhook
def verified?(headers, body)
headers["Outreach-Webhook-Signature"] == signature(body)
end
def signature(body)
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), SECRET, body)
end
Outreach does not provide fixed list of IP addresses from which the webhooks are sent.
Responding to webhooks
It is recommended that a service behind webhook URL responds with200 OK
upon processing POSTed event.Outreach does not retry webhook deliveries upon receiving any of the Status Codes including 500 Internal Server Error
and 429 Too Many Requests
.
Some proxies may automatically reply with 429 Too Many Requests
upon exceeding certain limits or purchased capacity and those responses from webhook URLs are taken as acks.
Please ensure appropriate monitoring is in place to notice such events and increase availability of webhook URL service.Redirects are not followed either.
The timeout while waiting for response is set to 5 seconds. Please ensure that processing of posted events takes less than that or use a message bus for asynchronous processing.
Outreach retries on network related failures such as socket errors, connection resets, etc... There are 4 attempts (3 retries) made to deliver each event to a webhook URL with a one second pause between them.
Webhook removal
Webhooks configured by 3rd party integrations should be ideally removed by the integration when you, our customer, stop using their, our partner's, services. However, when the tokens get revoked before the cleanup happens, the partner integration loses access to our standard API. That's why we introduced special single-purpose webhook cleanup token.
The cleanup token is returned on webhook creation in thecleanupToken
attribute and also it is sent in every request in the outreach-webhook-cleanup-token
HTTP header so even old webhooks can be cleaned up by the responsible integrators that already lost
ability to call Outreach API but are still receiving customer data.The cleanup token already contains all the details needed by Outreach and authorizes removal of that particular webhook. No other actions can be made with this token. Remove the unwanted webhook by sending POST request to https://api.outreach.io/api/v2/webhooks/cleanup and include the token in the authorization bearer header. No need to send any payload.
curl -X POST "https://api.outreach.io/api/v2/webhooks/cleanup" -H "Authorization: Bearer eyJh..."
The response is always empty and you can interpret the HTTP status code in this way:
- 204: webhook successfully removed
- 404: webhook is already gone
- 401: invalid cleanup token provided
Webhook de-activation
In case the configured hostname cannot be resolved to a public IP address the webhook gets disabled temporarily. If the hostname resolves to an IP address from the private network IP address range the webhook is disabled for an hour. If the hostname cannot be resolved the DNS request is retried 3-times and the webhook is disabled for a minute if all attempts fail. Webhooks that cannot recover for a week or more are disabled permanently.
Webhook configuration visibility
Any user or application with access to Outreach and proper scopes can list all the webhooks. However, there are certain attributes that should not be visible to anyone like thesecret
used for payload signing, the cleanupToken
or sensitive parts of the URL.Outreach is recording the client ID of the application that created the webhook since 2024. The information is used to limit access to sensitive attributes of webhook configuration from non-admin users and for other applications than the one that created the webhook.
Admin user (not accessing webhooks via an application) can see all the details. Every application can see details of its own webhooks only. Regular users (not accessing webhooks via an application) see all the sensitive attributes redacted.