Gareth Wilson Gareth Wilson

Guide to 8x8 Webhooks: Features and Best Practices

Published


8x8 webhooks tell your systems what is happening to the messages you send and receive through 8x8 Connect, the CPaaS portal behind 8x8's SMS and Chat Apps APIs. A customer texts your number, a WhatsApp message is read, a delivery receipt comes back from a carrier, someone clicks a shortened link, or Meta changes the status of one of your WhatsApp templates. Each of these can reach your own service as a JSON request, so you can reply to customers, update message status and keep your template records in sync without polling.

This guide covers how 8x8 Connect webhooks are configured and authenticated, the event types and the two payload shapes you will see, 8x8's short retry schedule, and the practices that make an 8x8 receiver reliable.

What are 8x8 webhooks?

In 8x8 Connect, a webhook belongs to a sub-account, or to every sub-account, and to a type: SMS, Messaging Apps or Voice. An SMS webhook receives both inbound SMS and SMS delivery receipts on one URL. Messaging Apps webhooks cover Chat Apps channels such as WhatsApp and Viber. This guide covers SMS and Chat Apps webhooks; Voice webhooks are configured through a separate API, and the Voice Call Action webhook expects a call flow in the response, which is a different integration pattern.

Every request is a POST with a JSON body and a top-level eventType. 8x8 does not sign these requests. Instead, each webhook has an optional HTTP Authorization value that 8x8 sends as the Authorization header on every request, exactly as you entered it. Comparing that header with the value you configured is how your receiver tells an 8x8 request from a forged one.

8x8 webhook features

FeatureDetails
Configuration8x8 Connect Webhooks page, or the Webhooks Configuration API
ScopeOne sub-account, or all sub-accounts with *
TypesSMS (inbound and delivery receipts), Messaging Apps, Voice
Method and formatPOST, JSON
Event fieldeventType in the body
AuthenticationOptional HTTP Authorization value (API field httpAuthorization), sent verbatim as Authorization
Other auth optionOAuth 2.0 client credentials, where 8x8 fetches a token from your token endpoint
Signature and timestampNone documented for Connect webhooks
RetriesAfter 1, 10, 30 and 90 seconds on a connection error, timeout, 4xx or 5xx
Delivery receipts8x8 keeps checking with the carrier for up to 48 hours
Source IPsNo egress list published for SMS or Chat Apps webhooks

8x8's IP Whitelisting page in Connect restricts which addresses can call the 8x8 API. It does not tell you where webhooks come from.

Common events

eventTypeEnvelopeSent when
inbound_message_receivedMessagingAn SMS or Chat Apps message arrives for your number. namespace is SMS or ChatApps
outbound_message_status_changedMessagingA delivery receipt. payload.status.state is queued, sent, delivered, undelivered, rejected or read
external_app_messageMessagingA message sent from the WhatsApp Business App is forwarded to your webhook
short_url_clickedMessagingA recipient clicks a shortened URL in an SMS
template_status_updateBusiness managementA WhatsApp or Viber template is approved, rejected, paused and so on
template_category_update, template_quality_update, template_correct_category_detectionBusiness managementA WhatsApp template's category or quality changes, or WhatsApp detects a better category
account_updateBusiness managementYour WhatsApp Business Account changes, refined by eventDetails.event
phone_number_quality_updateBusiness managementA WhatsApp number's messaging limit or throughput changes
whatsapp_automatic_eventsBusiness managementMeta detects a purchase or lead in a conversation from a Click-to-WhatsApp ad

Messaging events share one envelope: namespace, eventType, description and a payload object. Because SMS and Chat Apps use the same eventType values, route on namespace and eventType together. Here is 8x8's inbound SMS example:

{
  "namespace": "SMS",
  "eventType": "inbound_message_received",
  "description": "SMS inbound message",
  "payload": {
    "umid": "9e09ac86-bd74-5465-851d-1eb5a5fdbb9a",
    "subAccountId": "SubAccount-1",
    "timestamp": "2016-01-01T14:34:56.017Z",
    "source": "+6581968289",
    "destination": "+4534735477",
    "body": "Test MO message",
    "encoding": "GSM7",
    "smsCount": 2
  }
}

