Campaign Opt-Out for SMS, WhatsApp, and Email — Technical Reference

Campaign Opt-Out Keyword Detection (CCM)

This feature enables campaign unsubscribe detection without changing existing inbound behavior. CCM evaluates unsubscribe signals for every inbound message that reaches POST /message/receive, while keeping normal routing, session handling, reply windows, delivery notifications, surveys, and scheduler behavior unchanged.

MVP summary: The feature supports SMS, WhatsApp, MS Exchange Email, and Gmail Email. SMS uses keyword detection, WhatsApp uses Meta user_preferences, and email uses the standard List-Unsubscribe mechanism. All supported opt-out paths update CIM with isSubscribed=false and publish CAMPAIGN_UNSUBSCRIBED after successful opt-out.

Related guide: To configure the customer-facing acknowledgement message that gets sent after a successful opt-out, see Configure a Campaign Unsubscribe Flow in Conversation Studio.

Objective

The objective is to detect customer unsubscribe intent through the default unsubscribe mechanism of each supported channel and apply a customer opt-out in CIM, without disrupting the existing inbound message path.

  • Do not create a new session for unsubscribe-only signals.

  • Do not alter existing inbound routing behavior.

  • Do not introduce reply-window or routing changes.

  • Run unsubscribe checks asynchronously where applicable.

  • Log failures without failing the inbound request.

Core principle

The unsubscribe feature is intentionally additive. Every inbound message that reaches CCM is eligible for the unsubscribe check, including campaign replies, scheduled-activity replies, and normal non-campaign inbound messages. There is no campaign-versus-non-campaign gate.

Inbound kind

Existing behavior

Unsubscribe check

Notes

Normal customer message

Continues through the existing inbound path.

Yes

Run the channel-specific unsubscribe check in parallel without changing routing.

Reply to campaign or scheduled outbound

Continues as today, including scheduler queue behavior.

Yes

Do not add a campaign-versus-non-campaign gate.

Any other inbound message that reaches routeMessage

Continues through the existing inbound path.

Yes

No-op unless a supported channel rule matches.

High-level flow

  1. Connector sends inbound message to CCM through POST /message/receive.

  2. If header.intent is UNSUB, CCM opts the customer out through CIM and returns without creating a session or following the existing inbound path.

  3. If the intent is not UNSUB, CCM continues the existing system flow exactly as today.

  4. In parallel, CCM applies the default unsubscribe check for the channel.

  5. When a supported unsubscribe signal is detected, CCM calls CIM with isSubscribed=false.

  6. After a successful CIM opt-out, the system publishes CAMPAIGN_UNSUBSCRIBED on VirtualTopic.conversation-topic.

Important: The main request path is never blocked or altered by the unsubscribe check. Async failures are logged only.

Channel strategy

Channel

Default unsubscribe way

CCM inbound keyword check

Extra work

SMS

Customer texts a configured single-word keyword such as STOP or UNSUBSCRIBE.

Yes, for PLAIN SMS only.

No connector change required.

WhatsApp

Customer can unsubscribe through either Meta marketing opt-out user_preferences with value=stop, or by typing a configured opt-out keyword such as STOP, UNSUB, UNSUBSCRIBE, CANCEL, END, QUIT, OPTOUT, or OPT-OUT.

Yes, for customer-typed WhatsApp PLAIN messages. CCM reads body.markdownText and applies the same single-word keyword logic as SMS.

WhatsApp connector still detects Meta user_preferences and posts CCM /message/receive with intent=UNSUB. Typed WhatsApp keywords do not use the Meta default path; they are handled by CCM keyword detection.

MS Exchange Email

Email client uses List-Unsubscribe one-click or mailto unsubscribe.

No email body parsing.

Connector stamps headers on campaign outbound, exposes one-click endpoint, and forwards intent=UNSUB to CCM.

Gmail Email

Email client uses the same List-Unsubscribe standard.

No email body parsing.

SMTP stamps headers; IMAP poll detects mailto unsubscribe and forwards intent=UNSUB to CCM.

Media / DN / other

Not applicable.

No.

No-op.

SMS keyword detection

SMS is the only channel where CCM performs inbound body keyword matching. The check applies only to inbound SMS messages with body type PLAIN.

Candidate text

  • Use body.markdownText for every inbound PLAIN message on WhatsApp or any other supported channel where plain-text keyword detection is enabled.

  • Trim whitespace.

  • Require a single word.

  • Compare case-insensitively.

  • Optionally strip trailing punctuation.

Configuration

ef.campaign_unsubscribe_enabled=true
ef.campaign_unsubscribe_keywords=STOP,UNSUB,UNSUBSCRIBE,CANCEL,END,QUIT,OPTOUT,OPT-OUT

SMS behavior

Check

Result

PLAIN SMS with a single configured keyword

CIM opt-out and publish CAMPAIGN_UNSUBSCRIBED with reason KEYWORD.

Multi-word SMS

No-op.

Empty text

No-op.

Non-PLAIN message

No-op.

WhatsApp unsubscribe handling

WhatsApp unsubscribe supports two paths: the default Meta Cloud API user_preferences opt-out and customer-typed opt-out keywords. If the customer types a configured keyword such as STOP, UNSUB, or UNSUBSCRIBE in a WhatsApp PLAIN message, CCM detects it from body.markdownText using the same single-word keyword logic as SMS.

Item

Detail

Default way

Customer taps WhatsApp built-in marketing message Stop control.

Typed keyword behavior

