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

# Webhook trigger

> Start a Nedzo workflow from any external system using an HTTP POST request. Connect tools like Zapier, Make, n8n, or your own backend to trigger automations.

Fires when an external system sends an HTTP POST request to the workflow's unique webhook URL. Use this to connect Nedzo to any external tool — Zapier, Make, n8n, your own backend, or anything that can send HTTP requests.

## When it fires

The trigger fires immediately when a valid POST request hits the webhook URL. Each request creates one workflow execution. Unlike other triggers, webhook triggers are not deduplicated — every request runs the workflow.

## Configuration

When you add a webhook trigger, Nedzo generates two things:

| Field             | Description                                                                                              |
| ----------------- | -------------------------------------------------------------------------------------------------------- |
| **Webhook URL**   | The unique URL to send requests to. This stays the same even if you regenerate the token or change auth. |
| **Webhook token** | A 64-character secret embedded in the URL path. Cryptographically identifies the caller.                 |

### Regenerating the token

Click **Regenerate** in the trigger settings to create a new token. The old token stops working immediately. The URL stays the same.

### Authorization

The 64-character token in the URL authenticates the caller by itself. For extra protection, configure an additional authorization scheme from the trigger settings.

| Scheme             | How it works                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **None** (default) | Token in the URL is the only credential. Fine for most integrations — the token is 256 bits of entropy.                                                             |
| **HMAC**           | Caller signs each request with a shared secret using HMAC-SHA256. Timestamp-bound so replayed requests are rejected. Recommended if you need tamper-proof payloads. |
| **Bearer**         | Caller sends a static token in the `Authorization: Bearer <token>` header.                                                                                          |
| **Basic**          | Caller sends `Authorization: Basic <base64(user:pass)>` using HTTP Basic auth.                                                                                      |

Failed auth attempts are rate-limited per trigger. After too many bad attempts in a short window the trigger returns `401` until the cooldown ends.

#### HMAC details

When HMAC is selected, Nedzo gives you a shared secret and two header names (defaults: `X-Nedzo-Signature`, `X-Nedzo-Timestamp`). To sign a request:

1. Take the current Unix timestamp in seconds (e.g. `1730000000`).
2. Build the signed string: `{timestamp}.{rawRequestBody}`.
3. Compute `HMAC-SHA256(secret, signedString)` and hex-encode the result (64 characters).
4. Send the hex digest in the signature header and the timestamp in the timestamp header.

Example (Node.js):

```javascript theme={null}
import crypto from 'crypto';

const timestamp = Math.floor(Date.now() / 1000);
const body = JSON.stringify({ firstName: 'John', phone: '+14155551234' });
const signature = crypto
  .createHmac('sha256', SHARED_SECRET)
  .update(`${timestamp}.${body}`)
  .digest('hex');

await fetch(webhookUrl, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Nedzo-Timestamp': String(timestamp),
    'X-Nedzo-Signature': signature,
  },
  body,
});
```

Requests with a timestamp more than 5 minutes off from the server clock are rejected. Each signature can only be used once inside the tolerance window (replay protection).

### Sample payload

The trigger UI lets you capture a **sample payload**. Send a test request to the webhook URL, and Nedzo captures the payload structure. This is used to:

* Show available fields in the condition builder
* Provide autocomplete for variable references in actions
* Auto-detect contact fields (email, phone) for downstream contact-scoped actions

### Contact field mapping

If your workflow has a [Make phone call](/workflows/actions/voice-call), [Send SMS](/workflows/actions/send-sms), [Send email](/workflows/actions/send-email), or [Update contact](/workflows/actions/update-contact) action set to "Contact", the webhook payload itself supplies the contact data — there's no contact in scope at trigger time.

The trigger config shows a **Contact fields** section with one row per required field, based on which actions you've added downstream. The required fields depend on the action:

* **Send SMS / Make phone call** require **Phone**.
* **Send email** requires **Email**.
* **Update contact** (create or update) requires an **identifier** — either **Phone** or **Email**. Update contact resolves the contact by phone first, then email, so mapping either one is enough. The workflow no longer silently creates a blank contact when neither is present.

For each field, Nedzo:

1. **Auto-detects** common field names from the captured sample payload — `email` / `emailAddress` / `email_address` for Email, and `phone` / `phoneNumber` / `phone_number` for Phone. Auto-detection is case-insensitive and works on top-level fields.
2. **Falls back to a dropdown** of every string-valued path in the sample payload, so you can map a non-standard field. Useful for nested values like `data.lead.cell` or aliases like `mobile`.

