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

# What is a webhook and how to create one

> Send real-time data from Maestra Platform to any external service the moment something happens to a customer.

A webhook is a small, automated message Maestra fires off to another service the instant something happens — a customer signs up, places an order, hits a loyalty tier, abandons a cart. Instead of the other service constantly asking "anything new yet?", Maestra Platform tells it directly.

## What you can do with webhooks

<CardGroup cols={2}>
  <Card title="Trigger external workflows" icon="bolt">
    Notify your helpdesk, warehouse, or any other tool the moment a customer takes an action.
  </Card>

  <Card title="Sync data in real time" icon="arrows-rotate">
    Push customer attributes, order details, or loyalty status to any third-party tool.
  </Card>

  <Card title="Send to custom channels" icon="paper-plane">
    Reach Telegram, WhatsApp Business, voice bots, or any service with an API.
  </Card>

  <Card title="Pull data into flows" icon="arrow-down-to-line">
    Use the webhook's response to branch a flow or personalize the next message.
  </Card>
</CardGroup>

## How it works

Setting up a webhook in Maestra Platform is a two-part job:

<Steps>
  <Step title="Create an integration">
    The integration holds everything shared across your webhooks for a given service: the base URL, auth headers, and rate limits.
  </Step>

  <Step title="Create the webhook itself">
    The webhook defines the specific endpoint, HTTP method, request body, and what to do with the response. You can have many webhooks under one integration.
  </Step>
</Steps>

Think of the integration as the "account connection" and the webhook as an individual "action."

***

# Part 1: Create the integration

<Steps>
  <Step title="Click Integrations → Integration Setup → Create Integration">
    <Frame>
      <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-05-29-at-14.09.57-2.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=8dcd78230d239cabaa050ae7465c47a8" alt="Screenshot 2026 05 29 At 14 09 57 2" width="2936" height="756" data-path="images/Screenshot-2026-05-29-at-14.09.57-2.png" />
    </Frame>
  </Step>

  <Step title="Pick the Webhook Integration preset">
    <Frame>
      <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-05-29-at-14.12.07-1.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=a7166c400fb729763bf24fe016531b22" alt="Screenshot 2026 05 29 At 14 12 07 1" width="2916" height="1462" data-path="images/Screenshot-2026-05-29-at-14.12.07-1.png" />
    </Frame>
  </Step>

  <Step title="Name your integration">
    Use a descriptive name so you'll know what it does at a glance — like `Shopify Order Sync` or `Zendesk Ticket Webhooks`.

    <Frame>
      <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-06-01-at-12.02.23.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=9a99986f859a8a2e63adce8ed02d5b9b" alt="Screenshot 2026 06 01 At 12 02 23" width="2934" height="668" data-path="images/Screenshot-2026-06-01-at-12.02.23.png" />
    </Frame>
  </Step>

  <Step title="Set the root URL">
    This is the base URL all webhooks in this integration will share. You can either:

    * Put the **full URL** here and leave the path blank on individual webhooks, or
    * Put only the **root** here and let each webhook add its own path — the usual setup when one integration covers many different methods. Less typing, fewer typos, and if the base ever changes, you update it in one place instead of touching every webhook.

    <Tip>
      **Example:** For example, Maestra Platform's own API uses `https://api.maestra.io/v3/operations/` as the root. You set that once on the integration, and each webhook just adds the part that's unique to its method.\\

      <Frame>
        <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-06-03-at-16.20.20.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=ee8f0db1d6d0e521ebbfec8725919ddd" alt="Screenshot 2026 06 03 At 16 20 20" width="1794" height="278" data-path="images/Screenshot-2026-06-03-at-16.20.20.png" />
      </Frame>
    </Tip>
  </Step>

  <Step title="Set a request rate limit (optional)">
    If the receiving service has a rate cap, set the max requests per second here. Every webhook in this integration counts toward the same limit.

    <Note>
      The actual rate can vary slightly due to network delays. Set the limit a little below what the service can handle to stay safely under the cap.
    </Note>

    <Frame>
      <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-06-03-at-16.39.53.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=64c4e0cbe12dd4dc19e9a467b92c24be" alt="Screenshot 2026 06 03 At 16 39 53" width="2932" height="1034" data-path="images/Screenshot-2026-06-03-at-16.39.53.png" />
    </Frame>
  </Step>

  <Step title="Add headers">
    Add the headers your service needs. The two you'll almost always set are **Authorization** (your API key or token) and **Content-Type** (usually `application/json`).

    <Tip>
      Mark any header containing a token or secret as **secret**. Teammates without integration access won't be able to see the value when they're building webhooks.
    </Tip>

    <Frame>
      <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-06-03-at-16.46.35.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=6817e09cb99f21b3977cfcc9d024080c" alt="Screenshot 2026 06 03 At 16 46 35" width="2754" height="1494" data-path="images/Screenshot-2026-06-03-at-16.46.35.png" />
    </Frame>
  </Step>

  <Step title="Click Create to save the integration.">
    <Frame>
      <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-06-03-at-16.56.04.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=6d108f58947e530b938a5d81dedffde7" alt="Screenshot 2026 06 03 At 16 56 04" width="2910" height="384" data-path="images/Screenshot-2026-06-03-at-16.56.04.png" />
    </Frame>
  </Step>

  <Step title="Add your first webhook">
    Your integration is ready. Click **Create** to add your first webhook to it.\\

    <Frame>
      <img src="https://mintcdn.com/maestraio/gIiLYvn2bftMwXhD/images/Screenshot-2026-06-03-at-16.51.03.png?fit=max&auto=format&n=gIiLYvn2bftMwXhD&q=85&s=80ae507a5ed762a52b90c613bbc63cd0" alt="Screenshot 2026 06 03 At 16 51 03" width="2888" height="596" data-path="images/Screenshot-2026-06-03-at-16.51.03.png" />
    </Frame>
  </Step>
