Skip to main content
Create a WhatsApp webhook endpoint with a 60db API key scoped for WhatsApp. Owner, admin and developer roles may manage subscriptions on an active paid plan.

Manage subscriptions

The URL must use public HTTPS on port 443. Credentials, fragments and destinations resolving to private or internal addresses are rejected. Redirects are not followed. The creation response includes data.signing_secret once. Save it on your receiving server. List responses never include this secret. The dashboard’s WhatsApp → Webhooks form offers message, status, connection and assistant-failure selection. Select at least one event. Connection alerts and assistant failures are optional; they are not added to the default message/status/connected selection. Save or acknowledge the one-time secret before creating another endpoint. The additional number.updated, message.mirrored and message.updated events are currently selectable through the API.

Event envelope

This is an illustrative envelope; the message object also carries its content, status and timestamps. message.status also reports local dispatch failures, cancellation and unconfirmed sends, including broadcast messages. These terminal dispatch state changes and their events are persisted together. Internal request hashes are omitted from the message payload. A cancellation may leave an interrupted send unconfirmed; no automatic resend follows. Subscribe to the number lifecycle events explicitly through the API or dashboard. Stopping a number and recording its normalized event occur in the same transaction. An old token failure does not produce a revocation event for a successfully replaced token. Subscribe to number.updated through the API for supported account review/disable/deletion/offboarding, verified partner app/portfolio changes, policy violation/restriction, verified-name and quality changes. Its field identifies the native provider signal. Ordinarily number.provider_state[field].at records its epoch-second timestamp; ACCOUNT_VIOLATION and ACCOUNT_RESTRICTION both use field: "account_update" and store independent state in account_violation and account_restriction. Restriction entries retain their own at, expiry and optional remediation, so a later unrelated account notice cannot erase them. Multiple restriction changes in one receipt can each produce a distinct updated snapshot. These metadata changes and events commit with the provider receipt. A terminal event may produce both number.action_required and number.updated; warning/temporary restriction records do not clear credentials or emit number.action_required. Account reinstatement/reconnection and app/partner-added notices never emit number.connected or restart paused work. Later supported notices in the same receipt persist after its stop without admitting new inbound messages. See number resources for supported action guards, errors and required partner binding. Unknown variants are ignored; these events do not import coexistence history or certify arbitrary provider asset/app-review changes. Separate verified Business app callbacks now mirror supported history, contacts and app messages. number.updated with field: "history" carries current import progress or declined sharing in number.coexistence_state.history. Historical originals do not emit message.received or start AI. message.mirrored represents a new app-originated outbound message and a human handoff; message.updated replaces the displayed record after media hydration, edit or irreversible revocation. Its message has app_updated_at/app_revoked when applicable. Internal request hashes remain omitted. Receipt, message/state and normalized event persistence are atomic; deduplicate event IDs and refetch records when callbacks arrive out of order. Callback scope and unfinished onboarding number.updated with field: "sync" carries durable contact/history initiation transitions in number.coexistence_state.sync. accepted retains the support request ID but does not certify sharing/import completion; unconfirmed is never automatically dispatched again. A caller should refetch the number’s current state rather than assume delivery order. Flow-specific Business app offboarding can also record coexistence_state.offboarded_at with an account update; it cannot stop an ordinary or differently bound number. Initiation states and recovery

Verify the signature

Headers include X-60db-Event-ID, X-60db-Timestamp, X-60db-Signature and X-60db-Delivery-Attempt. The signature is v1= followed by the hexadecimal HMAC-SHA256 of timestamp + ”.” + raw request body, using the signing secret’s UTF-8 bytes. Do not decode the hexadecimal-looking secret into binary and do not parse/re-serialize the body before verification.
Use a five-minute timestamp tolerance and deduplicate by event id. Persist the verified event before acknowledging it with a 2xx response. Process your application’s work asynchronously after acknowledgement.

Retries

Timeouts, network errors, HTTP 408, 425, 429 and server errors retry with exponential delay, for up to eight attempts. Other 4xx responses are terminal. Delivery can be repeated after a worker restart, so processing must be idempotent. Ordering is not guaranteed. This signing secret is for 60db → your application deliveries. Meta → 60db receipt verification uses the platform’s Meta app secret and is handled by 60db.