If the customer types a configured opt-out keyword such as STOP, UNSUB, UNSUBSCRIBE, CANCEL, END, QUIT, OPTOUT, or OPT-OUT in a WhatsApp PLAIN message, CCM detects it from body.markdownText using the same single-word keyword logic as SMS.

Meta signal

Webhook contains user_preferences with value=stop.

Connector behavior

Detect user_preferences; on stop, post CCM /message/receive with header.intent=UNSUB.

Do not do

Do not build a fake PLAIN STOP chat message for Meta user_preferences, because that could start a session. Typed customer chat keywords are handled separately by CCM keyword detection.

CCM behavior

For header.intent=UNSUB from Meta user_preferences, CCM opts the customer out without creating a session. For customer-typed WhatsApp PLAIN keywords such as STOP, UNSUB, or UNSUBSCRIBE, CCM handles the message the same way as SMS keyword detection: read body.markdownText, apply the configured single-word keyword match, update CIM with isSubscribed=false, and publish CAMPAIGN_UNSUBSCRIBED with reason KEYWORD.

WhatsApp connector payload to CCM

  • header.intent = UNSUB

  • header.channelData.serviceIdentifier = phone number ID

  • header.channelData.channelCustomerIdentifier = WhatsApp wa_id

  • body.type = PLAIN

Email unsubscribe handling

Email uses the standard List-Unsubscribe mechanism for both MS Exchange and Gmail. CCM does not scrape email body content for keywords such as STOP.

Outbound email headers

For campaign outbound messages where header.schedulingMetaData is present, the connector stamps the following headers:

List-Unsubscribe: <mailto:{mailbox}?subject=UNSUBSCRIBE>, <{CONNECTOR_PUBLIC_URL}/unsubscribe/one-click?serviceIdentifier=...&channelCustomerIdentifier=...&tenantId=...>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Agent replies without schedulingMetaData remain unchanged.

Email unsubscribe paths

Signal

Path

Gmail or Outlook one-click unsubscribe

Email client calls connector /unsubscribe/one-click; connector posts CCM /message/receive with intent=UNSUB.

mailto unsubscribe

Unsubscribe email lands in the channel mailbox; connector poll detects subject UNSUBSCRIBE and posts CCM /message/receive with intent=UNSUB.

Free-text STOP in email reply body

Out of scope. No body scrape.

Connector public URL

CONNECTOR-PUBLIC-URL on the channel connector provider configuration is the preferred source for the public connector base URL used by Gmail and Outlook one-click unsubscribe. Optional environment fallback is CONNECTOR_PUBLIC_URL.

CIM opt-out API

All supported unsubscribe paths end in the same CIM Customer API call.

GET {ef.cim_customer_api}/customers?channelType={type}&customerChannelIdentifier={id}&isSubscribed=false

Parameter

Source / meaning

channelType

Resolved from serviceIdentifier using the existing channel lookup.

customerChannelIdentifier

header.channelData.channelCustomerIdentifier.

isSubscribed=false

Opt out. CIM backend attaches the system Opt Out label.

isSubscribed=true or an omitted subscription parameter represents subscribed state and can remove the Opt Out label if present; that path is not used by this CCM unsubscribe flow.

Event: CAMPAIGN_UNSUBSCRIBED

After a successful CIM isSubscribed=false update, CCM publishes CAMPAIGN_UNSUBSCRIBED for every opt-out path: SMS keyword, WhatsApp UNSUB, and email UNSUB.

Item

Detail

Topic

VirtualTopic.conversation-topic

Type

NOTIFICATION

Envelope

Standard CimEvent

Data

CampaignUnsubscribedDto

Supported reasons

KEYWORD, META_USER_PREFERENCES, LIST_UNSUBSCRIBE

Event data

Field

Required

Notes

customerId

If available

From CIM response.

channelCustomerIdentifier

Yes

Customer identifier on the channel.

serviceIdentifier

Yes

Channel service identifier.

channelType

Yes

Resolved channel type.

sourceMessageId

No

Inbound message ID, or null for one-click unsubscribe.

keyword

No

Matched keyword for SMS, or null for one-click / non-keyword paths.

reason

Yes

KEYWORD, META_USER_PREFERENCES, or LIST_UNSUBSCRIBE.

channelSessionId / conversationId

No

Only if already present.

timestamp

Yes

Event timestamp.

Conversation Manager behavior

Conversation Manager persists the CAMPAIGN_UNSUBSCRIBED event and posts intent CAMPAIGN_UNSUBSCRIBED to Conversation Studio through {CONTROLLER_URL}/intent. CCM does not need a handler for this intent.

Case

/intent payload

Webhook handling

Active conversation for customer / channel identifier

Real conversation and roomId.

Existing conversation path.

No active session

roomId and conversation remain null. Cache routing channelSession by correlationId.

Accept webhook with channelSession, or resolve cached session. Publish BOT_MESSAGE outbound. Do not create a conversation or session.

Out of scope

  • Session, CAMPAIGN_OUTBOUND, or reply-window changes.

  • NLP or multi-word opt-out detection.

  • Scraping STOP from email HTML or quoted replies.

  • Re-subscribe flow such as START.

  • Creating fake WhatsApp inbound chat messages for Meta opt-out events.

Referenced components

Component

Reference

WhatsApp connector

Branch 5.9.0_f-NCRI-23

MS Exchange email connector

Branch 5.9.0_f-NCRI-23

Gmail IMAP email connector

Branch 5.9.0_f-NCRI-23

Object model

Version 1.22.16.9, branch 5.9.0_f-NCRI-23