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:
ResourceActions
** 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

Copy
Copied
POST https://api.outreach.io/api/v2/webhooks
Copy
Copied
{
  data: {
    type: "webhook",
    attributes: {
      action: "completed",
      resource: "task",
      url: "https://foo.bar/webhooks",
    },
  },
}

Response

Copy
Copied
{
  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:

Copy
Copied
POST https://foo.bar/webhooks
Copy
Copied
{
  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:

Copy
Copied
POST https://foo.bar/webhooks
Copy
Copied
{
  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",
  }
}
To get version 2 of the webhook data payload, create the webhook with the payloadVersion attribute set to 2:
Copy
Copied
POST https://api.outreach.io/api/v2/webhooks
Copy
Copied
{
  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 a secret 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:

Copy
Copied
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 with 200 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 the cleanupToken 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.

Copy
Copied
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 the secret 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.