Skip to main content
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.
sandbox is a system-managed mode for shared Wassist test numbers (numbers with no WhatsApp Business Account). It lets you chat with yourself for testing. You cannot set sandbox from the API or dashboard; any attempt to do so returns 400. Sandbox numbers also ignore per-conversation routing overrides.

How effective routing is resolved

In code terms:
  1. If the number is a sandbox number → sandbox, full stop.
  2. Otherwise, if the conversation has its own non-null routing, use it.
  3. 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 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, with two extra fields:
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 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.
  • Any routing change on a sandbox number is rejected (the number is shared and system-managed).
  • webhookId must reference a webhook owned by the requesting user.
  • webhookId is required when (and only when) mode === 'webhook'.