</Steps>

***

# Part 2: Create the webhook

<Steps>
  <Step title="Add a new webhook">
    You can create a webhook from one of two places — both end up in the same editor.\\

    <Tabs>
      <Tab title="From webhook integration point">
        Continue the setup you started in Part 1 — on the webhook integration page, click **Create**.\\

        <Frame>
          <img src="https://mintcdn.com/maestraio/6qwnfOqUnljjhTXd/images/Unknown.png?fit=max&auto=format&n=6qwnfOqUnljjhTXd&q=85&s=1a098c1a39ebb9d9d021bb2d96c8be5f" alt="Unknown" width="2888" height="596" data-path="images/Unknown.png" />
        </Frame>
      </Tab>

      <Tab title="From webhooks list">
        Go to **Integrations → Webhooks → Create** and pick the integration this webhook belongs to.

        <Frame>
          <img src="https://mintcdn.com/maestraio/6qwnfOqUnljjhTXd/images/1111.png?fit=max&auto=format&n=6qwnfOqUnljjhTXd&q=85&s=749b21c44bc8ac97698e7d7d6cd48ca6" alt="1111" width="2600" height="1010" data-path="images/1111.png" />
        </Frame>

        <Frame>
          <img src="https://mintcdn.com/maestraio/6qwnfOqUnljjhTXd/images/Screenshot-2026-06-03-at-17.10.58.png?fit=max&auto=format&n=6qwnfOqUnljjhTXd&q=85&s=0a086d46eec3025918d92053f58e6dc2" alt="Screenshot 2026 06 03 At 17 10 58" width="2910" height="950" data-path="images/Screenshot-2026-06-03-at-17.10.58.png" />
        </Frame>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Fill in the basics">
    | Field                    | What it's for                                                                                                                             |
    | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
    | **Name**                 | Use a descriptive name so you'll know what it does at a glance.                                                                           |
    | **System name**          | A unique ID used to reference this webhook's response.                                                                                    |
    | **Description**          | Optional — fill in only if needed.                                                                                                        |
    | **Method**               | `GET`, `POST`, `PUT`, `PATCH`, or `DELETE` — whatever the receiving service expects.                                                      |
    | **Root URL**             | This URL is inherited from the webhook integration — it's shared by every webhook added to that integration.                              |
    | **URL**                  | The path that gets appended to the root URL from your integration. Leave blank if the webhook integration point already has the full URL. |
    | **Overview URL preview** | Shows the full assembled URL. Read-only — just for sanity-checking.                                                                       |

    <img src="https://mintcdn.com/maestraio/2_WUUqtXTQTrF_2i/images/imported/08_webhook.png?fit=max&auto=format&n=2_WUUqtXTQTrF_2i&q=85&s=30dde5859def394e1669eb84d47ce4e9" alt="Webhook basic settings" width="2578" height="904" data-path="images/imported/08_webhook.png" />
  </Step>

  <Step title="Add an idempotency key (recommended)">
    By default, a webhook is sent **once**. If the receiving service returns a `5xx` or `429` error, the request is gone.

    Adding an idempotency key tells Maestra Platform to safely retry — **up to 3 times over 6 minutes** — without the receiving service treating the retries as new, duplicate events.

    Use the template variable:

    ```text theme={null}
    ${WebhookRequest.TransactionalId}
    ```

    You can put it in the URL, a header, or the request body. Where it goes depends on what the receiving service expects.

    <Warning>
      Check your service's API docs before adding this. Some services expect a specific parameter name (`Idempotency-Key`, `request_id`, etc.) or a particular format. If the service doesn't recognize it, you won't get retry protection.
    </Warning>

    <AccordionGroup>
      <Accordion title="Example: adding it to the URL as a query parameter">
        Append it after a `?`:

        <img src="https://mintcdn.com/maestraio/hDwb_Ye73XB_-pfV/images/imported/09_webhook.png?fit=max&auto=format&n=hDwb_Ye73XB_-pfV&q=85&s=9902416ef9f1cac2560aa5ab1fdfec00" alt="Idempotency key as first query param" width="1798" height="804" data-path="images/imported/09_webhook.png" />

        If the URL already has query parameters, use `&` instead:

        <img src="https://mintcdn.com/maestraio/hDwb_Ye73XB_-pfV/images/imported/10_webhook.png?fit=max&auto=format&n=hDwb_Ye73XB_-pfV&q=85&s=25f5f9f1976e17a38106bef4e529201f" alt="Idempotency key as additional query param" width="1792" height="846" data-path="images/imported/10_webhook.png" />
      </Accordion>

      <Accordion title="Example: adding it as a header">
        Many APIs (Stripe-style) expect it as a header like `Idempotency-Key: ${WebhookRequest.TransactionalId}`.
      </Accordion>

      <Accordion title="Example: adding it to the request body">
        Drop it into your JSON payload as a field the receiving service expects, e.g. `"request_id": "${WebhookRequest.TransactionalId}"`.
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Set webhook-specific headers">
    Headers from the integration are inherited automatically. You can:

    * **Turn off** an inherited header by unchecking **Use**.
    * **Add** extra headers that only apply to this webhook.

    Both inherited and webhook-specific headers go out with the request.

    <img src="https://mintcdn.com/maestraio/EeW0Po78IZGyB5gF/images/imported/11_webhook.png?fit=max&auto=format&n=EeW0Po78IZGyB5gF&q=85&s=357792801d4a0b5290bb80ef4fd3ffa0" alt="Webhook headers" width="1802" height="856" data-path="images/imported/11_webhook.png" />

    In the example above, the final headers sent are:

    ```http theme={null}
    Content-Type: application/json
    webhookHeader: test
    ```
  </Step>

  <Step title="Build the request body">
    Drop in a JSON payload and pull in customer data using [dynamic parameters](/campaigns/message-template-engine/what-is-the-message-template-engine) from the message template engine.

    You can use:

    * **Variables** like `${customer.email}` or `${order.totalPrice}`
    * **Conditionals** with [if / else if / end if](/campaigns/message-template-engine/adding-a-condition-ifelse-ifend-if)
    * **Loops** with [for / end for](/campaigns/message-template-engine/how-to-use-elements-of-a-collection-with-the-forend-for-loop-and-the-set-keyword) — handy for line items in an order

    The right-hand sidebar has the full variable picker.

    <img src="https://mintcdn.com/maestraio/EeW0Po78IZGyB5gF/images/imported/12_webhook.png?fit=max&auto=format&n=EeW0Po78IZGyB5gF&q=85&s=c03901571681698a036a6fc19b91a2d9" alt="Request body editor" width="2598" height="1498" data-path="images/imported/12_webhook.png" />
  </Step>

  <Step title="Capture the response (optional)">
    If the receiving service sends back data you want to use later in your flow — an order number, a status, a tracking link — capture it here.

    Once captured, you can:

    * **Branch the flow** based on whether the call succeeded
    * **Personalize the next message** with values from the response

          <img src="https://mintcdn.com/maestraio/EeW0Po78IZGyB5gF/images/imported/13_webhook.png?fit=max&auto=format&n=EeW0Po78IZGyB5gF&q=85&s=4924737c8046c686d3771fc3c6d08b18" alt="Webhook response capture" width="1804" height="960" data-path="images/imported/13_webhook.png" />

    For the full walkthrough, see [How to use webhook responses in flows](/campaigns/webhooks/how-to-use-webhook-responses-in-flows).
  </Step>

  <Step title="Save">
    <img src="https://mintcdn.com/maestraio/EeW0Po78IZGyB5gF/images/imported/14_webhook.png?fit=max&auto=format&n=EeW0Po78IZGyB5gF&q=85&s=51f81d44c3d9aec71a853b4e096d963c" alt="Save webhook" width="2574" height="984" data-path="images/imported/14_webhook.png" />
  </Step>
