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

# Webhook step

> Call another system from an automation: send a POST with your own headers and JSON body, test it, and see what came back.

A **Webhook** step lets an automation call another system. When a contact reaches the step in a live automation, Senderz sends one HTTP `POST` to the URL on the step. The contact then moves on to the next step, whatever the answer was.

If you are moving from Klaviyo, this works like Klaviyo's webhook action: a destination URL, headers, and a JSON body. The merge tag syntax is different, so read [Merge tags](#merge-tags) before you copy a body across.

<Info>
  Each contact gets one call. Senderz does not retry a failed call, and the
  request times out after 10 seconds. The automation carries on either way.
</Info>

<Note>
  To send Senderz events to your own system with signatures and retries, use
  [outbound webhooks](/en/integrations/webhooks-outbound) instead.
</Note>

## Set up the step

<Steps>
  <Step title="Add the step">
    Drag **Webhook** from the palette onto the canvas, or add it from the **+**
    on a connector between two steps.
  </Step>

  <Step title="Enter the POST URL">
    Paste the address you want to call. It must start with `https://`.
  </Step>

  <Step title="Add headers">
    Add one row per header, for example `x-api-key` with the API key from your
    provider.
  </Step>

  <Step title="Write a body, or leave it empty">
    Leave **JSON body** empty to send the default Senderz payload, or write your
    own.
  </Step>

  <Step title="Test it">
    Press **Test** in the automation editor and run the automation for one
    contact. See [Test the step](#test-the-step).
  </Step>
</Steps>

## POST URL

The address must be a public `https://` address on the default port, so leave any port number out of it.

* Private, local and internal addresses are refused. Senderz checks the address when you save the step, and again right before each call.
* Redirects are not followed. If the server answers with a 3xx status, the call is recorded as **Redirected**. Enter the final address, not one that redirects to it.

## Headers

Headers are key and value rows. For example, `x-api-key` carries an API key.

* `Content-Type: application/json` is sent by default. Add your own `Content-Type` row to override it.
* Connection headers such as `Host`, `Content-Length`, `Connection` and `Transfer-Encoding` are not allowed.
* A value must fit on one line, with no Hebrew letters or emoji.
* The step shows how many header rows you can add and how long each value can be.
* Merge tags work in header values.

After you save, header values are shown as `********`. Saving the step again keeps the stored value. To change it, type a new value.

## JSON body

Leave the body empty and Senderz sends this default payload:

```json theme={"system"}
{
  "event": "flow.webhook",
  "flowId": "…",
  "enrollmentId": "…",
  "contact": { "id": "…", "email": "…", "phone": "…", "firstName": "…", "lastName": "…" },
  "timestamp": "2026-09-15T10:27:01.000Z"
}
```

If you write your own body:

* It must be valid JSON.
* Every merge tag must sit inside double quotes, for example `"{{ email }}"`.
* It can be up to 20,000 characters as written, and must stay under about 100 KB once the tags are filled in.

Tag values are escaped for you, so a quote mark or a line break in a contact's name cannot break the JSON.

If the body does not produce valid JSON when the step runs, nothing is sent and the call is recorded as **Invalid body**.

## Merge tags

Merge tags work in header values and in the body.

| Tag | What it holds |
| - | - |
| `{{ email }}` | The contact's email address |
| `{{ phone }}` | The contact's phone number, in international format with a plus, for example `+972501234567` |
| `{{ firstName }}` | The contact's first name |
| `{{ lastName }}` | The contact's last name |
| `{{ custom.<key> }}` | A custom contact field, where `<key>` is the field's key |
| `{{ orderId }}`, `{{ amount }}`, `{{ currency }}` | Values from the event that started the automation. These three are examples from order triggers |

Add a fallback after a pipe. `{{ firstName|friend }}` uses `friend` when the contact has no first name.

<Warning>
  Klaviyo tags such as `{{ person.phone_number|default:'' }}` do not work in
  Senderz. Replace them with the Senderz tags above, for example `{{ phone }}`.
</Warning>

## Example: send a WhatsApp template through your provider

This setup sends a WhatsApp template through a messaging provider's API. The address, template ID and field names below are placeholders. Take the real ones from your provider.

**POST URL**

```text theme={"system"}
https://api.example-provider.com/sendWhatsAppMessage
```

**Headers**

| Key | Value |
| - | - |
| `x-api-key` | Your provider's API key |

**JSON body**

```json theme={"system"}
{
  "templateId": "1234567890",
  "receiverPhoneNumber": "{{ phone }}",
  "templateParams": ["{{ firstName|friend }}"]
}
```

Check with your provider which phone format it expects and which parameters the template needs. Senderz sends phone numbers with a plus, for example `+972501234567`.

## Test the step

Press **Test** in the automation editor, choose a contact, and run the test. The automation runs once for that contact.

* Webhook steps are called for real, with that contact's details. If the step sends a message through your provider, that contact receives it, so test with your own contact first.
* Test runs skip waits.
* The test dialog shows what each webhook step got back: the HTTP status, how long the call took, and the start of the response.

## Check live calls

Select the Webhook step in the editor to see **Recent calls** for the last 30 days. It shows how many calls were delivered, and for each recent call:

* the result
* the HTTP status
* how long the call took
* whether it was a live call or a test
* the start of the response

Parts of the response that look like email addresses or phone numbers are hidden.

| Result | Meaning |
| - | - |
| Delivered | The server answered with a 2xx status |
| Rejected | The server answered with a 4xx or 5xx status |
| Redirected | The server answered with a 3xx status. Redirects are not followed |
| No response | The request timed out, or there was a network error |
| Address blocked | The URL was refused by the safety check |
| Invalid body | The body did not produce valid JSON, so nothing was sent |

## Troubleshooting

<AccordionGroup>
  <Accordion title="Rejected with 400, 401 or 403" icon="circle-question">
    This usually means the API key is wrong or missing, or the provider does
    not accept the body. Read the start of the response under **Recent calls**
    or in the test dialog.
  </Accordion>

  <Accordion title="No response" icon="circle-question">
    The server did not answer within 10 seconds.
  </Accordion>

  <Accordion title="Invalid body" icon="circle-question">
    A merge tag is outside quotes, or the JSON is broken. Put every tag inside
    double quotes and check the brackets and commas.
  </Accordion>

  <Accordion title="Nobody entered the automation" icon="circle-question">
    Check the trigger and the entry settings. With **Enter only once**, a
    contact who already went through the automation cannot enter it again. And
    adding someone to a list they are already on does not count as joining
    that list. See [Flow triggers](/en/messaging/flows/triggers).
  </Accordion>
</AccordionGroup>

## Keep API keys safe

Treat API keys like passwords. Do not paste them into chats or screenshots. If a key was shared, ask your provider for a new one.

## Related

<CardGroup cols={2}>
  <Card title="Flow steps and actions" icon="layer-group" href="/en/messaging/flows/steps">
    Every block you can place in an automation.
  </Card>

  <Card title="Flow triggers" icon="bolt" href="/en/messaging/flows/triggers">
    What starts an automation, trigger filters and re-entry rules.
  </Card>

  <Card title="Publishing and monitoring" icon="chart-simple" href="/en/messaging/flows/publish-and-monitor">
    Publish checks, test runs and the per-step reports.
  </Card>

  <Card title="Outbound webhooks" icon="arrow-right-from-bracket" href="/en/integrations/webhooks-outbound">
    Send Senderz events to your own systems as signed HTTPS deliveries.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.