Skip to main content
New to webhooks? Get a reply working on the sandbox with the webhook routing hello world, or deploy a ready-made receiver.
Every Wassist phone number has a default routing mode, and every conversation can optionally override it. The resolved mode — the effective routing — decides what happens when a customer sends a message.

The routing modes

“No routing” is represented as null on the wire — there is no "none" string value. A number or conversation with defaultRouting: null (or routing: null on the conversation) simply stores incoming messages and does nothing else.

The sandbox number

The shared Wassist sandbox number (the one with no WhatsApp Business Account) supports agent, webhook and null too, but routing is per user: a change only applies to your own conversation with the sandbox, never to other people’s. That makes it the quickest way to try either mode. See the managed agent and webhook routing quickstarts.
  • Set sandbox routing from the dashboard: Numbers → sandbox number → Routing. The phone-number endpoints also work with a signed-in user session, but API keys get 400. A key belongs to the organization, not a person, so it has no sandbox conversation to route.
  • Sandbox conversations belong to your personal organization. Create the agent or webhook there, and keep it as the active organization when you change sandbox routing.
  • applyToExisting is ignored and no lifecycle events fire.
  • The service-window events aren’t sent for sandbox conversations.

How effective routing is resolved

In code terms:
  1. If the conversation has its own non-null routing, use it.
  2. Otherwise, fall back to the number’s defaultRouting (which itself may be null — meaning “no routing”).
The same precedence applies to the webhook itself: conversation.webhookOverride wins over whatsappNumber.defaultWebhook.

Subscription lifecycle events

Whenever a conversation’s effective (routing, webhook) pair transitions to or away from a webhook, Wassist fires lifecycle events to the affected webhook: The triggers are:
Phone-number-level routing changes (the /phone-numbers/{n}/subscribe, /connect-agent, and /unsubscribe endpoints) do not fire subscription lifecycle events, even when applyToExisting is true. Number-wide changes are bulk admin actions — if your service needs to know about every conversation, subscribe to it individually.

Inbound message dispatch

Once a conversation is in webhook routing:
  • subscription.message.received is dispatched only to the assigned webhook (no fan-out).
  • The webhook must have the Subscription message received event enabled (it is by default). If it’s unticked, routed messages are dropped without a delivery.
  • The agent pipeline is skipped entirely.
  • The legacy message.received event still fan-outs to every active webhook subscribed to it, so existing integrations keep working.

Service window lifecycle

WhatsApp’s 24-hour customer service window is tracked per conversation in webhook mode:
  • subscription.service_window.expiring fires roughly 1 hour before the window closes for a given conversation, once per window.
  • subscription.service_window.closed fires when the window has closed.
  • The tracker resets the moment a new inbound user message lands.

Event payloads

All subscription.* events use the same envelope as message.received, plus routing and webhookId:
For lifecycle events (subscription.activated, .revoked, the service window pair) the message field is null — only conversation context is sent.

Setting routing from your code

To set any mode on a conversation (for example, hand it back to the agent), call the general-purpose endpoint directly. agent needs an agentId, webhook needs a webhookId, and null (or "inherit") clears the override:
To change the number-wide default, do it from the dashboard or via the three dedicated phone-number endpoints. Each one atomically sets the routing mode and clears the unrelated FK (agent vs. webhook), so the number can’t end up in an inconsistent state:
applyToExisting: true materialises the change onto every existing conversation on this number — activeAgent is overwritten with the new default (or cleared, for subscribe / unsubscribe) and any in-flight session is dropped. No subscription lifecycle events are dispatched.
Webhooks must be created in the dashboard. There is no API for webhook creation — the routing endpoints only let you point conversations at webhooks that already exist.

Validation rules

  • mode='sandbox' is rejected on every public endpoint.
  • Routing changes on a sandbox number need a signed-in user; API keys get 400. The change only touches that user’s own sandbox conversation.
  • On your own numbers, only the organization that owns the WhatsApp Business Account can change routing (403 otherwise).
  • webhookId must reference a webhook in your organization.
  • webhookId is required when (and only when) mode === 'webhook'.