If a required field can't be resolved (no auto-detect match, no explicit mapping, no `contactId` in the payload), the **Publish** button is disabled with a tooltip explaining what's missing, and the webhook trigger node shows an attention indicator on the canvas. Once every required field is resolvable, Publish becomes available.

#### Bypass mapping with `contactId`

If your payload includes a `contactId` that matches an existing contact, that takes precedence — the Email and Phone mapping is bypassed entirely and the contact is loaded from the database. Useful when the upstream system already knows the Nedzo contact ID.

```json theme={null}
{
  "contactId": "a1b2c3d4-..."
}
```

## Sending a request

Send an HTTP POST with a JSON body. The 64-character token is part of the URL path:

```bash theme={null}
curl -X POST "https://api.nedzo.ai/webhooks/trigger/{webhook-token}" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "John",
    "lastName": "Doe",
    "phone": "+14155551234",
    "email": "john@example.com",
    "dealValue": 5000,
    "source": "website-form"
  }'
```

If the trigger is configured with HMAC, Bearer, or Basic auth, add the required headers as well — see the [Authorization](#authorization) section above.

### Request requirements

| Requirement        | Details                                                    |
| ------------------ | ---------------------------------------------------------- |
| Method             | POST only                                                  |
| Content-Type       | `application/json`                                         |
| Body               | Valid JSON, max 1 MB                                       |
| Extra auth headers | Only required if the trigger has HMAC/Bearer/Basic enabled |

## Data available

Every field you send in the request body is available as a variable using the `{{trigger.webhook.body.*}}` pattern.

For the example payload above:

| Variable                             | Value              |
| ------------------------------------ | ------------------ |
| `{{trigger.webhook.body.firstName}}` | `John`             |
| `{{trigger.webhook.body.lastName}}`  | `Doe`              |
| `{{trigger.webhook.body.phone}}`     | `+14155551234`     |
| `{{trigger.webhook.body.email}}`     | `john@example.com` |
| `{{trigger.webhook.body.dealValue}}` | `5000`             |
| `{{trigger.webhook.body.source}}`    | `website-form`     |

If you include a `contactId` in the payload and it matches an existing contact, the contact's standard fields (`{{firstName}}`, `{{phone}}`, etc.) are also loaded. Otherwise, contact-scoped actions resolve the contact from the email/phone fields you mapped in the trigger config — see [Contact field mapping](#contact-field-mapping) above.

## Use case examples

### Trigger from Zapier on form submission

Connect a Zapier Zap to send form data to Nedzo and call the lead.

1. **In Zapier:** Create a Zap with your form tool as trigger, and a Webhook action pointing to your Nedzo webhook URL
2. **Map fields:** firstName, lastName, phone, email
3. **In Nedzo:**
   * **Trigger:** Webhook
   * **Action:** [Update contact](/workflows/actions/update-contact) — Create contact from webhook data
     * First name: `{{trigger.webhook.body.firstName}}`
     * Phone: `{{trigger.webhook.body.phone}}`
     * Tags: "website-lead"
   * **Action:** [Wait](/workflows/actions/wait) — 2 minutes
   * **Action:** [Voice call](/workflows/actions/voice-call) — Sales agent

### Trigger from your backend on deal close

Your app sends a POST when a deal closes.

1. **Trigger:** Webhook
2. **Action:** [Send SMS](/workflows/actions/send-sms)
   * Message: *"Congrats `{{trigger.webhook.body.firstName}}`, your deal is confirmed! We'll be in touch with next steps."*
3. **Action:** [Slack message](/workflows/actions/slack-message) — #wins
   * Message: *"Deal closed: `{{trigger.webhook.body.firstName}}` `{{trigger.webhook.body.lastName}}` — \$`{{trigger.webhook.body.dealValue}}`"*

### Trigger from Make (Integromat)

Use a Make HTTP module to call the webhook URL after any automation step.

1. **In Make:** Add an HTTP "Make a request" module
2. **URL:** Your Nedzo webhook URL
3. **Method:** POST
4. **Body:** JSON with your data
5. **In Nedzo:** Build any workflow using the incoming data

### Manual trigger via API

Use the webhook URL from your own code or Postman for testing or custom integrations.

```javascript theme={null}
const response = await fetch(webhookUrl, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    contactId: 'existing-contact-id',
    customData: 'anything you need'
  })
});
```
