Journey webhooks are secure connections that enable you to send and fetch data from an external app or service when users reach a particular step in a journey. You can use journey webhooks to:
Send data to external services (Webhook tile):
- Send data to business intelligence tools like Amplitude or Looker
- Save data to Segment
- Set up a direct mail integration with Inkit or Lob
- Launch NPS surveys with Delighted
- Trigger an action on Zapier
- Automate phone calls with Twilio
- Update your users' Iterable profiles
- Save data to your own servers
Fetch real-time data into a journey (Live Data tile):
- Fetch live data from external sources for real-time decisioning and personalization in journeys
NOTE
Journey webhooks always make outbound HTTP calls from Iterable to your endpoint,
using the HTTP method you configure (GET, POST, PUT, PATCH, or DELETE).
What differs is how the response is handled — and that depends on which tile type
calls the webhook:
- Webhook tiles discard the response.
-
Live Data tiles capture the response and make it available to
downstream tiles in the journey via
{{liveData.objectName.fieldName}}references.
Once you've configured a journey webhook, you can use an integration tile to call it when users reach that step of a journey.
Read on to learn how to create webhooks and use them in your journeys.
IMPORTANT
Journeys were previously known as "Workflows." Iterable's API endpoints and
field names still use this legacy term (for example, workflowId). When setting
up journey webhooks, be sure to use the correct field names and endpoint URLs in
your configuration to avoid errors.
# In this article
# How journey webhooks work
Create a journey webhook, then add it to a journey using a Call Webhook tile. When a user reaches that tile, Iterable sends a request to the endpoint you configured.
The request includes the user profile fields you selected, journey trigger data, and any custom body you defined.
# Delivery characteristics
- Delivery is not guaranteed. Iterable retries some failed requests, but a user can still exit a journey at this step if the request keeps failing.
- Order is not guaranteed. Journey processing and webhook retries can cause webhook requests to arrive out of order.
- Duplicate deliveries are possible when a previous attempt times out or fails and a retry is sent.
# Response times, retries, and timeouts
Iterable applies a 10-second timeout to journey webhook requests.
If a call times out after 10 seconds or returns a retryable status code, Iterable retries after a 3-minute delay, up to 3 times.
# HTTP response code handling
Iterable uses the webhook response status code to determine next steps:
| HTTP status | How Iterable responds |
|---|---|
2xx | User continues to the next step in the journey. |
401 | For OAuth 2.0 authentication, Iterable fetches a new access token and retries the webhook request. |
429, 502, 503, 504
| Iterable retries after 3 minutes, up to 3 retries. |
| All other status codes | User exits the journey. |
If the webhook request still fails after 3 retries, the user exits the journey at this step. When future users enter the tile, Iterable still attempts to call the webhook.
# OAuth 2.0 behavior for 401 responses
If a journey webhook configured with OAuth 2.0 receives a 401 response,
Iterable fetches a new access token and retries the webhook request.
# Other failure conditions and notifications
- If the webhook has an invalid URL or returns an SSL exception, the user exits the journey at this step.
- If a Live Data response exceeds the supported 50 KB payload size, the user follows the Live Data tile's Failure path.
- For User Profile tiles, if the response body is not a valid JSON object, the user exits the journey at this step.
- If a webhook's request to fetch an OAuth 2.0 access token fails, the user exits the journey at this step.
# Journey webhook endpoint requirements
To work reliably with Iterable, your endpoint should:
- Support the HTTP method you configure in the journey webhook (
GET,POST,PUT,PATCH, orDELETE). - Respond within 10 seconds.
- Return
2xxfor successful requests. - Handle duplicate deliveries safely (idempotent processing is recommended).
- Return a valid JSON object if you use a User Profile tile to process the webhook response.
If your endpoint URL is invalid, or if the endpoint returns an SSL exception, the user exits the journey at this step.
# Creating journey webhooks
To create a journey webhook:
-
Go to Integrations > Journey webhooks and click New webhook. This takes you to the Create Journey Webhook page:
Give the webhook a name for display purposes. You'll use this name to select the webhook when you add it to a journey.
-
For Destination, choose one of the following:
- Custom to set up the webhook to any other endpoint than Inkit.
- Inkit to use the Iterable + Inkit integration. (When you select Inkit, Iterable automatically preselects the required properties in the Journey Webhook form. All you have to do is fill them in.)
Select the HTTP method that the webhook endpoint requires:
GET,POST,PUT,PATCH, orDELETE. (If you're unsure which one to choose, ask the endpoint's owner.)For Endpoint, enter the destination URL for the HTTP request. You may enter Handlebars expressions to include user profile fields in the URL. For endpoint formatting and Handlebars guidance, including how to add variables, see Endpoint.
-
Select an Authentication option to set up the
AuthorizationHTTP header.- None - No authentication header. (You can still use custom headers.)
-
Basic - Basic authentication. Enter your token in the
Authentication Token field. Iterable adds the
Authorizationheader to the webhook request with the valueBasic <YOUR_AUTH_TOKEN>. - OAuth 2.0 - OAuth 2.0 authentication using client credentials. Provide your Client ID, Client secret, and Token URL.
For more information about these authentication options, see Authentication.
NOTE
Iterable does not encode your authentication token. If needed, encode it before saving it to the journey webhook.
-
Add the webhook request Body (the data that the webhook should send to the app or service you're connecting to the journey).
For body formats, payload field behavior, and request field options, see Body, Include all triggering event fields and
workflowId, and User fields. -
If you want to use webhook data to orchestrate journeys and personalize messages, check the Data Retrieval box to define specific fields from the webhook's payload to make available to reference in journeys (required for journey webhooks that will be referenced in Live Data tiles). After enabling Data Retrieval, you must run a successful test of your journey webhook configuration to fetch a response, then select which fields from the response you wish to make available for referencing as live data.
Under Test Connection, enter sample JSON data to populate any variables used in the endpoint URL, request body, and any dynamic parameters you plan to pass from Webhook or Live Data tiles. For example, for a request that contains
{{email}},{{userId}}, and{{dataFields.orderId}}:{ "email": "user@example.com", "userId": "123456", "dataFields": { "orderId": "A-10001" } }
Click Run Test to test the connection to the external app. If the test is successful, Iterable shows the response and generates a list of returned objects and fields available to be defined as live data fields in journeys. The webhook cannot be saved until a successful test is completed. This ensures that broken or mistyped configurations never reach your production journeys.
In the Live Data Fields section, select up to 50 fields and associated data types from the JSON response that you wish to save as live data fields that can be referenced in journeys. Keep your selected payload as small as possible and include only the fields you need downstream. The selected live data payload can be up to 50 KB. If the payload exceeds the supported size, users follow the Live Data tile's Failure path. To learn more about configuring and using live data in journeys, see Using Live Data in Journeys.
# Journey webhook properties
When you create a journey webhook, you can set the following properties:
# Name
The name of the webhook. You'll use this name to select the webhook when you add it to a journey.
# Destination
The destination of the webhook. You can choose from the following destinations:
- Custom - Set up the webhook yourself. When you select Custom, you can enter the Method, Endpoint, Authentication, and Body properties.
- Inkit - Set up the webhook to send data to Inkit. When you select Inkit, Iterable automatically preselects the required properties. All you have to do is fill them in. To learn more about setting up a journey webhook for Inkit, see Inkit + Iterable Integration.
# Method
The HTTP method of the webhook. You can choose from the following methods: GET,
POST, PUT, PATCH, and DELETE. Most use cases use POST, but check with
the endpoint's owner to confirm the correct method.
NOTES
- If you want to update a user profile based on a
GETresponse from your endpoint, use a User Profile tile in a journey to update the user profile, rather than a Call Webhook tile. To learn more, read User Update Tiles. - To learn about the different HTTP request methods, check out MDN's guide to HTTP Request Methods.
# Endpoint
The URL of the webhook.
You can use Handlebars expressions to pass user profile fields to the webhook
URL. If the values in those expressions can contain anything other than
letters, numbers, periods, hyphens, asterisks, or underscores, wrap those
parameters with {{#urlEncode}}:
https://api.example.com/update?email={{#urlEncode}}{{email}}{{/urlEncode}}
Handlebars expressions defined in the endpoint URL here apply to all journey tiles that reference this webhook. If you need different values per tile—for example, to pass a different experiment ID depending on the journey—you can instead add Handlebars expressions via the Dynamic Parameters field when configuring a Live Data or Webhook tile. The Dynamic Parameters field lets you reuse one webhook across multiple tiles and populate dynamic data specific to each one.
IMPORTANT
Don't place API keys, bearer tokens, or other secrets directly in the endpoint URL. Use Iterable's built-in authentication options whenever possible, or use secure custom headers when a sensitive header value is required.
# Authentication
The authentication method of the webhook. This creates an Authorization
HTTP header that's included in the webhook request.
You can choose from the following authentication options:
# None
Choose None for authentication if your webhook endpoint does not require authentication, or if you use a custom authentication method such as a different header name.
# Basic
Choose Basic authentication if your webhook endpoint requires Basic authentication.
When you select Basic, and enter your token in the webhook's Authentication Token field, Iterable adds this header:
Authorization: Basic <YOUR_AUTH_TOKEN>
NOTE
Iterable does not encode your authentication token. If needed, encode it before saving it to the journey webhook.
# OAuth 2.0
Choose OAuth 2.0 to authenticate your webhook with OAuth 2.0 client credentials.
In the journey webhook properties, provide:
- Client ID
- Client secret
- Token URL - The endpoint that Iterable uses to fetch an access token using the OAuth 2.0 flow.
To fetch an access token, Iterable sends a POST request to your Token URL with:
content-type: application/x-www-form-urlencoded-
This form-encoded body:
client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&grant_type=client_credentials
Then, the OAuth 2.0 endpoint should respond with these JSON fields:
-
access_token(Required) - Access token for subsequent webhook requests. -
expires_in(Optional) - Number of seconds until the token expires on your servers.
An example token response:
{ "access_token": "<ACCESS_TOKEN>", "expires_in": 432000 }
Iterable then sends Authorization: Bearer <ACCESS_TOKEN> in the webhook request
to the webhook endpoint.
NOTE
OAuth token caching in Iterable:
Iterable caches OAuth 2.0 access tokens for up to 10 days. Iterable continues
to use the cached token until your server responds with a 401 error, or
after 10 days, whichever comes first.
The value your server provides for expires_in does not change Iterable's
token cache duration.
If Iterable receives a 401 error for an OAuth 2.0 webhook call, it fetches a new
token and retries the request (up to three times).
# Custom headers
The custom headers to include in the webhook request. These are added as HTTP headers to the webhook request in addition to the header specified in the Authentication property, if used.
# Security considerations for custom headers
IMPORTANT
Custom headers are less secure than Authorization headers. Use them with caution.
Custom headers are visible as plain text to any Iterable user with the Manage Integrations project permission.
Keep this in mind if you need to include sensitive information in a custom header.
To have Iterable securely handle tokens, consider using Authentication headers whenever possible.
Secure custom headers aren't a separate authentication type. They're a safer way
to store sensitive header values such as X-API-Key, bearer tokens, or other
authorization-style headers when a custom header is required.
When you need a sensitive custom header, mark it as secure. Iterable masks secure custom header values in the UI, preserves them on future edits, and still sends them with outbound webhook requests. Secure handling is not automatic based on header name—you must explicitly mark the header as secure.
# Adding custom headers
To add a custom header:
- Click Add Header.
- Enter a Name and Value.
- (Optional) Check Secure to store the header value securely. Use this for sensitive values such as API keys or bearer tokens.
After you save a secure custom header, Iterable masks its value in the UI. To change a secure header back to a plain header, delete it and add a new one.
# Body
A custom request body to add additional fields to the webhook payload. Use this field to enter any additional fields that aren't already included in the default payload. The custom body is merged into the default payload, and all fields are sent to your destination.
# Custom body formats
Select JSON or URL-encoded:
JSON - Enter JSON directly in the field provided.
-
URL-encoded - Add form fields as name-value pairs.
For example, if your project stores first names in
firstName, you can set:-
Name:
firstName -
Value:
{{firstName}}
-
Name:
# Include all triggering event fields and workflowId
Whether to include triggering event fields and workflowId in the webhook
request body. This adapts to the payload type selected (JSON or URL-encoded).
# User fields
User profile fields to include in the webhook request body.
Add fields by clicking Add user field and selecting the fields to include.
NOTES
- If a referenced field exists on both the user profile and in the event or API payload that entered the journey, Iterable uses the value from the event or API payload.
- If a referenced field is not on the user profile, Iterable omits it from the webhook request (unless it is present in the event or API payload that triggered the journey).
- Available fields can vary by journey entry source. For example, a journey
with a Purchase entry source can include
total, while a List Subscribe entry source cannot. - Always test a journey webhook before using it in a live journey.
# Journey webhook payloads
When a user reaches a Call Webhook tile that uses a saved journey webhook, Iterable sends URL-encoded form fields, or a JSON object, to your destination.
That payload includes the following data:
- The user profile fields you add with Add user field.
- The journey trigger data (for example, if the journey was triggered by a purchase event, the payload will include the purchase amount and product details).
- Triggering event fields and/or
workflowId(when Include all triggering event fields andworkflowIdis enabled). - Your webhook's custom body (if you define one).
# Field name conflicts
If the same field name appears in multiple data sources for the payload, values from trigger data or your custom body replace values from selected user profile fields. In other words, fields in the custom body overwrite fields in the trigger data or user profile fields.
For example, if you add a custom body with a firstName field, and the user
profile has a firstName field, the value from the custom body is sent
to the destination.
# Rate limiting journey webhook traffic
Journey webhooks can use rate limits to pace outbound requests so your endpoint can handle journey volume. Iterable doesn't batch multiple users into one webhook request. Instead, requests are sent individually and are held briefly when capacity is full, then resume as capacity becomes available.
You can set a default rate limit for a webhook when it is initially configured, and this default limit is automatically applied to any new Live Data or Webhook tiles that reference it. You can also choose to override the inherited default limit at the tile level.
Rate limits are applied per tile, not as one shared global cap across all tiles or journeys. If your endpoint is used in multiple journeys or by multiple tiles, the total traffic it receives is the sum of the request rates across those tiles.
Lower rate limits and higher retry volume can increase queueing time before users move to the next tile. Overall processing time varies based on your configured rate limit, journey volume, retry behavior, and destination endpoint performance. Use rate limits that match what your destination can handle, and plan for processing time to vary with traffic and endpoint responsiveness.
# Managing journey webhooks
To view the journey webhooks that have been set up for your Iterable project, navigate to Integrations > Journey Webhooks. From this page, you can create, copy, edit, and delete journey webhooks.
# Viewing journey webhooks
To view the details for a particular journey webhook, click the arrow next to its name. You'll see information about the request body that's included with the webhook, as well as a list of journey tiles that rely on it.
# Copying journey webhooks
To make a copy of an existing journey webhook, click the overflow menu (three dots) for your desired webhook, and select Duplicate Webhook.
When you duplicate a journey webhook, the copy includes the same authentication settings as the original. However, sensitive authentication values (including secure custom header values) are stored securely and are not displayed in the UI for the duplicate. Be sure to keep your own record of any authentication keys or secrets you may need in the future.
When you clone a journey or tile that uses an endpoint created within the tile itself (not a saved journey webhook), don't assume sensitive authentication values entered directly in that configuration will be available in the new copy. This behavior does not apply to authentication configured on a saved journey webhook. If you need reusable authentication across journeys, configure it on a saved journey webhook and reference that webhook from your tiles.
# Editing journey webhooks
To edit an existing journey webhook, click the overflow menu (three dots) for your desired webhook, and select Edit Webhook.
When you edit an existing journey webhook, the changes take some time to propagate due to caching. For example, if you change the URL, Iterable may continue to send data to the old webhook URL for a short time after you update it.
# Deleting journey webhooks
To delete a journey webhook, click the overflow menu (three dots) for your desired webhook, and click Delete Webhook.
Before deleting a journey webhook, check whether any live journeys currently reference it. If a user reaches a Webhook or Live Data tile that references a deleted journey webhook, they'll exit the journey.
IMPORTANT
To prevent failed webhook calls and unintended user exits, journey webhooks that are referenced in Live Data tiles in published journeys cannot be edited or deleted. To make changes to a journey webhook that's in use, first stop or unpublish the journey, then edit the journey webhook and republish the journey.
# Testing journey webhooks
As you're setting up a journey webhook, it can be useful to test the requests it sends to make sure everything is working as you expect.
If you need a temporary endpoint for testing, try using a service like Webhook.site or RequestBin to set up a test webhook URL and check its incoming requests.
# Example journey webhook requests
Let's say you have a journey called "Welcome Sequence" (journey ID: 12345),
which uses the API call entry source and contains three journey webhooks. Here
are some sample calls and responses for this journey.
# POST request
Let's say you add a user to the Welcome Sequence journey by calling Iterable's
POST /api/workflows/triggerWorkflow
API endpoint, and you include this data in the request:
{ "workflowId": 12345, "email": "test.user@example.com", "dataFields": { "promotionId": 123, "color": "red" } }
If the journey contains a Webhook tile that makes a POST request,
that request includes the items contained in the dataFields of the triggering
API call, like this:
{ "promotionId": 123, "color": "red", "email": "test.user@example.com", "workflowId": 12345 }
# PUT request
Let's say you make a call to the Iterable POST /api/workflows/triggerWorkflow API endpoint that looks like
this:
{ "workflowId": 12345, "email": "user@example.com", "dataFields": { "promotionId": 123, "color": "red" } }
This call would add the user user@example.com to the Welcome Sequence journey.
If the journey contains a webhook that includes the user profile fields city
and state in the JSON request body, Iterable would send the following PUT
request when the user reaches that tile:
{ "city": "San Francisco", "state": "California", "promotionId": 123, "color": "red" }
# GET request
When you configure a User profile tile in a journey to retrieve data from a
webhook, it makes GET requests to the URL that you specify (appending
email as a query parameter). For example:
https://api.example.com/webhook/endpoint?email=user@example.com
This endpoint might return JSON data such as:
{ "firstName": "Test", "lastName": "User" }
After receiving this response, Iterable would update user@example.com to have
a firstName of Test and a lastName of User.
# Marketing example: Button-click journey with custom fields
Let's reuse the earlier POST /api/workflows/triggerWorkflow example. Imagine
you run a campaign where users click a Claim offer button in an email, and
that click triggers a journey webhook call to an external attribution or ad platform.
The journey is triggered with:
{ "workflowId": 12345, "email": "test.user@example.com", "dataFields": { "promotionId": 123, "color": "red", "buttonName": "Claim offer", "buttonUrl": "https://example.com/spring-sale" } }
Without a custom JSON body, the webhook payload can include:
{ "promotionId": 123, "color": "red", "buttonName": "Claim offer", "buttonUrl": "https://example.com/spring-sale", "email": "test.user@example.com", "workflowId": 12345 }
If you add a custom JSON body, Iterable adds those custom fields to the payload.
For example, you might add a custom JSON body that includes:
{ "campaignName": "Spring Launch 2026", "journeyStep": "clicked_offer_button", "marketingContext": { "channel": "email", "audienceSegment": "high_intent_returning_users" } }
And then Iterable sends the following payload to your destination.
{ "promotionId": 123, "color": "red", "buttonName": "Claim offer", "buttonUrl": "https://example.com/spring-sale", "email": "test.user@example.com", "workflowId": 12345, "campaignName": "Spring Launch 2026", "journeyStep": "clicked_offer_button", "marketingContext": { "channel": "email", "audienceSegment": "high_intent_returning_users" } }
# Want to learn more?
For more information about some of the topics in this article, check out these resources. Iterable Academy is open to everyone — you don't need to be an Iterable customer!
Iterable Academy
Support docs