> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Journey webhooks

> Send HTTP requests from Journey steps to external servers, personalized with user data via Liquid syntax, with retry logic and debugging tools.

Journey webhooks send HTTP requests from a Journey step to your servers or any publicly accessible endpoint. You configure the HTTP method, URL, headers, and body content. Requests can be personalized with user-specific data using [Liquid syntax](./using-liquid-syntax), so you can sync Journey events with CRMs, analytics platforms, or custom backends in real time.

## Requirements

* [Contact the OneSignal sales team](https://onesignal.com/contact) for access.
* The URL or IP address must be valid and reachable over HTTP or HTTPS.
* Endpoints must be **publicly routable** — not behind a firewall or on localhost.
* Domains must have a valid top-level domain (`.com`, `.org`, `.net`, etc.).

<Warning>
  Journey webhooks cannot call OneSignal APIs. They are designed for sending data to external systems only.
</Warning>

***

## Setup

Once your Journey is created, follow these steps:

1. Navigate to **Data > Webhooks** in the OneSignal dashboard.
2. Click to create a new webhook.
3. Define the following:
   * **HTTP method** (usually `POST`)
   * **Target URL**
   * **Custom headers** (for example, authentication tokens)
   * **Body content** (plain text or JSON, optionally using Liquid)

<Frame caption="Webhook configuration screen">
  <img src="https://mintcdn.com/onesignal/Xl2NHJvxakrK4JbL/images/docs/f4bb77a-Screenshot_2023-04-20_at_9.24.03_PM.png?fit=max&auto=format&n=Xl2NHJvxakrK4JbL&q=85&s=79005b0332776e4703954789fe40c5ea" alt="Webhook setup form with HTTP method, URL, headers, and body fields" width="1038" height="926" data-path="images/docs/f4bb77a-Screenshot_2023-04-20_at_9.24.03_PM.png" />
</Frame>

### Disallowed headers

You cannot set the following headers:

* `content-length`
* `referer`
* `metadata-flavor`
* `x-google-metadata-request`
* `host`
* Any header starting with `x-onesignal`

### Testing webhooks

You can also test your endpoint manually using a tool like curl:

```bash theme={null}
curl -X POST https://yourserver.com/webhook \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "abc123" }'
```

This validates that your endpoint is reachable and functioning before you add it to a Journey.

***

## Personalization

All webhook fields support [Liquid syntax](./using-liquid-syntax), allowing you to dynamically insert user and subscription data into the request.

See [Message personalization](./message-personalization) for the full list of available properties.

### User data reference

The following properties from the `user` object are available in webhook fields via Liquid syntax:

| Property     | Type   | Usage                               | Available in Test? |
| ------------ | ------ | ----------------------------------- | ------------------ |
| OneSignal ID | String | `{{ user.onesignal_id }}`           | ✅                  |
| External ID  | String | `{{ user.external_id }}`            | ✅                  |
| Tags         | Object | `{{ user.tags.your_tag_key_here }}` | ❌                  |
| Language     | String | `{{ user.language }}`               | ✅                  |

<Warning>
  Tags are not available when testing webhooks outside an active Journey. Use [Test Users](./test-users) to validate behavior before going live.
</Warning>

***

## Adding a webhook to a Journey

1. After creating and testing your webhook, open your Journey.
2. Add a **Webhook step** where needed.
3. Select the webhook you configured earlier.

Each time a user reaches that step, the webhook fires with their personalized data.

<Frame caption="Webhook step within a Journey">
  <img src="https://mintcdn.com/onesignal/0qspEXXeJ8zJbkJ-/images/docs/7e6782f-Screenshot_2023-04-20_at_3.54.09_PM.png?fit=max&auto=format&n=0qspEXXeJ8zJbkJ-&q=85&s=e017a2eff865b6de671a34b71c437332" alt="Journey canvas with a webhook step connected between message steps" width="664" height="1058" data-path="images/docs/7e6782f-Screenshot_2023-04-20_at_3.54.09_PM.png" />
</Frame>

***

## Debugging and logs

### Webhook stats

Go to the webhook’s **Stats** tab to view how your webhook is performing. This includes:

* Total events sent
* Response time trends
* Status code distribution

<Frame caption="Webhook stats showing event count and response times">
  <img src="https://mintcdn.com/onesignal/Xl2NHJvxakrK4JbL/images/docs/f0cfcb8-small-Screenshot_2023-04-21_at_9.04.57_AM.png?fit=max&auto=format&n=Xl2NHJvxakrK4JbL&q=85&s=f4514d8cecf78831bd55019cfce61b9b" alt="Webhook stats tab with event count, response time trends, and status code distribution" width="1185" height="1007" data-path="images/docs/f0cfcb8-small-Screenshot_2023-04-21_at_9.04.57_AM.png" />
</Frame>

### Logs tab

For more granular insights, the Logs tab displays:

* Recent request/response data
* Status codes and error messages
* Headers and payloads (where applicable)

<Frame caption="Webhook logs showing request and response details">
  <img src="https://mintcdn.com/onesignal/jBdBk5XvQR5eKOks/images/docs/729c24f-Screenshot_2023-04-20_at_9.27.31_PM.png?fit=max&auto=format&n=jBdBk5XvQR5eKOks&q=85&s=3923dcf3fbf2975e27b5f158a1442824" alt="Webhook logs tab with recent requests, status codes, and payloads" width="2130" height="1264" data-path="images/docs/729c24f-Screenshot_2023-04-20_at_9.27.31_PM.png" />
</Frame>

***

## Retry logic and disabling behavior

OneSignal retries failed webhook requests for recoverable errors (like `429 Too Many Requests`). Retries use increasing delays between attempts.

If retries repeatedly fail, the webhook is marked as **permanently failed** and no further attempts are made.

### Automatic disabling

If a webhook consistently fails, OneSignal disables it to prevent further issues. Admins receive email alerts and a dashboard banner before and after disabling. Troubleshoot the root cause and test the webhook before re-enabling it.

Your endpoint must handle the volume of events your Journey produces. Review your app’s message send volume to estimate the throughput your API needs.

### Performance guidance

* Slow or overloaded endpoints (especially those returning `429` responses) can trigger automatic disabling.
* Record events quickly and defer additional processing to avoid timeouts.
* Volume scales with user activity — ensure your endpoint can handle peak throughput.
* Use the `event.id` value (available as a header or in the body) to deduplicate incoming events.

### Reliability best practices

* **Route through your own server first**, not directly to third-party services. This gives you control over logging, authentication, throttling, and queuing. Third-party services may rate-limit or block requests during volume spikes, and OneSignal does not manage those limits.
* **Plan for bursts.** Webhook execution happens immediately when users reach the step. If many users hit a webhook step at once, OneSignal sends a burst of HTTP requests without rate limiting. Consider building a lightweight API layer or queuing proxy that can ingest webhooks reliably, apply rate limits or batching, and route requests to third-party services gracefully.
* **Review third-party API limits.** Popular tools like Slack, Twilio, and Segment offer public HTTP APIs but have their own rate limits, authentication requirements, and error handling. Check their documentation before connecting directly.

***

## Securing your webhook

To verify that requests are coming from OneSignal, add a custom authorization header (for example, `Authorization: Bearer YOUR_SECRET_TOKEN`) and verify it server-side before accepting the request.

***

## FAQ

### Can I use webhooks to call OneSignal APIs?

No. Journey webhooks are designed for sending data to external systems only. They cannot call OneSignal APIs. To update OneSignal data based on Journey events, route the webhook through your own server and have your server call the OneSignal API.

### Why was my webhook automatically disabled?

OneSignal disables webhooks that consistently fail to prevent further issues. Check the **Logs** tab for error details (status codes, response bodies), fix the root cause, and test the webhook before re-enabling it. Common causes include endpoint downtime, authentication errors, and rate limiting.

### How do I deduplicate webhook events?

Use the `event.id` value (available as a header or in the request body) to identify unique events. Store processed event IDs on your server and skip any duplicates. This is especially important if retries deliver the same event more than once.

### Are webhook requests rate limited by OneSignal?

No. OneSignal sends webhook requests immediately as users reach the step, without rate limiting. If many users hit a webhook step at once, your endpoint receives a burst of requests. Build your endpoint to handle peak volume, or use a queuing proxy to buffer and batch requests.

### Can I test webhooks without launching a Journey?

Yes. Use the **Test** button in the webhook configuration to send a sample request. Note that [tags](./add-user-data-tags) are not available in test requests — use [Test Users](./test-users) in a live Journey for full validation.

***

## Related pages

<Columns cols={2}>
  <Card title="Journeys overview" icon="route" href="./journeys-overview">
    Introduction to Journeys and how multichannel workflows operate.
  </Card>

  <Card title="Journey actions" icon="code-branch" href="./journeys-actions">
    Add wait steps, branching logic, time windows, and split paths.
  </Card>

  <Card title="Liquid syntax" icon="code" href="./using-liquid-syntax">
    Full reference for Liquid templating in OneSignal messages and webhooks.
  </Card>

  <Card title="Message personalization" icon="user-pen" href="./message-personalization">
    Overview of all personalization methods including tags, Liquid, and dynamic content.
  </Card>
</Columns>
