Skip to content

Webhook

Webhooks let you send journey data to external services such as analytics, CRM systems, and marketing tools. You can:

  • Notify external systems when a customer takes an action in the journey
  • Send customer data to analytics tools
  • Trigger third-party emails, SMS, or WhatsApp on specific journey events

How to set up the Webhook element

Anchor link to

Add the Webhook element

Anchor link to

Drag-and-drop the Webhook element to the canvas. Place Webhook anywhere you’d like, keeping in mind what journey info you’re going to send to a third-party service.

Customer Journey canvas with a selected Amplitude webhook step after entry and wait elements

Name the Webhook step and specify the request URL and type

Anchor link to

In the STEP NAME field, enter a name for the webhook. It might be handy to name webhooks according to the services they send data to or the use case.

Next, in the URL field, specify the request URL to which the data should be sent. Next to the URL field, select the request type from the REQUEST TYPE dropdown: GET or POST.

Webhook configuration interface showing URL field and REQUEST TYPE dropdown for selecting GET or POST method

Configure headers

Anchor link to

In the HEADERS section, set the content type.

By default, the content type is application/json. If the service you’re sending the webhook to requires another content type, enter the appropriate one in the Content-Type header value.

Examples of content types are:

  • x-www-form-urlencoded
  • text/plain
  • text/xml

Add additional headers if needed by clicking + ADD HEADER. You can remove any header by clicking the ‘x’ icon next to it.

Add whatever authentication header your endpoint requires, for example:

  • Authorization: Bearer <token>
  • X-Api-Key: <key>
  • Authorization: Basic <base64(user:pass)>

Only a static secret in a header is supported. OAuth2 token-exchange flows, mTLS, and request signing on the Pushwoosh side aren’t supported. You can also restrict the endpoint to Pushwoosh’s IP addresses instead of, or in addition to, a header secret. See Pushwoosh IP addresses.

For HTTP Basic authentication specifically, do the following:

  1. Open a plain text editor and type your username and password with no spaces, separated by a colon. For example: <username>:<password>
  2. Encode this string into Base64.
  3. Copy the resulting Base64 string (for example, <base64-encoded-string>).
  4. In the webhook settings, add an Authorization header with the value: Basic <base64-encoded-string>. Make sure there is a space after the word “Basic”.
Authorization header example for Basic authentication in webhook settings showing Content-Type and Authorization headers

Mark a header value as secret

Anchor link to

Click the eye icon next to a header’s value to mask it. Pushwoosh hides that value everywhere it would otherwise leave the service: in the UI, in API responses, and in the journey’s version history.

Webhook headers list with a masked Authorization value and disabled eye icon next to an unmasked Content-Type value with an active eye icon
  • Automatic masking. Headers whose names look like credentials are masked automatically, even if you never click the eye icon. This includes Authorization, Proxy-Authorization, Cookie, Set-Cookie, and any name containing token, secret, password, credential, auth, or api-key/api_key/apikey (with a hyphen, an underscore, or no separator).
  • Change a masked value. Click into the field showing •••••••• and type the new value. There’s no button to reveal the stored value. The eye icon stays locked while the mask is shown. To remove the secret flag from a header, type a new value first, then click the icon.
  • Rename a masked header. Renaming a header whose value is currently shown as the mask clears that value. Enter it again under the new name. Renaming a header that currently holds a value you just typed keeps that value.

Add the JSON request body

Anchor link to

In the DATA section, enter your JSON request body. Make sure the request body is in correct JSON format.

Example:

{
"hwid": "{{device:hwid}}"
}

Use dynamic data and macros

Anchor link to

The DATA BUILDER panel allows you to insert dynamic information (such as user, device, tag, or event data) directly into your JSON request body. With Dynamic Data, you can include values specific to the individual user progressing through the journey.

For this:

  1. Select a category. You can pull data from three categories:
  • Device: Use Device data when you need technical information tied to the user’s device.

  • Tag: Use Tag data when you want to send information stored in the user profile.

  • Event: Use Event data when the webhook should send values from the triggering event of the journey.

  1. Select a parameter (for example, HWID, favourite category, etc.).
  2. Pushwoosh generates a macro that looks like this:
{{tag:Language}}
  1. Copy the macro and paste it into your JSON body in the DATA section.