Chat Apps Business Management Updates use a different, flatter shape with no namespace or payload: eventId, timestamp, provider (WhatsApp or viber), businessAccountId, accountId, eventType and eventDetails. A receiver that handles both needs to check which shape it has before reading fields. Business Management Updates and inbound SMS are not on by default. The Chat Apps webhook API only adds the business management type (CABM) when you ask for it, and inbound SMS has to be activated with your 8x8 account manager.

8x8 does not publish a list of every eventType in one place, and WhatsApp group events and SMS Engage responses are not covered by the list above. Acknowledge any event type you do not recognise.

Inspect 8x8 webhooks as they arrive. Point an 8x8 Connect webhook at a Hookdeck Inspect URL to see the exact headers and envelope your account sends, with no account or setup required.

Setting up 8x8 webhooks

Generate a long random token first, for example with openssl rand -hex 32, and decide on the full header value you want 8x8 to send, such as Bearer <token>.

  1. Sign in to 8x8 Connect and open the Webhooks page.
  2. Click Add webhook.
  3. Choose the sub-account, or all sub-accounts, and the type: SMS or Messaging Apps.
  4. Enter your HTTPS endpoint URL.
  5. In HTTP Authorization, enter the full value, for example Bearer <token>. 8x8 sends it as the Authorization header without adding a prefix or encoding anything.
  6. Save, and check that the webhook's status is enabled.

The same settings are available through the Webhooks Configuration API, where the value goes in httpAuthorization and subAccountId can be * for every sub-account. The API masks the value as *** when you read webhooks back, so store it before you save.

To route through Hookdeck, create a Hookdeck source with the 8x8 type and use its URL as the webhook URL. The source has one field, Authorization Header: paste the HTTP Authorization value exactly as set in 8x8, including any Bearer or Basic prefix. Hookdeck compares the whole value. The OAuth authentication option is not supported, because 8x8 then sends a token it fetches from your token endpoint, which changes over time. The source accepts POST.

Securing 8x8 webhooks

Compare the whole Authorization header with the value you configured, in constant time. Do not strip the Bearer prefix, and do not base64-decode a Basic value: 8x8 sends the string you typed, so compare the string.

const crypto = require('crypto');
const express = require('express');

// The full HTTP Authorization value, e.g. "Bearer 9QQhlsEb8VY".
const EXPECTED = process.env.EIGHT_X_EIGHT_AUTHORIZATION;

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

