Webhook routing lets you define fine-grained rules that determine where the Conversation API should deliver specific callbacks. With routing enabled, a single Conversation API app can send different callback types (for example, inbound messages vs. delivery reports) to different URLs based on message content, sender, channel, or status.
This page explains how webhook routing works, how to configure webhook routing, and provides ready-to-use examples.
Webhook routing is currently available for open beta usage. Interfaces and behavior may change before general availability.
During the open beta, sender_id_filters matching is only supported for the SMS and MMS channels. For all other channels, the sender_id field is empty in callbacks, and any routing rule using sender_id_filters will not match. Support for additional channels may be added in the future.
Note the following important pieces of information about webhook routing:
- Webhook routing is configured per webhook in the
routing_configurationfield. - Webhook routing currently supports routing for the following triggers:
MESSAGE_INBOUND(inbound contact messages, also called MO)MESSAGE_DELIVERY(delivery reports for app messages, also called DR)
- When no rule matches, you can either send the callback to the webhook's default
targetor discard it, based onfallback_behavior. - You can optionally specify a per-rule HMAC
secretto override the webhook's default secret for signature verification.
More specific information is provided throughout the rest of this document.
At runtime, the Conversation API evaluates routing rules only for webhooks that both:
- Subscribe to the relevant trigger (for example,
MESSAGE_INBOUNDorMESSAGE_DELIVERY), and - Have
routing_configurationdefined.
For an incoming callback, the ruleset is selected by callback type:
- Inbound message (MO) →
routing_configuration.message_inbound_rules - Delivery report (DR) →
routing_configuration.message_delivery_rules
The rule and filtering logic is described below:
- Rule Precedence: Rules are evaluated in the order they are listed. Once a rule matches, evaluation stops (first matched rule wins).
- AND/OR Semantics:
- Inbound Rules: Filters within a rule (
keyword_filterANDsender_id_filtersANDchannel_filter) use AND logic. All specified filters must match. - Inbound Sender ID Filters: If multiple IDs are specified in
sender_id_filters, it uses OR logic. - Delivery Rules: Both
sender_id_filtersANDchannel_status_filtersmust match. - Delivery Channel/Status Filters: Any one entry in the
channel_status_filterslist can match (OR logic). Within an entry, thechannelAND one of thestatusesmust match (AND logic).
- Inbound Rules: Filters within a rule (
- Fallback: If no rules match, the
fallback_behaviordetermines the outcome:FALLBACK_TO_DEFAULT(send to webhook target) orDISCARD(drop callback).
If a route rule defines a non-blank secret, that secret is used to sign the outbound callback. Otherwise, the webhook's own secret is used if present. If both the rule-level and webhook-level secrets are empty, callbacks are sent unsigned.
Route-specific secrets take precedence over webhook-level secrets. The receiving endpoint must use the rule-specific secret when validating signatures; using the default webhook secret will fail verification when a route secret exists.
For more information on signature verification, see the HMAC verification guide. Routed webhook endpoints must verify signatures to ensure the authenticity of the callbacks.
The routing_configuration contains two optional rule groups:
message_inbound_rules— Rules for inbound messages (MO)message_delivery_rules— Rules for delivery reports (DR)
Below are the fields common to both message_inbound_rules and message_delivery_rules:
rules— ordered list of routing rules. Each rule has:name(required): A descriptive name for the rule.target_url(required): Where to send matching callbacks. We recommend using HTTPS for all webhook routing targets as callback payloads may contain sensitive message data and should be protected in transit.match_condition(required): Fields that must match.secret(optional): Per-rule HMAC secret.
fallback_behavior— What to do when no rule matches:FALLBACK_TO_DEFAULT(default) → Send to webhooktarget.DISCARD→ Drop the callback (no outbound call).
Match condition fields (InboundMatchCondition):
keyword_filter(optional): Case-insensitive match on the first word of extracted message text. Blank/empty means "match any text".channel_filter(optional): Restrict to a specific channel;CHANNEL_UNSPECIFIEDmeans "any channel".sender_id_filters(optional, repeated): OR across entries. Empty means "any sender". Note that during the open beta, this only applies to the SMS and MMS channels.
Keyword filtering in webhook routing is for directing callbacks to specific endpoints and should not be confused with Consent management. Consent management is a separate feature for managing user opt-ins and opt-outs. For more details, see Consent management.
Keyword matching is performed by extracting text from the following ContactMessage payload fields:
text_message.textmedia_card_message.captionfallback_message.raw_messagechoice_response_message.postback_dataproduct_response_message.textlocation_message.title(falls back tolabelif title is blank)
The keyword_filter only matches the first word of the extracted text. For example, "Help" will match, but "Please help" will not.
Match condition fields (DeliveryMatchCondition):
sender_id_filters(optional, repeated): OR across entries. Empty means "any sender".channel_status_filters(optional, repeated): OR across entries. Each entry (ChannelStatusFilter) uses:channel(optional): specific channel;CHANNEL_UNSPECIFIEDmeans "any channel".statuses(optional, repeated): empty means "any status".
During the open beta, sender_id_filters only supports the SMS and MMS channels. For all other channels, the sender_id value is empty in callbacks, so rules using this filter will not match for those channels.
Below are the limitations and validation constraints enforced by the webhook routing functionality:
- Max Rules: Up to 10 rules per ruleset.
- Mandatory Fields: Every rule must have a
name,target_url, andmatch_condition. - Match Condition: Must contain at least one non-empty filter.
- Lengths:
secretmax 1024 chars,target_urlmax 742 chars.
The sections below provide copy-and-paste-ready YAML and JSON snippets illustrating common routing patterns.
The sub-sections below provide simple single-filter example snippets.
Matches any incoming callback of the respective type. Since the API requires at least one non-empty filter, we use a "match-all" value like CHANNEL_UNSPECIFIED.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: ROUTE_ALL_MO
target_url: https://example.com/mo/all
match_condition:
channel_filter: CHANNEL_UNSPECIFIED- Matches: Any inbound message from any channel (WhatsApp, SMS, RCS, etc.).
- Does not match: Delivery reports (because this is in
message_inbound_rules). - Why it is useful: Directs all incoming traffic of a specific type to a dedicated processing service.
Filters callbacks by a specific channel.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: SMS_ONLY
target_url: https://example.com/mo/sms
match_condition:
channel_filter: SMS- Matches: Any inbound message where the channel is SMS.
- Does not match: Messages from WhatsApp, Viber, RCS, or any other channel.
- Why it is useful: Separating logic for legacy channels like SMS from modern IP-based channels.
Routes messages based on the first word of the message text.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: KEYWORD_HELP
target_url: https://example.com/mo/help
match_condition:
keyword_filter: "help"- Matches: Messages like "Help", "HELP me", or "help! please" (case-insensitive).
- Does not match: "Please help" (the keyword must be the first word), or "helper" (exact match on the first word is required).
- Why it is useful: Directing support-related keywords to a helpdesk system.
Filters by the originator identity. This is currently supported for SMS and MMS only.
YAML
routing_configuration:
message_delivery_rules:
rules:
- name: SENDER_SINCH_SD
target_url: https://example.com/dr/sinch-sd
match_condition:
sender_id_filters: ["SinchSD"]- Matches: Any delivery report for messages sent with the "SinchSD" sender ID.
- Does not match: Delivery reports for any other sender ID.
- Why it is useful: Tracking delivery performance for a specific brand or campaign identified by a unique sender ID.
The sub-sections below provide more complex rulesets that feature multiple filters.
Combines a channel filter with specific originator identities.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: SMS_SENDER_12345
target_url: https://example.com/mo/sms-12345
match_condition:
channel_filter: SMS
sender_id_filters: ["12345"]- Matches: Inbound SMS messages received on short code "12345".
- Does not match: Inbound SMS messages to other numbers, or MMS messages to "12345".
- Why it is useful: Isolating traffic for a specific short code or long code within your SMS application.
Routes messages based on both the arrival channel and the starting word.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: WA_START
target_url: https://example.com/mo/wa-onboarding
match_condition:
channel_filter: WHATSAPP
keyword_filter: "start"- Matches: WhatsApp messages that start with the word "start" (e.g., "Start my trial", "START").
- Does not match: SMS messages starting with "start", or WhatsApp messages starting with "hello".
- Why it is useful: Triggering different onboarding flows based on the channel the user chose to engage on.
Filters by originator identity and a specific keyword.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: SMS_67890_STOP
target_url: https://example.com/mo/sms-stop
match_condition:
keyword_filter: "stop"
sender_id_filters: ["67890"]- Matches: SMS messages starting with "stop" sent to sender ID "67890".
- Does not match: Messages with "stop" sent to other numbers, or "JOIN" sent to "67890".
- Why it is useful: Managing opt-outs for specific marketing campaigns where each campaign uses a distinct short code.
Demonstrates the most granular MO routing possible.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: SMS_SENDER_HELP
target_url: https://example.com/mo/sms-help-special
match_condition:
channel_filter: SMS
sender_id_filters: ["12345"]
keyword_filter: "help"- Matches: Only SMS messages received on "12345" that start with the word "help".
- Does not match: MMS messages to "12345" with "help", or SMS messages to "12345" with "support".
- Why it is useful: Precise handling of specific customer interactions on a shared or multi-purpose sender identity.
Using CHANNEL_UNSPECIFIED while filtering by sender ID allows matching across any channel that supports sender identities.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: SENDER_123_ANY_CHANNEL
target_url: https://example.com/mo/sender-123
match_condition:
channel_filter: CHANNEL_UNSPECIFIED
sender_id_filters: ["123"]- Matches: Inbound messages from sender "123" on any supported channel (currently SMS/MMS).
- Does not match: Messages from other sender IDs, or messages on channels that do not provide a
sender_id(like WhatsApp). - Why it is useful: Simplified routing for a brand identity that might be reachable via multiple traditional messaging protocols.
Using OR logic for multiple channel/status combinations in delivery reports.
YAML
routing_configuration:
message_delivery_rules:
rules:
- name: CRITICAL_DR
target_url: https://example.com/dr/critical
match_condition:
channel_status_filters:
- channel: WHATSAPP
statuses: [FAILED]
- channel: SMS
statuses: [DELIVERED, FAILED]- Matches: WhatsApp FAILED reports, OR SMS DELIVERED/FAILED reports.
- Does not match: WhatsApp DELIVERED or READ reports.
- Why it is useful: Concentrating all terminal or critical delivery updates in one endpoint.
The following examples demonstrate how to combine rules to handle common messaging scenarios.
This example shows how to prioritize specific traffic (e.g., from a VIP sender) while still having a general catch-all for other SMS traffic.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: VIP_PRIORITY
target_url: https://example.com/mo/priority
match_condition:
sender_id_filters: ["VIP_SENDER"]
- name: SMS_GENERAL
target_url: https://example.com/mo/sms
match_condition:
channel_filter: SMS
fallback_behavior: FALLBACK_TO_DEFAULT- Matches:
- A message from
VIP_SENDERvia SMS matches the first rule and goes to/priority. - A message from
OTHER_SENDER(or any other sender that isn'tVIP_SENDER) via SMS fails the first rule, matches the second rule, and goes to/sms.
- A message from
- Does not match: A message from
VIP_SENDERvia WhatsApp would fail the first rule (becausesender_idis empty for WhatsApp) and fail the second rule (not SMS). It would then usefallback_behavior. - Why it is useful: Implementing tiered service levels or special handling for high-value originators.
When no rules in a ruleset match a callback, the fallback_behavior determines if the callback is sent to the default target or discarded.
YAML
routing_configuration:
message_delivery_rules:
rules:
- name: FAILURES_ONLY
target_url: https://example.com/dr/errors
match_condition:
channel_status_filters:
- channel: CHANNEL_UNSPECIFIED
statuses: [FAILED]
fallback_behavior: DISCARD- Matches: A delivery report with status
FAILEDmatches the rule and is sent to/errors. - Does not match: A delivery report with status
DELIVEREDdoes not match the rule. Becausefallback_behaviorisDISCARD, the callback is dropped and not sent anywhere. - Why it is useful: Reducing noise on your main callback endpoint by only receiving the specific events you care about (e.g., errors) and discarding everything else.
The following sections provide examples highlighting contextualized use cases.
A company uses the same Conversation API App for both marketing (SMS via Short Code) and transactional (WhatsApp) messages. They want to process responses in different backend systems.
YAML
app_id: "{{APP_ID}}"
target: "https://example.com/default"
triggers: ["MESSAGE_INBOUND", "MESSAGE_DELIVERY"]
routing_configuration:
message_inbound_rules:
rules:
- name: MARKETING_SMS_MO
target_url: https://marketing-system.example.com/callbacks
match_condition:
channel_filter: SMS
sender_id_filters: ["67890"]
- name: TRANSACTIONAL_WA_MO
target_url: https://transactional-system.example.com/wa-inbound
match_condition:
channel_filter: WHATSAPP
message_delivery_rules:
rules:
- name: WA_READ_RECEIPTS
target_url: https://transactional-system.example.com/wa-dr
match_condition:
channel_status_filters:
- channel: WHATSAPP
statuses: [READ]
fallback_behavior: DISCARDYou might want to route sensitive callbacks to a highly secure endpoint that uses a different HMAC secret for validation.
Do not use this code snippet without modifying the example secret value.
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: SECURE_AUTH_FLOW
target_url: https://secure.example.com/auth-otp
secret: "highly-secret-hmac-key-123"
match_condition:
keyword_filter: "code"
channel_filter: SMSIf you encounter any obstacles, review the following troubleshooting tips:
If no callbacks are being routed:
- Ensure that the corresponding webhook is subscribed to
MESSAGE_INBOUNDand/orMESSAGE_DELIVERYtriggers. - Check if
routing_configurationis correctly defined in the webhook object. - Ensure your rules are defined correctly.
Consider the example and corresponding scenarios below:
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: SMS_HELP
target_url: https://example.com/mo/help
match_condition:
channel_filter: SMS
keyword_filter: "help"- Callback Scenario 1: The webhook(s) you are trying to route are not subscribed to either the
MESSAGE_INBOUNDorMESSAGE_DELIVERYtriggers.- Result: No match. The webhook(s) must be subscribed to either the
MESSAGE_INBOUNDand/orMESSAGE_DELIVERYtriggers.
- Result: No match. The webhook(s) must be subscribed to either the
- Callback Scenario 2: A WhatsApp message with text "help".
- Result: No match.
channel_filteris SMS.
- Result: No match.
- Callback Scenario 3: An SMS message with text "please help".
- Result: No match.
keyword_filtermatches only the first word.
- Result: No match.
- Callback Scenario 4: An SMS message with text "help me".
- Result: Match. The first word of the SMS message is "help".
Reviewing your webhook and rule configuration helps in debugging why messages are falling back to the default target or being discarded.
If a specific rule isn't matching:
- Verify rule order. A broad rule earlier in the list might be "stealing" the callbacks.
- Remember
keyword_filtermatches only the first word. - During open beta,
sender_id_filtersonly works for SMS and MMS.
The following example shows how the "first match wins" logic works:
YAML
routing_configuration:
message_inbound_rules:
rules:
- name: PRIORITY_SENDER
target_url: https://example.com/mo/priority
match_condition:
sender_id_filters: ["VIP_SENDER"]
- name: GENERAL_SMS
target_url: https://example.com/mo/general-sms
match_condition:
channel_filter: SMS
fallback_behavior: FALLBACK_TO_DEFAULTEvaluation:
- If a message comes from
VIP_SENDERvia SMS, it matches the first rule and goes to/priority. It never reaches the second rule. - If a message comes from
OTHER_SENDERvia SMS, it fails the first rule, matches the second, and goes to/general-sms. - If a message comes from
OTHER_SENDERvia WhatsApp, it fails both rules and falls back to the default webhook target.
Rule order is useful when implementing "special handling" for certain senders or conditions while having a catch-all for the rest of the channel traffic. Ensure that your ordering is well-defined so that all webhooks are routed correctly.
Keyword matching is only performed on specific fields (see the list of supported payloads). If your message is an Interactive Message or a Contact/Location message without a title, it might not match as expected.
- Callbacks overview and management: Conversation API callbacks
- Opt-in/opt-out keyword analysis: Consent management