To help your users manage their preferences for all of your brand’s communication channels in one place, build a subscription preference center and link to it from messages sent from Iterable. While doing so is a complex option, it's also the most flexible, enabling you to keep full creative control over design and user experience.
This article describes how to use Iterable's API to integrate a subscription preference center into your website or app.
# In this article
- Before you begin
- Prerequisite: Set up message channels and types
- Step 1: Display a user's subscription preferences
- Step 2: Update a user's subscription preferences
- Step 3: Add your project's hosted unsubscribe URL
- Step 4: Use the hosted unsubscribe URL for List-Unsubscribe (optional)
- Step 5: Use the hosted unsubscribe URL in templates
- Attributing unsubscribes to a specific campaign and template
- Security and compliance considerations
# Before you begin
When you create a subscription preference center, here are some functional and technical requirements you should consider, as well as some important reminders.
# Functional requirements for your subscription preference center
Your subscription preference center should allow users to view and update their subscription preferences.
Ultimately, your subscription preference center page should allow users to functionally perform the following actions:
- View their current subscription preferences
- Update their subscription preferences by:
- Subscribing to and unsubscribing from message channels
- Subscribing to and unsubscribing from message types within a message channel
- Subscribing to and unsubscribing from lists
In addition to the above, your subscription preference center should be developed with security and compliance in mind. See Security and compliance considerations below for some important reminders.
# Considerations for channel and message type nuances
Some channels and message types have nuances you should be aware of. These nuances may affect how you build your subscription preference center page.
Example: SMS resubscribes
As an example of how a channel-specific nuance can affect your subscription preference center page, consider the scenario where a user has unsubscribed from a SMS channel and intends to re-subscribe.
If a user has unsubscribed from a SMS channel by sending in a text message to unsubscribe (such as a stop keyword), they can only re-subscribe by sending in an accepted subscribe keyword to the same sender ID they used to unsubscribe. Attempting to re-subscribe to SMS via an API-driven preference center won't work properly because Iterable can't send these preferences downstream to your SMS provider. For more information, see SMS Unsubscribes and Resubscribes.
This is just one example of a nuance you should consider when building your subscription preference center page. Different channels and message types may have different nuances that you should be aware of.
# Reminder: Iterable tracks subscription events when preferences update
Iterable tracks subscription events (such as emailSubscribe and
emailUnsubscribe) when you update preferences using the Users API.
When users save their preferences, you can send Iterable a complete list of their subscribed and unsubscribed message types. If this request payload includes implied subscriptions, these become explicit subscriptions. This generates a subscribe event that can trigger journeys and webhooks.
This might be undesirable if a user was already implicitly subscribed and is now explicitly confirming that subscription, as it's not a new subscription but rather a confirmation of existing preferences.
This behavior is true whether or not implied subscriptions
is enabled for GET requests, but it becomes more important when implied
subscriptions is enabled because it's more likely to create a duplicate
subscribe event that can trigger journeys and webhooks.
# Recommended API flow for a subscription preference center
When you set up a subscription preference center, be sure you set up your API calls in the following order:
Fetch current state: When displaying the subscription preference center to a user, always make a call to a User
GETendpoint (such asGET /api/users/getByEmail) to retrieve the user's current subscriptions.Apply user changes: Use the data from the
GETcall to populate your subscription preference center UI. As the user makes changes (subscribing or unsubscribing), apply those changes to the list you received.Update Iterable: When the user finalizes their changes, send an update to Iterable with their new preferences.
# Prerequisite: Set up message channels and types
Before you begin, be sure you've set up your project's message channels and types. For more information and best practices, read Message Channels and Types Best Practices.
# Step 1: Display a user's subscription preferences
First, you need to create a page that displays the user's current subscription preferences. This page should be designed to allow users to view and update their subscription preferences.
When a user accesses this page, it should display the user's current subscription preferences. This requires making an API call to Iterable to retrieve the user's current subscription preferences.
To identify the user who is viewing the page, Iterable provides two API endpoints that accept user identifiers as a URL parameter. The endpoint you use depends on the user identifier you are using:
- If you're using an
emailas the user identifier, useGET /api/users/{email}. - If you're using a
userIdas the user identifier, useGET /api/users/byUserId/{userId}.
# Using implied subscriptions in your preference center (optional)
By default, Iterable's Users API only returns explicit subscriptions (subscriptions that the user has explicitly subscribed to or unsubscribed from), and does not include implicit subscription statuses (user subscriptions that the user is subscribed to or unsubscribed from by default).
This means that when you call the Users API to retrieve subscription data,
the response bodies default to include unsubscribedMessageTypeIds, and
subscribedMessageTypeIds arrays only if any one of those values has been
explicitly modified for the user. To learn more, read about message type subscriptions in User API responses.
As a result, Users API GET requests only return a partial view of a user's
subscription statuses because they don't include message types that the user
is subscribed to or unsubscribed from by default.
If your project uses mixed opt-in and opt-out message types, which is common, the default behavior can create challenges for preference centers. Users may appear to have no subscription preferences for message types they're actually eligible to receive (opt-out types) or ineligible for (opt-in types).
When implied subscriptions is enabled, User API endpoints return both explicit and implied subscription statuses, providing a complete view of all message types the user can manage. To learn more about the technical details, read about implied subscriptions in User API responses.
NOTE
When you update a user's subscription preferences and a user subscribes to a message type that was already implicitly subscribed to, the update creates a subscribe event that can trigger journeys and webhooks. Learn more about tracking subscription events when preferences update.
When to consider implied subscriptions:
- Your project uses both opt-in and opt-out message types
- You need to display all available message types in your preference center
- You want to avoid showing incomplete subscription information to users
To enable this feature, contact your Iterable account manager. For detailed technical information about how response fields change when this feature is enabled, see the Users API documentation.
# Example response with subscription data
When you're finished with this step, your subscription preference center should display the user's current subscription preferences when the page loads.
For example, consider the following subscription preferences:
To represent this data, the response from GET /api/users/{email}
might look similar to the following:
{ "user": { "email": "user@example.com", "dataFields": { "emailListIds": [3, 4, 5], // Note: This could be "userListIds" depending on project type "unsubscribedMessageTypeIds": [1, 2] } } }
This data indicates that the user is subscribed to lists 3 (Weekly
Newsletter), 4 (Cart Reminders), and 5 (Product Suggestions), and
is unsubscribed from message types 1 (Daily Promotional) and 2 (Weekly
Promotional).
# Step 2: Update a user's subscription preferences
The above user might update their subscription preferences as follows:
When the user submits these updates, your web server should pass them to the
POST /api/users/updateSubscriptions
API, with a request body similar to the following:
{ "email": "user@example.com", "unsubscribedMessageTypeIds": [2, 4] }
In this example, if the user selected Unsubscribe Me From All Marketing Emails, the request body might instead look something like this:
{ "email": "user@example.com", "unsubscribedChannelIds": [100], "unsubscribedMessageTypeIds": [1, 2, 3, 4, 5] }
This request body unsubscribes the user from the channel (with an id of 100)
and the individual message types (with ids of 1, 2, 3, 4, and 5).
# Updating double opt-in subscriptions (SMS only)
Double opt-in is a message type that triggers a SMS double opt-in flow that sends users a SMS to confirm their request. Users must reply to subscribe to these message types.
If you're subscribing a user to a double opt-in message type (SMS only), use the
POST /api/subscriptions/subscribeToDoubleOptIn
endpoint to trigger the SMS Double Opt-In
subscription flow. The user will receive a confirmation message to confirm
their subscription.
Once the user confirms their subscription by replying to the SMS confirmation message, they'll be subscribed to the message type.
To learn more, read SMS Double Opt-In Overview.
# Validating channel alignment for message type subscriptions (optional)
By default, Iterable rejects API subscriptions to message types when a user is unsubscribed from the message type's corresponding channel. While the channel remains unsubscribed, Iterable prevents users from subscribing to messages they can’t receive.
You can save message type preferences when a user unsubscribes, so they’re
preserved in case a user decides to resubscribe at a later date, by setting
the optional validateChannelAlignment parameter to false for the following
endpoints:
# Using the Subscriptions API (optional)
If your project has opt-in message types enabled, you can also use the following APIs to modify user subscriptions to message channels, message types and lists:
PATCH /api/subscriptions/{subscriptionGroup}/{subscriptionGroupId}/user/{userEmail}DELETE /api/subscriptions/{subscriptionGroup}/{subscriptionGroupId}/user/{userEmail}PUT /api/subscriptions/{subscriptionGroup}/{subscriptionGroupId}?action=subscribePUT /api/subscriptions/{subscriptionGroup}/{subscriptionGroupId}?action=unsubscribe
NOTE
Iterable's Subscriptions API and opt-in message types are enabled by default for all organizations created after September 2019.
Because of a breaking API change, if your organization was created before this date, your customer success manager must enable this feature.
To learn more, read Breaking API Change.
# Step 3: Add your project's hosted unsubscribe URL
The hosted unsubscribe URL is the link that directs users to your custom
subscription preference center. Iterable serves this URL in email templates
with the {{hostedUnsubscribeUrl}} merge parameter.
To use your preference center's URL as the opt-out link, enter the URL in the Hosted Unsubscribe URL field in Iterable's Project Settings page.
# Using parameters to pass data in your hosted unsubscribe URL
When you enter your hosted unsubscribe URL in the Hosted Unsubscribe URL field, you can use parameters and Handlebars to pass additional data to your preference center.
To use the hosted unsubscribe URL for easy unsubscribe,
the URL must include merge tags for either email or userId to
identify the user (depending on how your project identifies users).
Only email-based projects support cookies, so if you are using a hybrid or
userID-based project, you may need to pass the user identifier as a URL
parameter. To identify the user, you can add a URL parameter for your project's
unique identifier, email or userId. You should URL-encode the user
identifier's value with the #urlEncode
Handlebars helper.
Example:
https://www.example.com/accounts/preferences?email={{#urlEncode}}{{email}}{{/urlEncode}}
# Step 4: Use the hosted unsubscribe URL for List-Unsubscribe (optional)
Next, decide whether to use the hosted unsubscribe URL as the
List-Unsubscribe email header, which uses the preference center's URL for
easy unsubscribe.
This is optional and requires additional setup.
Iterable automatically uses the default unsubscribe URL for the
List-Unsubscribe header, even if you have a hosted unsubscribe URL set up.
Clicks made on default easy unsubscribe links are processed by Iterable's
default unsubscribe process, and users are unsubscribed from the message
channel that the campaign was sent from.
When you use a hosted unsubscribe URL for easy unsubscribe, the following considerations apply:
HTTPSis required. URLs that useHTTPare not supported.Iterable does not automatically unsubscribe users when they click on the easy unsubscribe link. Instead, your server must handle the
POSTrequest for easy unsubscribe and promptly process opt-outs in a one-click experience.Iterable does not track any events until your server sends an API request to Iterable. This means that users aren't unsubscribed until your server processes the
POSTrequest and sends the data back to Iterable, and that Iterable doesn't track a hosted unsubscribe click event when using this URL for easy unsubscribe.
To set up your hosted unsubscribe URL for easy unsubscribe, follow these steps:
- Update your web server to handle the
POSTrequest for easy unsubscribe. - Change the use the hosted unsubscribe URL for List-Unsubscribe headers.
- Contact Iterable to finish setup.
- Test the hosted unsubscribe URL for easy unsubscribe.
# Step 4.1: Handle the POST request for easy unsubscribe
To use your hosted unsubscribe URL for easy unsubscribe, your server must be able to:
- Identify the user by
emailoruserIdin the URL. (See Using parameters to pass data in your hosted unsubscribe URL from Step 3 above). - Accept a
GETrequest to direct the user to your preference center. - Accept a
POSTrequest to process the easy unsubscribe request. Your server's response to thePOSTrequest must function as a one-click unsubscribe, and cannot redirect to a page.
Additionally, the URL must be the same for both requests.
# Accept a GET request
When a user clicks on the link within the email body (set using the
{{hostedUnsubscribeUrl}} merge parameter), this sends a GET request to your
URL, which should direct the user to the page for your custom preference center.
# Accept a POST request
When a user uses easy unsubscribe from their inbox, the inbox provider sends an
empty POST request to your server.
When receiving the POST request, your web server should:
- Send an API request to Iterable that unsubscribes the user from the appropriate list(s).
- If the request to Iterable is successful, send a
200response to the inbox provider to confirm that the user has been successfully unsubscribed. - If the
POSTrequest fails for any reason, send a400response to the inbox provider. This response triggers the inbox provider to defer to the mailto method instead, so you stay in compliance with easy unsubscribe requirements. (This is standard behavior for the List-Unsubscribe header and not specific to Iterable.)
For a hosted unsubscribe URL that looks like https://example.com/unsubscribe/example?,
the incoming easy unsubscribe POST request looks something like this:
POST /unsubscribe/example?user@example.com HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Content-Length: 26 List-Unsubscribe=One-Click
# Step 4.2: Change the List-Unsubscribe header project setting
Once your hosted unsubscribe URL is set up to handle the POST request for easy
unsubscribe, you can enable this feature in Iterable.
To change the project setting to use the hosted unsubscribe URL for List-Unsubscribe headers:
- In Iterable, go to Settings > Project Settings.
- Find the setting Hosted Unsubscribe URL setting and check that the URL is correct, or make changes as needed.
- Next, find the setting Use the Hosted Unsubscribe URL as the 'List-Unsubscribe' Mail Header Value.
- Toggle this setting to Yes.
- Scroll to the very bottom of the page and click Save Project Settings.
# Step 4.3: Contact Iterable to finish setup
Once you've made these changes, contact your customer success manager or Iterable support and ask to enable the hosted unsubscribe URL for your List-Unsubscribe headers.
You must contact Iterable to enable this feature.
This step is required in addition to changing the project setting in Iterable.
# Step 4.4: Test the hosted unsubscribe URL for easy unsubscribe
After you've made these changes, test your easy unsubscribe setup.
If the user is successfully unsubscribed, the hosted unsubscribe URL is set up correctly and is working as expected.
If the user is not unsubscribed, or if the easy unsubscribe link doesn't work as
expected, review the setup of your hosted unsubscribe URL in project settings,
and the POST request handling.
# Step 5: Use the hosted unsubscribe URL in templates
When you make a marketing email template, Iterable checks the content for
presence of an unsubscribe link (see Unsubscribe Links
To use your custom subscription preference center's URL as the opt-out link,
use the {{hostedUnsubscribeUrl}} merge parameter in your email templates.
When you use the {{hostedUnsubscribeUrl}} merge parameter, Iterable adds the
URL as entered in project settings. Your URL may already include parameters
like email or userId to identify the user.
However, you can add additional parameters in the message template by appending
them to the merge parameter. When you do this, we recommend using & as the
first character instead of ?.
# Attributing unsubscribes to a specific campaign and template
If a user navigates to your email preference center through a
{{hostedUnsubscribeUrl}} link, you may want to attribute unsubscribe events
back to the campaign or template in which they clicked the link. To determine
the relevant campaign and template ID, you can pass attributes in one of two
ways:
- URL parameters
- Cookies
NOTE
This tracking advice applies to clicks on the link created by the
{{hostedUnsubscribeUrl}} merge parameter (for example, in Iterable
templates). It does not apply to clicks on the easy unsubscribe link from the
List-Unsubscribe header. To learn more about how Iterable tracks events
sourced from the List-Unsubscribe headers, read Tracking easy unsubscribe clicks
# URL parameters
You can append userId, campaignId, and/or templateId query parameters
to your hosted preference center URL (on the Settings > Project Settings
screen), and use these parameters to attribute the unsubscribe event to a user,
campaign, and template. To learn how to do this, read Using parameters to pass data in your hosted unsubscribe URL.
For example, your preference center's URL might be something like:
https://www.example.com/accounts/preferences?email={{#urlEncode}}{{email}}{{/urlEncode}}&campaignId={{campaignId}}&templateId={{templateId}}
Your server can then access email, campaignId, and templateId as needed.
# Cookies
When a user clicks a link in an email sent by an Iterable campaign, Iterable sets browser cookies for your project's link tracking domain.
If your hosted subscription preference center shares a root domain with your Iterable project's link tracking domain, it can use the values stored in these cookies to find the campaign and template to associate with the unsubscribe.
For example, if a project's link tracking domain were links.example.com
and its subscription preference center URL were unsubscribe.example.com,
the web server for the subscription preference center could access these
cookies to get the relevant campaignId and templateId.
Iterable sets the following cookies:
-
iterableEndUserId- The recipient's e-mail address. This cookie expires in one year. This cookie is set for email-based projects only.NOTE
To identify user for hybrid or userID-based projects, pass the user identifier as a URL parameter using Handlebars. To learn how to do this, read Using parameters to pass data in your hosted unsubscribe URL.
iterableEmailCampaignId- The unique ID associated with the email's Iterable campaign. This cookie expires in 24 hours.iterableTemplateId- The unique ID associated with the email's Iterable template. This cookie expires in 24 hours.iterableMessageId- A unique ID associated with a specific send of a specific campaign to a specific user. If the same user ever receives the same campaign more than once, each message will have a uniqueiterableMessageId. This cookie expires in 24 hours.
Once you have the campaignId and/or templateId, your web server should
include them in its call to POST /api/users/updateSubscriptions.
For example, the request body might look similar to this:
{ "email": "user@example.com", "unsubscribedChannelIds": [100], "unsubscribedMessageTypeIds": [1, 2, 3, 4, 5], "campaignId": 123, "templateId": 456 }
DANGER
Use your server (not client-side JavaScript) to make API calls that fetch and modify a user's subscription preferences. For more information, read Make API calls from your server, not the browser.
# Security and compliance considerations
When you use a subscription preference center, you're responsible for handling unsubscribe requests and updating user preferences in Iterable in a timely manner. Here are some important reminders for maintaining compliance and ensuring data security.
# Stay compliant with sender requirements
When you use a subscription preference center, you're responsible for handling unsubscribe requests and updating user preferences in Iterable in a timely manner.
Iterable does not automatically unsubscribe users who click on a hosted unsubscribe URL. Instead, your server must handle requests to change preferences by making API calls to Iterable.
IMPORTANT
Failure to process unsubscribe requests and continuing to send messages after a user indicates their request to unsubscribe can result in:
- Harm to your brand's reputation.
- Spam complaints.
- Serious deliverability and compliance consequences for regulated messaging channels like email and SMS.
- Violation of Iterable policies, such as acceptable use and anti-spam.
# Make API calls from your server, not the browser
When you build a subscription preference center, use your server to make API calls that fetch and modify a user's subscription preferences. This is important for security reasons, as it prevents client-side JavaScript from accessing your server-side API key.
WARNING
The APIs that fetch and update a user's subscription preferences require authentication with a server-side API key. This type of API key can access all of your project's data, so it's never safe to embed a server-side API key in client-side code (whether JavaScript, a mobile app, or otherwise). Instead, make API calls from your server. Then, use the data returned from those calls to display a web page that has no knowledge of the API key.
# Do not rely on locally stored or cached copies of a user's subscriptions
Your subscription preference center should not rely on locally stored or cached copies of a user's subscriptions as the basis for an update. Doing so can lead to an incorrect or incomplete user profile in Iterable.
Instead, you should make API calls to Iterable to retrieve the user's current subscription preferences.