When the webhook runs in a live journey, Pushwoosh automatically replaces the macro with the actual value for that user.

Insert Dynamic Data placeholders into the webhook request body

Type additional placeholders manually

Anchor link to

A placeholder is a macro you type by hand instead of generating it from a DATA BUILDER category. The DATA BUILDER panel only covers Device, Tag, and Event data. Type these placeholders directly into the URL, HEADERS, or DATA section instead. They don’t appear in the panel:

PlaceholderValue
{{application_code}}The application code of the app the traveler belongs to.
{{traveler:id}}The ID Pushwoosh assigns to this traveler for this journey run.
{{journey:uuid}}This journey’s UUID.
{{journey:name}}This journey’s name.
{{point:uuid}}This Webhook step’s UUID.
{{point:name}}This Webhook step’s STEP NAME.
{{event:name}}The name of the event that triggered this traveler’s entry into the journey.
{{device:platform}}The device’s platform, for example Android or iOS.
{{device:push_subscribed}}Whether the traveler is subscribed to push notifications — true or false.
{{now}}The current date and time, ISO 8601, UTC.
{{now:unix_ms}}The current time as Unix milliseconds.
{{tags:all}}Every tag value for the traveler’s device, as one JSON object. Use it unquoted, for example "user_properties": {{tags:all}}. Quoting it turns the object into an escaped string instead.

Keep a placeholder’s type in the JSON body

Anchor link to

A placeholder inside quotes always becomes a JSON string, whatever type the value actually is. The same placeholder on its own, with no quotes around it, keeps the value’s own type instead: a number stays a number, true/false stays a boolean, and a list becomes a JSON array. An unquoted placeholder must be the field’s entire value — "age": {{tag:Age}} works, but "note": prefix{{tag:Age}}suffix doesn’t, because everything outside quotes is written out exactly as typed and the extra characters break the JSON.

{
"age": {{tag:Age}},
"age_as_text": "{{tag:Age}}"
}

Here age sends the tag’s numeric value (34), while age_as_text sends the string "34". Use whichever the field on the receiving end expects. If the tag has no value, an unquoted placeholder still resolves to an empty string, not a number or false. See the note under Add the JSON request body.

Map webhook response data to variables

Anchor link to

Besides sending data out, the Webhook step can keep values from the reply your service sends back. You give each value a name (Attribute). Later steps can use that name the same way they use other webhook response values. For example, set a Tag with Update user profile, or schedule a Time Delay from a date the service returned. For a full journey example, see Using webhook response data in your journey.

Example: the CRM returns a user ID. You store it as Attribute crm_user_id. Then Update user profile writes it to a Tag.

Before you map anything, get one sample response from the service. Ask your developer, or open a successful call in Calls log after a test and look at the response body. You need the field names from that reply to build Path.

In the RESPONSE MAPPING section, click + ADD MAPPING and fill in two fields for each value you want to capture:

  • Path: the location of the value inside the response JSON body, with dots between levels
  • Attribute: the name you will use later in the journey
Response mapping section with Path and Attribute fields and Add mapping button in webhook settings

For example, if your CRM responds with:

{
"data": {
"user": {
"id": "789xyz"
}
}
}
  1. Set Path to data.user.id.
  2. Set Attribute to crm_user_id.

After a user passes this step, later elements can pick Attribute crm_user_id the same way they pick other webhook response values.

Condition split cannot use them directly. Mapped webhook values have no type. Save the value as a Tag first, then branch on that Tag. See Compare a webhook value in Condition split.

For a single field, Path and values work like this:

Map every element of an array

Anchor link to

Sometimes a webhook response doesn’t have just one value. It has a list, like every product in an order, every item in a cart, or every result from a search. Response mapping normally captures one value per field, so without this you’d only get one mapped value from that list, and the rest would be lost.

Put * in the Path field where the list is. Pushwoosh then grabs a value from every item in the list, not just one position. For example, if the list is called items and each item has item_name, set Path to items.*.item_name.

In RESPONSE MAPPING, click + ADD MAPPING and fill in the two fields as usual, with * marking the list:

  • Path: the location of the value inside the response, with * where the list is. Example: items.*.item_name.
  • Attribute: the name you’ll use later. What you write here decides how you get the results back:
    • Include {n} in the name, for example item_{n}, to get each item as its own value, numbered from 1: item_1, item_2, item_3, and so on. {n} can sit anywhere in the name, for example item_{n}_sku.
    • Leave {n} out, for example item_names, to join every item into one value, separated by commas: Sofa, Lamp, Rug.