app.post('/webhooks/8x8', express.raw({ type: '*/*' }), (req, res) => {
  const received = req.get('authorization');
  if (!EXPECTED || typeof received !== 'string' || !safeEqual(received, EXPECTED)) {
    return res.sendStatus(401);
  }

  let event;
  try {
    event = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Queue the event, then route on event.namespace and event.eventType in a worker.
  res.sendStatus(200);
});

The same check in Python:

import hmac
import os

EXPECTED = os.environ.get("EIGHT_X_EIGHT_AUTHORIZATION", "")  # e.g. "Bearer 9QQhlsEb8VY"


def verify_8x8_request(headers) -> bool:
    if not EXPECTED:
        return False  # never accept when unconfigured
    received = headers.get("authorization")
    if not received:
        return False
    # Compare bytes: compare_digest raises TypeError on a non-ASCII str.
    return hmac.compare_digest(received.encode("utf-8"), EXPECTED.encode("utf-8"))

A static Authorization value authenticates the sender, not the body. It cannot detect a modified payload, and with no signature or timestamp a captured request can be replayed and still pass. Use HTTPS so the value cannot be read in transit, use a long random token, and change it in 8x8 and in your environment together if it might have leaked. If you leave HTTP Authorization empty, 8x8 sends no Authorization header and there is nothing to check, so always set one.

8x8's WhatsApp reference tells receivers to check an X-8x8-Signature header "if provided", but no Connect page says when it is sent, what it covers or how to verify it. Until 8x8 documents it, treat the Authorization value as the credential.

Make 8x8 webhooks production-ready. Hookdeck Event Gateway checks the HTTP Authorization value on every 8x8 request, accepts each delivery straight away so 8x8's short retry window is never used up, and queues every message event durably.

8x8 webhook limitations and pain points

Four retries in about two minutes

The Problem: A deploy, a restart or a short outage on your side loses inbound messages and delivery receipts for good.

Why It Happens: 8x8 retries a failed delivery after 1, 10, 30 and 90 seconds. Once those attempts are used, the event is not sent again. 8x8's WhatsApp reference describes retries "with exponential backoff" instead, so the two pages disagree, but neither describes retries that last longer than a few minutes.

Workarounds:

  • Keep the receiving endpoint separate from the code that processes events, so a deploy of one does not take down the other.
  • Return 200 as soon as the event is stored, and process it afterwards.
  • For delivery status, reconcile against 8x8's APIs for messages whose receipts never arrived.

How Hookdeck Can Help: Hookdeck accepts each 8x8 request as it arrives and stores it, then delivers it to your service with retries on a schedule you choose. You can replay any event after an outage, so 8x8's retry window no longer decides what you keep.

Errors from your own checks are retried too

The Problem: A rotated token, a bug that returns 400, or a missing route produces a burst of identical requests, and then the events are gone.

Why It Happens: 8x8 treats any 4xx or 5xx response the same way as a timeout and retries it. A 401 from a stale credential is retried four times and then dropped, like any other failure.

Workarounds:

  • When you rotate the HTTP Authorization value, accept both the old and new values for a short overlap.
  • Log the header names that arrive (never the values) so you can see quickly whether Authorization is missing or just wrong.

How Hookdeck Can Help: Hookdeck's 8x8 source compares the value you paste. Hookdeck answers 8x8 with a 200 either way, but a request with a missing or wrong value is recorded as failed verification and never reaches your service, and you can see exactly what 8x8 sent.

Two payload shapes on Chat Apps

The Problem: A handler written for messaging events crashes on a template or account update, or the reverse.

Why It Happens: Messaging events nest their data under payload and carry a namespace. Business Management Updates put eventId, provider and eventDetails at the top level and have no payload.

Workarounds:

  • Check for payload or eventDetails before reading fields, and route on eventType.
  • Give business management events their own webhook URL if you subscribe to them.

How Hookdeck Can Help: Hookdeck filters can route events by eventType or by the presence of namespace to separate destinations, and transformations can normalise the two shapes before they reach your service.

No documented sender IPs or signature

The Problem: You cannot allowlist 8x8's addresses or verify that a body is unchanged.

Why It Happens: 8x8 publishes no egress IP list for SMS or Chat Apps webhooks, and the only documented credential for Connect webhooks is the static Authorization value.

Workarounds:

  • Rely on the Authorization value over HTTPS, and keep it out of logs.
  • For actions with real consequences, treat the webhook as a notification and confirm the message status through 8x8's API.

How Hookdeck Can Help: Hookdeck gives you a stable HTTPS URL to put in 8x8 Connect and checks the Authorization value before anything reaches your service, so your own endpoint can sit behind your firewall.

Best practices

Store the full Authorization value

Keep Bearer <token>, or your exact Basic value, as one string and compare it whole. 8x8 masks the value in API responses, so record it when you set it.

Deduplicate on the message and its status

Retries resend the same event. For messaging events, key on payload.umid plus payload.status.state for receipts, or payload.umid alone for inbound messages. For business management events, key on eventId. See our guide to webhook idempotency.

Do not depend on order

8x8 does not document an ordering guarantee, and a retried receipt can arrive after a newer one. Store each status with its payload.status.timestamp and only move a message's state forward.

Acknowledge fast and process asynchronously

With four retries in about two minutes, a slow handler turns into lost events. Verify the header, store the event, return 200, and do the work afterwards. See why to process webhooks asynchronously.

Conclusion

8x8 Connect sends SMS and Chat Apps events as unsigned JSON POST requests, authenticated only by the HTTP Authorization value you set on the webhook. Compare that whole value in constant time over HTTPS, handle both the messaging and business management shapes, deduplicate on umid or eventId, and plan for a retry schedule that ends after about two minutes.

Hookdeck Event Gateway checks the header, accepts each 8x8 request immediately, and keeps every event until your service has processed it, so a restart on your side does not cost you inbound messages or delivery receipts.

Get started with Hookdeck for free and handle 8x8 webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.