Overview

Added link to broader description on firewall.

Webhooks allow apps to receive HTTP callbacks in real-time when events happen in Hootsuite rather than having to periodically poll the REST API.

Registering

The webhooks URL you use has several restrictions placed on it. It must:

  • Resolve to a public IP address not owned by Hootsuite (for example, it must not be in the internal network ranges defined in RFC 1918/RFC 4193 or the link-local ranges defined in RFC 3927/RFC 4291)
  • Accept traffic on either port 80 or 8080 for http or 443 or 8443 for https.
  • Return a response no larger than 32KB.

Ad-hoc webhooks URL can be provided when scheduling a message through the publishing API via the webhookUrls parameter.

A webhooks URL for Organization Apps can be configured through My Apps. These apps will then be notified in real-time when events occur related to organizations that install the app.

Receiving Data

Hootsuite will make a HTTP POST request to the configured webhooks URL. Event data will be aggregated and sent in a batch at most once every 5 seconds, or when the number of events exceeds 100. We expect your webhooks URLs to quickly respond (in less than 10 seconds) with a successful HTTP response (anything in the 200-299 range). You should return an empty body with your response.

Error Handling

See Error Handling for details on how Hootsuite handles failed webhook requests.

Payload Format

The webhook POST request body will have a Content Type of application/json and contain a JSON Array of Event objects.

The Event object has the following fields:

Field
Description
Type

seq_no

String representation of a 64-bit sequence number that will be different for each event. This can be used to deduplicate events that may occur as a result of retries.

string

type

A string that indicates the type of event. This will determine what the format of the data field is.

string

data

The actual event data. The contents of this depend on the type of event.

object

[
    {
        "seq_no": "123",
        "type": "some.type.here",
        "data": {
            // data specific to the event type
        }
    },
    // ... up to 100 of these
]

Payload Verification

Webhook POST requests may also include the following headers that can be used to verify the integrity of the request:

  • X-Hootsuite-Timestamp (All webhooks) - Unix timestamp in milliseconds of when the request was sent
  • X-Hootsuite-Signature (Organization Apps only) - Digital signature that can be used to verify that the contents of the request have not been tampered with.

The X-Hootsuite-Signature header value is calculated as follows:

  • Concatenate the value of the X-Hootsuite-Timestamp header and the POST body.
  • Calculate a SHA-512 HMAC hex digest on the bytes of that value (as a UTF-8 encoded string) using the Organization App's shared secret as the key. These shared secrets can be configured through My Apps.

For more information on the HMAC algorithm, see RFC 2104

You can perform the same calculation and compare the value against the contents of the X-Hootsuite-Signature

define('APP_SECRET', 'this_is_my_secret');

function verify_webhook_signature($sig, $timestamp, $body)
{
  $calculated = bin2hex(hash_hmac('sha512', $timestamp.$data, APP_SECRET, true));
  return ($sig == $calculated);
}

$signature = $_SERVER['HTTP_X_HOOTSUITE_SIGNATURE'];
$timestamp = $_SERVER['HTTP_X_HOOTSUITE_TIMESTAMP'];
$data = file_get_contents('php://input');
$verified = verify_signature($signature, $timestamp, $data);
package verification

import (
	"crypto/hmac"
	"crypto/sha512"
	"encoding/hex"
)

const (
	appSecret = "secret"
)

func VerifyWebhookSignature(signature string, timestamp string, body []byte) bool {
	h := hmac.New(sha512.New, []byte(appSecret))
	h.Write([]byte(timestamp))
	h.Write(body)
	return hex.EncodeToString(h.Sum(nil)) == signature
}
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

object Verifier {
  private val secret = new SecretKeySpec("secret".getBytes, "HmacSHA512")
  def verifyWebhookSignature(signature: String, timestamp: String, body: Array[Byte]): Boolean = {
    val mac = Mac.getInstance("HmacSHA512")
    mac.init(secret)
    mac.update(timestamp.getBytes)
    mac.doFinal(body).map(b => "%02X".format(b)).mkString.toLowerCase == signature
  }
}

Whitelisting Hootsuite Server IPs

All Hootsuite webhook requests originate from a static set of IP addresses. If your server handing webhook requests is behind a firewall, you will need to whitelist
the following set of IP addresses to ensure that requests succeed:

52.200.215.209
52.200.216.126
52.200.216.124

Event Types

See Event Reference for a list of events produced by Hootsuite

Overview


Added link to broader description on firewall.

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.