</Steps>

***

## Cloning a webhook

If you need a near-duplicate (same auth, different endpoint or payload), open the webhook's menu and click **Copy** — faster than rebuilding from scratch.

<img src="https://mintcdn.com/maestraio/m5TAkUWmgaRT4x96/images/imported/15_webhook.png?fit=max&auto=format&n=m5TAkUWmgaRT4x96&q=85&s=60e15e906f5bb4a969a378583557cb4d" alt="Copy webhook" width="2596" height="934" data-path="images/imported/15_webhook.png" />

## Troubleshooting and monitoring

Once your webhook is live, the [Webhook Logs](/campaigns/webhooks/webhook-logs) view is your source of truth for what was actually sent, what came back, and where things went wrong.

<CardGroup cols={2}>
  <Card title="Use webhook responses in flows" icon="diagram-project" href="/campaigns/webhooks/how-to-use-webhook-responses-in-flows">
    Branch flows or personalize messages with data the receiving service sends back.
  </Card>

  <Card title="Webhook logs" icon="list-check" href="/campaigns/webhooks/webhook-logs">
    Inspect every request and response for debugging and auditing.
  </Card>

  <Card title="Webhook-powered campaign content" icon="envelope-open-text" href="/campaigns/webhooks/adding-webhook-powered-content-to-campaigns">
    Pull live data from an external service into your campaign content.
  </Card>

  <Card title="Message template engine" icon="code" href="/campaigns/message-template-engine/what-is-the-message-template-engine">
    Reference for variables, conditionals, and loops in your request body.
  </Card>
</CardGroup>
