Skip to content

Webhook routing

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.

Open beta

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.

Important information

Note the following important pieces of information about webhook routing:

  • Webhook routing is configured per webhook in the routing_configuration field.
  • 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 target or discard it, based on fallback_behavior.
  • You can optionally specify a per-rule HMAC secret to override the webhook's default secret for signature verification.

More specific information is provided throughout the rest of this document.

How Rule Matching Works

At runtime, the Conversation API evaluates routing rules only for webhooks that both:

  1. Subscribe to the relevant trigger (for example, MESSAGE_INBOUND or MESSAGE_DELIVERY), and
  2. Have routing_configuration defined.

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

Rules and filter logic

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_filter AND sender_id_filters AND channel_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_filters AND channel_status_filters must match.
    • Delivery Channel/Status Filters: Any one entry in the channel_status_filters list can match (OR logic). Within an entry, the channel AND one of the statuses must match (AND logic).
  • Fallback: If no rules match, the fallback_behavior determines the outcome: FALLBACK_TO_DEFAULT (send to webhook target) or DISCARD (drop callback).
Secrets and HMAC signing

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.

Configuration Reference

The routing_configuration contains two optional rule groups:

  • message_inbound_rules — Rules for inbound messages (MO)
  • message_delivery_rules — Rules for delivery reports (DR)

Common Fields

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 webhook target.
    • DISCARD → Drop the callback (no outbound call).

Inbound Messages (MO) — message_inbound_rules

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_UNSPECIFIED means "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 vs. Consent management

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.

Supported Payloads for Keyword Matching

Keyword matching is performed by extracting text from the following ContactMessage payload fields:

  • text_message.text
  • media_card_message.caption
  • fallback_message.raw_message
  • choice_response_message.postback_data
  • product_response_message.text
  • location_message.title (falls back to label if title is blank)
Keyword Match Limitation

The keyword_filter only matches the first word of the extracted text. For example, "Help" will match, but "Please help" will not.

Delivery Reports (DR) — message_delivery_rules

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_UNSPECIFIED means "any channel".
    • statuses (optional, repeated): empty means "any status".
Sender IDs Support

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.

Limits and Validation

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, and match_condition.
  • Match Condition: Must contain at least one non-empty filter.
  • Lengths: secret max 1024 chars, target_url max 742 chars.

Examples

The sections below provide copy-and-paste-ready YAML and JSON snippets illustrating common routing patterns.

Single filters

The sub-sections below provide simple single-filter example snippets.

Route All MO Callbacks

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.

Route SMS Messages Only

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.

Match MO by Keyword only

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.

Match DR by senderId only

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.

Combining multiple filters

The sub-sections below provide more complex rulesets that feature multiple filters.

Match MO by Channel + Sender ID

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.

Match MO by Channel + Keyword

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.

Match MO by Keyword + Sender ID

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.

Match MO using all available filters

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.

Match MO using CHANNEL_UNSPECIFIED with Sender ID filter

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.

Route DR by Channel and Delivery Status

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.

Rule combination

The following examples demonstrate how to combine rules to handle common messaging scenarios.

Priority Routing (Rule Precedence)

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_SENDER via SMS matches the first rule and goes to /priority.
    • A message from OTHER_SENDER (or any other sender that isn't VIP_SENDER) via SMS fails the first rule, matches the second rule, and goes to /sms.
  • Does not match: A message from VIP_SENDER via WhatsApp would fail the first rule (because sender_id is empty for WhatsApp) and fail the second rule (not SMS). It would then use fallback_behavior.
  • Why it is useful: Implementing tiered service levels or special handling for high-value originators.

Handling no matches (fallback)

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 FAILED matches the rule and is sent to /errors.
  • Does not match: A delivery report with status DELIVERED does not match the rule. Because fallback_behavior is DISCARD, 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.

Practical configuration examples

The following sections provide examples highlighting contextualized use cases.

Use Case: Separate marketing and transactional traffic

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: DISCARD

Use Case: Secure Routing with Per-Rule Secrets

You might want to route sensitive callbacks to a highly secure endpoint that uses a different HMAC secret for validation.

Edit code snippet

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: SMS

Troubleshooting

If you encounter any obstacles, review the following troubleshooting tips:

Nothing is being routed

If no callbacks are being routed:

  • Ensure that the corresponding webhook is subscribed to MESSAGE_INBOUND and/or MESSAGE_DELIVERY triggers.
  • Check if routing_configuration is 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_INBOUND or MESSAGE_DELIVERY triggers.
    • Result: No match. The webhook(s) must be subscribed to either the MESSAGE_INBOUND and/or MESSAGE_DELIVERY triggers.
  • Callback Scenario 2: A WhatsApp message with text "help".
    • Result: No match. channel_filter is SMS.
  • Callback Scenario 3: An SMS message with text "please help".
    • Result: No match. keyword_filter matches only the first word.
  • 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.

A specific rule isn't matching

If a specific rule isn't matching:

  • Verify rule order. A broad rule earlier in the list might be "stealing" the callbacks.
  • Remember keyword_filter matches only the first word.
  • During open beta, sender_id_filters only 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_DEFAULT

Evaluation:

  1. If a message comes from VIP_SENDER via SMS, it matches the first rule and goes to /priority. It never reaches the second rule.
  2. If a message comes from OTHER_SENDER via SMS, it fails the first rule, matches the second, and goes to /general-sms.
  3. If a message comes from OTHER_SENDER via 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.

Payload not matching keyword

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.

See also

We'd love to hear from you!
Rate this content: