# 8x8 Webhooks

8x8 Connect (CPaaS) sends webhooks for inbound SMS, SMS and Chat Apps delivery receipts,
short URL clicks and WhatsApp/Viber template updates. Payloads are **not signed**. The only
authentication is an optional static **HTTP Authorization** value you set on the webhook in
8x8, which 8x8 sends back as the request's `Authorization` header.

## When to Use This Skill

* How do I receive 8x8 webhooks?
* How do I verify the 8x8 HTTP Authorization header (Bearer or Basic)?
* Does 8x8 sign webhook payloads? (No: shared credential in the `Authorization` header only)
* Why does my 8x8 webhook endpoint return 401?
* How do I handle `inbound_message_received` or `outbound_message_status_changed` events?

## Verification (core)

When you add a webhook in 8x8 Connect (portal or Webhooks Configuration API, field
`httpAuthorization`), you can set an HTTP Authorization value such as `Bearer 9QQhlsEb8VY` or
`Basic dXNlcjpwYXNzd29yZA==`. Paste it **exactly as set in 8x8, including any `Bearer ` or
`Basic ` prefix**. OAuth authentication is not supported. Compare the **whole
`Authorization` header** to that value. Do not strip, parse or base64-decode it, for Basic as
well as Bearer:

```javascript
const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verify8x8Request(headers, expected = process.env.EIGHT_X_EIGHT_AUTHORIZATION) {
  if (!expected) return false;                  // never accept when unconfigured
  const received = headers['authorization'];    // full value, e.g. "Bearer 9QQhlsEb8VY"
  return Boolean(received) && safeEqual(received, expected);
}

```

Return `401` when this fails, `400` for a body that isn't a JSON object, and `200` once
handled. Read the body raw (`express.raw({ type: '*/*' })`) so any content type works, and
parse it only after the check. Python and Next.js equivalents are in
[references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/8x8-webhooks/references/verification.md).

**A static shared credential authenticates the sender, not the body, and gives no replay
protection** (no signature, no timestamp). Use HTTPS and rotate the credential in 8x8 if it
leaks.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/8x8-webhooks/examples/express/),
> [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/8x8-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/8x8-webhooks/examples/fastapi/).

## Event Types

The body is JSON with an `eventType` string:

| `eventType` | Sent when |
| --- | --- |
| `inbound_message_received` | An SMS or Chat Apps/WhatsApp message arrives for your number |
| `outbound_message_status_changed` | A delivery receipt: `payload.status.state` is `queued`, `sent`, `delivered`, `undelivered`, `rejected` or `read` |
| `external_app_message` | A message sent from the WhatsApp Business App (Chat Apps DR v9) |
| `short_url_clicked` | A recipient clicks a shortened link in your SMS |
| `template_status_update` | A WhatsApp or Viber template is approved, rejected, paused and so on |
| `template_category_update` | A WhatsApp template's category changes |
| `template_quality_update` | A WhatsApp template's quality score changes |
| `template_correct_category_detection` | WhatsApp detects a more correct category for a template |
| `phone_number_quality_update` | A WhatsApp phone number's quality changes |
| `account_update` | Your WhatsApp Business Account changes (`eventDetails.event` refines it, e.g. `ACCOUNT_VIOLATION`) |
| `whatsapp_automatic_events` | Meta detects a purchase or lead conversion in a thread from a Click-to-WhatsApp ad |

Failed deliveries (connection error, timeout, 4xx or 5xx) are retried after 1, 10, 30 and 90
seconds, so dedupe on `payload.umid` plus `payload.status.state` (or `eventId`).

## Not Covered

* The OAuth client-credentials option (rotating tokens)
* The separate 8x8 Contact Center Chat `x-8x8-signature` (JWS) webhooks

## Environment Variables

```bash
EIGHT_X_EIGHT_AUTHORIZATION=Bearer 9QQhlsEb8VY   # full value, prefix included

```

## Local Development

```bash
npx hookdeck-cli listen 3000 8x8 --path /webhooks/8x8

```

No account required. Use the printed URL as the webhook URL in 8x8 Connect.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/8x8-webhooks/references/overview.md) - Webhook kinds, event types, payload fields
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/8x8-webhooks/references/setup.md) - Creating the webhook and HTTP Authorization value in 8x8
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/8x8-webhooks/references/verification.md) - Header check in Node, Python and Next.js, gotchas
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/8x8-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests