Agent skill

Clickatell Webhooks Skill

Receive and authenticate Clickatell One API (WhatsApp and SMS) callbacks. Use when setting up a Clickatell webhook endpoint, adding a Custom Security Header to the callback URL, debugging 401s, or handling messageStatusUpdate, moText, moMedia and other One API callback events.

Install this skill

npx skills add hookdeck/webhook-skills --skill clickatell-webhooks


Clickatell's One API (WhatsApp and SMS) posts JSON callbacks to your server: delivery status updates to a status callback URL and inbound messages (replies) to a reply callback URL. Callbacks are not signed. You authenticate them with a secret header that you add yourself as a Custom Security Header on the callback URL.

Hookdeck's Clickatell source shows this setup note: "Covers One API callbacks only. Add a Custom Security Header to the callback URL."

Basic Auth alone and legacy SMS Gateway callbacks are not covered by Hookdeck's Clickatell source.

When to Use This Skill

  • How do I receive Clickatell One API webhooks (callbacks)?
  • How do I authenticate Clickatell callbacks? (No signature: use a Custom Security Header)
  • How do I handle messageStatusUpdate, moText or moMedia events?
  • Why does my Clickatell callback endpoint return 401?
  • How do I tell status callbacks from reply callbacks?

Verification (core)

Add a Custom Security Header to the callback URL in the Clickatell portal, then compare the whole header value with the value you stored. The header name is whatever you chose (CLICKATELL_WEBHOOK_HEADER, default Authorization). Do not strip or parse a Bearer prefix: the expected value is exactly what you typed, prefix included.

const crypto = require('crypto');

function verifyClickatellRequest(headers, {
  name = process.env.CLICKATELL_WEBHOOK_HEADER || 'Authorization',
  expected = process.env.CLICKATELL_WEBHOOK_SECRET,
} = {}) {
  if (!expected) return false;                       // never accept when unconfigured
  const received = headers[name.toLowerCase()];      // Node lowercases header names
  if (!received) return false;
  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
  • Return 401 when the header is missing or does not match, 400 for a body that isn't a JSON object, and any 2xx once handled (any 2xx acknowledges the callback).
  • Read the body raw (express.raw({ type: '*/*' })) so any content type works, and authenticate before parsing it.
  • A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS and rotate the header value in the portal if it leaks.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Event Types

The body is { integrationId, integrationName, event: { <kind>: [ ... ] } }. The event kind is the name of the key inside event; each value is an array of events. Dispatch on that key.

Kind (key in event)CallbackSent When
messageStatusUpdateStatusA message's status changes (e.g. DEVICE_ACK)
messageFallbackStatusA message falls back to another channel
statusIsTypingStatusThe user is typing
moTextReplyAn inbound text message
moMediaReplyAn inbound image, file or other media
moLocationReplyAn inbound location
moContactsReplyInbound contact cards
moSuggestedResponseReplyA suggested-reply button is tapped
moListPickerResponseReplyA list item is selected
moButtonResponseReplyA button is tapped

Clickatell adds new kinds and fields over time, so acknowledge kinds you don't handle. Events can be duplicated (retries) or arrive out of order: deduplicate on messageId and order by timestamp (milliseconds).

Environment Variables

CLICKATELL_WEBHOOK_HEADER=Authorization   # name of the Custom Security Header (default: Authorization)
CLICKATELL_WEBHOOK_SECRET=your-header-value  # the exact, whole header value, e.g. "Bearer abc123"

Local Development

Clickatell can't reach localhost. Tunnel with the Hookdeck CLI (no account required):

npx hookdeck-cli listen 3000 clickatell --path /webhooks/clickatell

Use the printed URL as the callback URL in Clickatell.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 10, 2026

View on GitHub →