Response mapping row with Path set to items.*.item_name and Attribute set to item_{n}

Path list positions start at 0 (items.0.item_name is the first item). Attribute names built with {n} start at 1 (item_1 is that first item). These are two different numberings.

If you only need one item from the list, use a number in Path instead of *, for example items.0.item_name.

If your CRM responds with:

{
"items": [
{ "item_name": "Sofa" },
{ "item_name": "Lamp" },
{ "item_name": "Rug" }
]
}
  • Set Path to items.*.item_name and Attribute to item_{n} to get three separate values: item_1 is Sofa, item_2 is Lamp, item_3 is Rug.
  • Set Attribute to item_names instead to get one value: item_names is Sofa, Lamp, Rug.

You can use the mapped values later in the journey like any other webhook response attribute:

Condition split cannot use them directly. Mapped webhook values have no type. Save the value as a Tag first, then branch on that Tag. See Compare a webhook value in Condition split.

Timeout, retries, and failed requests

Anchor link to

Pushwoosh waits up to 10 seconds for a response. The whole Webhook step, including sending the request and processing the response, is capped at 30 seconds.

On a 500, 502, 503, or 504 response, or a network error such as a connection failure, Pushwoosh retries the request once before giving up. A request that times out is not retried — see What happens when a request fails below. Any other non-2xx response is not retried either.

Rate limits

Anchor link to

Pushwoosh limits how many webhook requests an account can send per second. The limit is sized well above real traffic peaks, so normal journeys aren’t affected. A burst that exceeds it waits briefly for room before failing.

Endpoint cooldown

Anchor link to

If an endpoint fails several times in a row, Pushwoosh stops sending it requests for a while instead of retrying a broken endpoint on every traveler, starting at 30 seconds and doubling on further failures up to 5 minutes. A single successful request clears this and resumes normal delivery.

What happens when a request fails

Anchor link to

The Webhook element has no separate branch for failed requests. Any of the following drops the traveler from the journey at this step:

CauseWhat triggers it
Blocked endpoint addressThe URL is private, internal, loopback, or link-local, including cloud metadata endpoints
Rate limitThe account’s per-second webhook request limit is exceeded and no room opens up during the brief wait
Endpoint cooldownThe endpoint failed several times in a row and Pushwoosh is temporarily skipping it
TimeoutNo response within 10 seconds, or the step exceeds its 30-second cap
Network errorThe request couldn’t reach the endpoint at all
Non-2xx responseThe endpoint returned an error status that isn’t retried, or was retried once and failed again

See Request error.

If you can’t afford to lose travelers here, have your endpoint always return a 2xx response and put any failure state in the response body instead, for example as a value your Response mapping can pick up.

This applies to every Webhook step, including ones created earlier. An endpoint address that now matches the blocked-address rule above will start failing the same way.

Unlike a failed request, a response that arrives but doesn’t map cleanly, such as invalid JSON, an unresolved Path, or a body over 64 KB, does not drop the traveler. See the note under Response mapping above.

Test the Webhook

Anchor link to

Click Test webhook to verify that your webhook configuration is correct and the request is sent successfully.

If a header still shows the stored mask, Pushwoosh fills in the real, saved value for the test request. The value never appears in your browser.

This substitution only works for a header already saved on this exact step. A step you haven’t saved yet, or one you just copied, has no saved value behind the mask, so Pushwoosh sends the test request without that header.

After a successful test (or a live call), open Calls log, expand the row, and compare the response body with each Path. The field must exist exactly as in Path. If the request succeeds but a later step has no value, Path usually does not match the response. The Webhook step will not show an error for that.

Save your configuration

Anchor link to

Click Save to save your webhook configuration.

Open the Calls log tab in the point’s drawer to see what Pushwoosh actually sent for this step: time, user, result, and duration, going back 30 days.

Filter by result (Success, HTTP error, No response) or search by the exact User ID or HWID. Click a row to expand it and see the request (method, URL, and body) and, depending on the outcome, either the response (status and body) or the error text. Duration covers the whole step, including time spent on an automatic retry.