Agent skill
8x8 Webhooks Skill
Receive and authenticate 8x8 Connect webhooks. Use when setting up an 8x8 webhook endpoint, checking the optional HTTP Authorization value (Bearer or Basic), debugging 401s, or handling inbound_message_received, outbound_message_status_changed, short_url_clicked and template_status_update events from 8x8 SMS and Chat Apps (WhatsApp).
Install this skill
npx skills add hookdeck/webhook-skills --skill 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
Authorizationheader only) - Why does my 8x8 webhook endpoint return 401?
- How do I handle
inbound_message_receivedoroutbound_events?message_ status_ changed
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:
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.
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/, examples/nextjs/, 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_ | 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_ | WhatsApp detects a more correct category for a template |
phone_ | A WhatsApp phone number's quality changes |
account_update | Your WhatsApp Business Account changes (eventDetails.event refines it, e.g. ACCOUNT_VIOLATION) |
whatsapp_ | 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
EIGHT_X_EIGHT_AUTHORIZATION=Bearer 9QQhlsEb8VY # full value, prefix included
Local Development
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 - Webhook kinds, event types, payload fields
- references/setup.md - Creating the webhook and HTTP Authorization value in 8x8
- references/verification.md - Header check in Node, Python and Next.js, gotchas
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests