Architecting Multi-Channel Outbound Campaigns Blending Voice, SMS, and Email Touchpoints

Architecting Multi-Channel Outbound Campaigns Blending Voice, SMS, and Email Touchpoints

What This Guide Covers

This guide details the architectural pattern for orchestrating outbound campaigns that dynamically switch between voice, SMS, and email based on real-time customer response state. You will configure an External Data Source to manage contact preferences, define a campaign logic that respects compliance windows, and construct a Flow that handles channel-specific responses without creating infinite retry loops. Upon completion, you will have a resilient orchestration layer capable of maximizing engagement while minimizing regulatory risk and agent fatigue.

Prerequisites, Roles & Licensing

Before initiating this implementation, verify the following environment requirements. Failure to meet these prerequisites results in licensing errors or API quota exhaustion during production execution.

Licensing Tier Requirements

  • Genesys Cloud CX: Professional License is required for basic Outbound Campaigns. Enterprise License is mandatory for Advanced Orchestration features, specifically the ability to use Data Stores for complex state management across multiple channel types within a single interaction record.
  • Add-ons: You must purchase the SMS Add-on and Email Service Provider (ESP) integration license. Voice-only licenses do not support SMS payload delivery.
  • API Quotas: Ensure your organization has sufficient API quota for the POST /api/v2/outbound/campaigns endpoint, especially if running high-frequency data syncs.

Granular Permissions
The executing user or service account requires the following permission sets:

  • Outbound > Campaigns > Edit: Allows modification of dialing rules and channel settings.
  • Data Store > Read/Write: Required for persisting contact state between channel attempts.
  • External Data Source > Connect: Necessary to pull preference data from your CRM or CDP.
  • API > OAuth2 > Client Credentials: For automated campaign triggers.

OAuth Scopes
When authenticating via Service Account, request the following scopes:

  • outbound.campaigns.write
  • datastore.read
  • datastore.write
  • external_data_source.connection

External Dependencies

  • CRM/CDP: Must support a REST API endpoint for retrieving contact attributes (phone, email, consent status).
  • Email Service Provider (ESP): If using native Genesys Email, ensure the domain is verified. If using Salesforce Marketing Cloud or similar, configure the webhook listener.
  • SMS Aggregator: Ensure your SMS provider supports MMS and 10DLC registration if operating in North America to prevent carrier filtering.

The Implementation Deep-Dive

1. Data Ingestion & State Management via External Data Source

The foundation of a multi-channel campaign is the data structure. You cannot rely on static CSV uploads for dynamic channel switching. You must implement a real-time state management system that tracks which channels have been attempted and what the last response was.

Architectural Reasoning
We utilize a Genesys Cloud Data Store rather than a raw External Data Source (EDS) for the active campaign state. While an EDS is excellent for pulling static contact attributes (name, phone), a Data Store allows you to persist transient variables like last_channel_attempted and channel_success_flag. This persistence ensures that if a voice call fails, the system knows not to retry voice immediately but instead escalate to SMS.

Configuration Steps

  1. Navigate to Data > Data Stores in the Admin UI.

  2. Create a new Data Store named Outbound_Campaign_State.

  3. Define the schema with the following keys:

    • contact_id (String, Unique Key)
    • phone_number (String)
    • email_address (String)
    • preferred_channel (String: Voice, SMS, Email)
    • last_channel_attempted (String: Nullable)
    • channel_success_status (String: Pending, Success, Failed, Opted_Out)
  4. Create a Data Store Integration to populate this store from your source CRM. Map the source fields to the Data Store keys during the integration setup.

  5. Ensure the integration runs on a schedule or triggers via webhook upon new lead generation.

The Trap: Static CSV Refresh Loops
A common misconfiguration is relying solely on a nightly CSV refresh for campaign data. If a contact opts out of SMS mid-campaign, the system will not know until the next nightly refresh. This results in continued SMS attempts to an opted-out number, violating CTIA guidelines and risking carrier blacklisting.

The Solution: Implement a “Write-Back” mechanism. When the Flow completes (Success or Failure), it must update the Data Store immediately via the API.

POST https://api.mypurecloud.com/api/v2/outbound/campaigns/{campaignId}/contacts/externalDataSources/{edsId}/update
{
  "contact": {
    "contactId": "12345",
    "dataStoreValues": {
      "last_channel_attempted": "VOICE",
      "channel_success_status": "SUCCESS"
    }
  },
  "overwriteData": true
}

Note: The overwriteData flag must be set to true to ensure the state updates propagate correctly in real-time.

2. Campaign Configuration & Channel Prioritization Logic

Once data is flowing, you must configure the Outbound Campaign object itself. This involves defining the dialing rules and, crucially, the channel selection logic.

Architectural Reasoning
In a multi-channel environment, you do not simply enable Voice, SMS, and Email simultaneously. You define a priority hierarchy. The system should attempt the highest preference channel first. If that fails or times out, it falls back to the next tier. This requires specific configuration within the Outbound Campaign > Channels tab.

Configuration Steps

  1. Navigate to Engagement > Outbound > Campaigns.

  2. Create a new campaign and select Multi-Channel as the campaign type.

  3. Under Channels, add Voice, SMS, and Email.

  4. For each channel, configure the Dialing Rules:

    • Voice: Set Max Retries to 3. Set Time Window (e.g., 9 AM - 7 PM local).
    • SMS: Set Max Retries to 2. Set Time Window (e.g., 10 AM - 6 PM local). SMS has stricter carrier time windows than voice.
    • Email: No retry limit required by default, but set a delay interval of 24 hours between attempts.
  5. Configure the Channel Selection Logic. Select the option to “Use Contact Attribute” for channel preference mapping. Map this attribute to the preferred_channel field in your Data Store.

  6. Enable Smart Routing if available on your license tier. This allows the system to skip channels that have previously failed within a short window.

The Trap: Ignoring Carrier Time Windows
A critical misconfiguration occurs when setting identical time windows for Voice and SMS. SMS carriers often block messages sent outside of specific business hours or during local night time more aggressively than voice calls. If you schedule an SMS at 8 AM EST while the contact is in PST, you risk delivery failure.

The Solution: Configure separate time windows per channel based on carrier recommendations.

  • Voice: 09:00 - 21:00 Local Time.
  • SMS: 10:00 - 18:00 Local Time.
  • Email: 09:00 - 17:00 Local Time (to ensure open rates are not impacted by weekend delivery).

API Payload for Channel Configuration
When automating this via API, the JSON body must explicitly define the channel order and retry behavior.

{
  "name": "Q4_Multi_Channel_Outreach",
  "channelSelectionStrategy": "CONTACT_PREFERENCE",
  "channels": [
    {
      "type": "VOICE",
      "priority": 1,
      "maxRetries": 3,
      "timeWindow": {
        "startHour": 9,
        "endHour": 21
      }
    },
    {
      "type": "SMS",
      "priority": 2,
      "maxRetries": 2,
      "timeWindow": {
        "startHour": 10,
        "endHour": 18
      }
    },
    {
      "type": "EMAIL",
      "priority": 3,
      "maxRetries": 5,
      "timeWindow": {
        "startHour": 9,
        "endHour": 17
      }
    }
  ],
  "contactDataStoreId": "12345678-abcd-efgh-ijkl-mnopqrstuvwx"
}

3. Interaction Flow Logic for Channel Handoff

The Outbound Campaign initiates the interaction, but the Flow handles the logic. You must design a Flow that can accept callbacks from failed voice attempts and trigger subsequent SMS or Email channels without creating duplicate records or confusing the customer.

Architectural Reasoning
Standard outbound flows often end on “No Answer” or “Busy”. In a multi-channel context, these outcomes should not be terminal states. Instead, they must trigger a state transition in your Data Store and queue the next channel for execution. This requires the Flow to write back to the Data Store upon completion of every branch.

Configuration Steps

  1. Open Architect > Flows. Create a new Flow named Multi_Channel_OuterLoop.

  2. Add a Get Contact Data node at the start. Retrieve the last_channel_attempted and channel_success_status from the Data Store.

  3. Implement a Decision Node to check the status:

    • If channel_success_status is SUCCESS, end the Flow.
    • If channel_success_status is OPTED_OUT, end the Flow.
    • If channel_success_status is FAILED, proceed to the Logic for Next Channel.
  4. Voice Branch:

    • If the current channel is Voice and it rings out, route to a “Wait” node (e.g., 2 hours).
    • After the wait, update the Data Store: Set last_channel_attempted to VOICE, set channel_success_status to FAILED.
    • Route to the Next Channel Logic.
  5. SMS/Email Branch:

    • If the flow determines SMS is next, use a Send SMS node (or Email node).
    • Update the Data Store: Set last_channel_attempted to SMS, set channel_success_status to PENDING.
  6. Termination Logic: Add a global exit condition that prevents more than 3 total channel attempts per contact within a 7-day window. This requires a flow variable that increments on each failure.

The Trap: Infinite Retry Loops
A frequent failure mode is designing a Flow where a failed Voice call triggers an SMS, which then fails and triggers another Voice call. Without state tracking in the Data Store, the system perceives this as a new interaction rather than a continuation of a sequence. This confuses the customer (“Why are you calling me again after I just texted?”) and wastes agent time.

The Solution: The Flow must check the last_channel_attempted variable before selecting the next channel.

  • Expression Logic: If last_channel_attempted is “VOICE” AND attempt_count < 3, THEN select “SMS”.
  • Expression Logic: If last_channel_attempted is “SMS” AND attempt_count < 2, THEN select “EMAIL”.
  • Expression Logic: If last_channel_attempted is “EMAIL”, END.

Flow Expression Snippet for Channel Selection
Use this expression within the Decision Node to determine the next action:

${if(
    getContactDataStoreValue("outbound_state", "last_channel_attempted") == "VOICE" && 
    getContactDataStoreValue("outbound_state", "attempt_count") < 3,
    "SMS_TRIGGER",
    if(
        getContactDataStoreValue("outbound_state", "last_channel_attempted") == "SMS" && 
        getContactDataStoreValue("outbound_state", "attempt_count") < 2,
        "EMAIL_TRIGGER",
        "END_FLOW"
    )
)}

Note: Ensure the getContactDataStoreValue function is available in your Architect version. If not, use the API call node to fetch the value first.

Validation, Edge Cases & Troubleshooting

Edge Case 1: TCPA Consent Window Violation

The Failure Condition: The system attempts an SMS or Voice call outside of the consent window defined by local law (e.g., US TCPA).
The Root Cause: The campaign time window is set globally, but individual contacts have different legal consent timestamps stored in your CRM.
The Solution: Implement a “Consent Check” node at the top of the Flow. This node queries the CRM for the last_consent_timestamp. If the current timestamp minus last_consent_timestamp is less than 24 hours (or defined policy), abort the interaction immediately and log as “Compliance Block”. Do not count this against the retry limit.

Edge Case 2: Number Portability & SMS Routing

The Failure Condition: A voice number is ported to a different carrier, but the system continues to attempt an SMS on the old routing table, resulting in delivery failure.
The Root Cause: Carrier routing tables are not updated in real-time by Genesys Cloud when a number ports.
The Solution: Enable Dynamic Number Routing (DNR) if available for your region. Alternatively, implement a retry logic where an SMS failure triggers a “Voice Verification” step to confirm the number is active before sending further messages. This ensures you do not spam inactive numbers.

Edge Case 3: Email Bounce Handling

The Failure Condition: The campaign sends an email which bounces (Hard Bounce), but the system continues to include that contact in future voice or SMS attempts.
The Root Cause: Standard Outbound Campaigns often treat email delivery as a “send” event without checking for SMTP error codes like 5xx.
The Solution: Configure a webhook listener from your ESP back into Genesys Cloud. Map the email_status payload to update the Data Store channel_success_status. If the status is “Hard_Bounce”, trigger a flow state that sets the contact to “Do_Not_Contact” for all channels except perhaps a re-confirmation email, depending on compliance policy.

Edge Case 4: High Volume Latency

The Failure Condition: During peak campaign execution, API calls to update the Data Store cause latency, leading to multiple agents calling the same number simultaneously from different channels (e.g., one agent calls while another sends SMS).
The Root Cause: Lack of locking on the contact record in the Data Store.
The Solution: Implement optimistic locking or use a “Locking” mechanism in your Data Store schema. When initiating an interaction, write a lock token to the Data Store. Subsequent attempts check for this token. If it exists, wait or queue the action. This prevents race conditions during high concurrency.

Official References