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

# Incoming Webhooks

> Start a FlowX process from an external HTTP POST, or resume a running process instance and inject the POST payload into it. Secured with API keys, event-driven, no polling.

## What is an incoming webhook?

A webhook is an HTTP endpoint that an external system calls to notify FlowX when an event happens. Instead of FlowX polling for updates, the third-party system sends an HTTP POST the moment something occurs — and FlowX starts a process in response. Common uses include receiving events from payment processors, CRMs, ERPs, or CI/CD pipelines.

## Overview

Incoming Webhooks allow external systems to trigger FlowX processes by sending HTTP POST requests to a generated URL. Each webhook gets a unique, cryptographically secure API key for authentication — no IMAP setup, no polling, just a direct HTTP call.

Use webhooks when you need to:

* **Receive events from third-party platforms** (payment processors, CRMs, ERPs) that support outbound webhooks
* **Trigger processes from CI/CD pipelines** or internal tools via simple HTTP calls
* **Replace polling-based integrations** with event-driven process starts

> [card] API key authentication
>
> Each webhook gets a unique, secure API key included in the URL

> [card] JSON payload forwarding
>
> Webhook body and HTTP headers are forwarded to the process instance as variables

> [card] Manage Triggers UI
>
> Activate, deactivate, and manage webhook URLs from Runtime Settings

> [card] Zero configuration
>
> No server settings needed — create the data source, connect to a process, activate

***

## How it works

A webhook does one of two things, depending on the **Webhook Behavior** you choose when you create the data source.

> [tab] Start a process
>
> Each POST creates a **new** process instance.
>
> **1. Create an Incoming Webhook data source**
>
> In Integration Designer, create a new data source and select **Incoming Webhook**. Provide a name and optional description, and leave **Webhook Behavior** on **Start a Process/Workflow**. No connection settings are required.
>
> **2. Connect to a Message Start Event**
>
> In your process definition, add a **Message Start Event** node. Set the **Trigger Type** to **Incoming Webhook** and select your webhook data source.
>
> **3. Commit and activate**
>
> Commit the version, then go to **Runtime** → **Manage Triggers**. The webhook appears as **Deactivated**. Click the action menu and select **Activate**.
>
> **4. Get the webhook URL**
>
> From the trigger's action menu, select **Get URL**. Copy the generated URL (includes the API key as a query parameter) and configure it in your external system.
>
> **5. External system sends POST request**
>
> When the external system POSTs JSON to the webhook URL, the `webhook-gateway` service validates the API key, publishes the event to Kafka, and the process engine starts a new process instance.

> [tab] Resume a waiting process
>
> Each POST resumes an **already-running** instance and hands it the payload. Use this to push data into a process in flight — for example, sending updated credit data into a loan application that is parked waiting on an external system.
>
> **1. Create an Incoming Webhook data source**
>
> In Integration Designer, create a new data source and select **Incoming Webhook**. Set **Webhook Behavior** to **Resume a Waiting Process**, then set **Webhook Correlation Key** to the path in the incoming payload that identifies the instance to resume (for example, `application.applicationId`).
>
> **2. Connect to a Message Catch Intermediate Event**
>
> In your process definition, add a **Webhook Trigger** message catch event where the process should pause. Select the webhook as its **Data Source**, then set **Process Correlation Key** to the matching process variable and **Process Key** to where the incoming data should land.
>
> **3. Commit and activate**
>
> Commit the version, then activate the webhook in **Runtime** → **Manage Triggers**, exactly as for a start webhook.
>
> **4. Get the webhook URL**
>
> From the trigger's action menu, select **Get URL** and configure that URL in your external system.
>
> **5. External system sends POST request**
>
> When the external system POSTs JSON, `webhook-gateway` validates the API key, reads the correlation value at the **Webhook Correlation Key** path, and publishes a message event. The process engine matches it against instances waiting on that catch event and resumes the one whose **Process Correlation Key** holds the same value, storing the incoming data at the **Process Key**.
>
> > [!NOTE]
> >
> > If no waiting instance matches the correlation value, or the correlation path does not resolve in the payload, the request is rejected and nothing is resumed. See [Error handling](#error-handling).

***

## Creating a webhook data source

**1. Navigate to Data Sources**

Go to **Integrations** → **Data Sources** in your project.

**2. Add a new data source**

Click **+** and select **Incoming Webhook** as the data source type.

**3. Configure basic information**

| Field | Required | Description |
| - | - | - |
| **Name** | Yes | A unique name for the webhook (3–50 characters, letters, numbers, `[] () . _ -`) |
| **Description** | No | Purpose or context for this webhook |
| **Webhook Behavior** | Yes | Whether each POST starts a new process or resumes a waiting one. See [Webhook Behavior](#webhook-behavior). |
| **Webhook Correlation Key** | Only for **Resume a Waiting Process** | Dotted path into the incoming payload whose value identifies which waiting process instance to resume. See [Webhook Correlation Key path syntax](#webhook-correlation-key-path-syntax). |

**4. Create**

Click **Create**. The webhook data source is ready to be connected to a process.

![Incoming Webhook data source page](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.6/webhook_data_source_created.png)

> [!NOTE]
>
> Incoming Webhooks accept POST requests with JSON payloads (max 1 MB). No connection settings, credentials, or polling configuration is needed.

> [!WARNING]
>
> When deleting a webhook data source, deactivate it first and remove the URL from any third-party systems that use it. Deleting the webhook in FlowX does not notify external systems — they will continue sending requests that return errors.

***

## Connecting to a Message Catch Event

A webhook can drive two kinds of message catch events:

| Event placement | Webhook Behavior (API value) | What the webhook does |
| - | - | - |
| **Message Start Event** | `START_PROCESS` | Creates a new process instance on each POST |
| **Message Catch Intermediate Event** | `RESUME_PROCESS` | Resumes a process instance waiting on the catch |

You choose which of the two a webhook does when you create the data source, using the **Webhook Behavior** setting.

### Webhook Behavior

On the webhook data source's **Settings** tab, **Webhook Behavior** offers two options:

| Option | What it does | Attach the webhook to |
| - | - | - |
| **Start a Process/Workflow** | Creates a new process instance on each POST, with the webhook payload as its input. It can start an integration workflow instead; see [Activating a webhook](#activating-a-webhook). | A **Message Start Event** node |
| **Resume a Waiting Process** | Resumes a process instance already waiting on a catch event, using a correlation key. | A **Message Catch Intermediate Event** node |

![Webhook data source Settings tab with Resume a Waiting Process selected and a correlation key set](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/api_key_only.png)

Selecting **Resume a Waiting Process** enables **Webhook Correlation Key** in the **Process Connection** panel and marks it required. It is the dotted path into the incoming payload whose value identifies the instance to resume, and it is defined once on the data source rather than per node. Under **Start a Process/Workflow** the field is greyed out, and saving a value there is rejected.

> [!WARNING]
>
> **Webhook Behavior is immutable after the first save.** Once the webhook data source has been saved, the setting is locked in the Designer and the API rejects a change to it. To switch a webhook between starting and resuming, create a new webhook data source.

### On a Message Start Event

**1. Open your process definition**

Open the process in the Process Designer.

**2. Add a Message Start Event node**

Drag a **Message Start Event** from the Start Events section of the node palette onto the canvas.

**3. Configure the Process Trigger**

In the node configuration panel, under **Process Trigger**:

* Set **Trigger Type** to **Incoming Webhook**
* In **Select Webhook**, choose your webhook data source from the dropdown

**4. Save the node**

Click **Save** to persist the configuration.

![Message Start Event configured with Incoming Webhook trigger](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.6/webhook_trigger_configured.png)

### On a Message Catch Intermediate Event

Use a webhook on an intermediate catch event to resume a process instance that is waiting for an external signal — for example, a callback from a third-party document scanner, a downstream system confirming an enqueued job, or a manual operator action delivered over HTTP.

**1. Add a Webhook Trigger intermediate catch event**

From the node palette, drag a **Webhook Trigger** message catch event onto the canvas at the point where the process should pause.

**2. Select the webhook data source**

In the node configuration panel, set the **Data Source** dropdown to the webhook that drives this catch event. The **Webhook Correlation Key** field appears as read-only and shows the correlation key defined on the webhook itself — included here so you can see how incoming requests are matched.

**3. Set the Process Correlation Key**

Set **Process Correlation Key** to a business or process variable from the data model (for example, `applicationId`, `orderId`). At runtime this value is matched against the incoming payload to identify which waiting process instance to resume.

**4. Set the Process Key**

Set **Process Key** to the data-model path where the incoming webhook data should be stored when the catch fires. The value stored there is the same `{ dateTime, payload, headers }` envelope described in [Webhook payload](#webhook-payload), not the bare POST body — read the request body from `payload` underneath it.

**5. Save the node**

Click **Save** to persist the configuration.

> [!NOTE]
>
> The same webhook data source can drive multiple intermediate catch events as long as the Process Correlation Key disambiguates which instance to resume. Without a valid correlation match, the inbound request is rejected.

#### Webhook Correlation Key path syntax

The **Webhook Correlation Key** points into the incoming webhook payload. It uses dotted-path notation and supports array indices in square brackets, so deeply nested provider payloads can be addressed directly.

| Path pattern | Resolves to |
| - | - |
| `application.applicationId` | `payload.application.applicationId` |
| `entry[0].changes[0].value.messages[0].text.body` | The first inbound text-message body in a Meta WhatsApp webhook payload |
| `data.orders[2].id` | The `id` of the third element of the `orders` array |

> [!NOTE]
>
> Array index syntax (`field[N]`) is supported in correlation keys, so the canonical Meta WhatsApp resume path works as-is. Paths like `entry[0]...` resolve directly and no longer need to be flattened in the producer before posting.

If the path does not resolve in an incoming request (missing field, index out of bounds, null leaf), the request is rejected and the waiting process instance is not resumed.

***

## Managing webhooks at runtime

### Activating a webhook

For a webhook to appear in Manage Triggers:

1. The webhook data source must be created
2. The webhook must be connected to a node that can carry a webhook trigger — a **Message Start Event** or a **Message Catch Intermediate Event**. A **Start a Process/Workflow** webhook can also be bound to the Start node of an integration workflow; see [External triggers](https://docs.flowx.ai/6.x/docs/platform-deep-dive/integrations/integration-designer.md#external-triggers)&#x20;
3. The version must be committed

Navigate to **Runtime** → **Manage Triggers** to view and control webhooks:

| Column | Description |
| - | - |
| **State** | `Active` or `Deactivated` |
| **Trigger Type** | `Webhook` |
| **Event Name** | The webhook data source name |
| **Location** | `Local`, or the library project the webhook comes from |

Use the action menu (three-dot icon) to:

* **Activate** — Start accepting webhook requests
* **Deactivate** — Stop accepting requests. The API key and URL are preserved, so you can reactivate later without reconfiguring external systems.
* **Get URL** — View and copy the webhook URL with API key

> [!TIP]
>
> A single webhook can trigger multiple processes — add the same webhook data source to Message Start Event nodes in different process definitions. Each incoming request starts all connected processes. Integration workflows bound to the same webhook are started too.

![Manage Triggers showing active webhook](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.6/webhook_trigger_active.png)

***

### Webhook URL and API key

When you activate a webhook and select **Get URL**, a modal displays:

* **Webhook URL** — The full URL including the API key as a `flowxApiKey` query parameter
* **Copy button** — Copies the URL to clipboard
* **Reset API Key** — Generates a new API key (invalidates the previous URL)

![Webhook URL modal with API key and Reset button](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.6/webhook_url_modal.png)

**URL format:**

```
https://{services-host}/webhooks/incoming/{orgId}/{workspaceId}/{appId}/{resourceDefinitionId}?flowxApiKey={apiKey}
```

A webhook with the **Slack** or **Meta** provider adds the provider as a lowercase path segment (`slack` or `meta`) after `/webhooks/incoming/`:

```
https://{services-host}/webhooks/incoming/{provider}/{orgId}/{workspaceId}/{appId}/{resourceDefinitionId}?flowxApiKey={apiKey}
```

> [!WARNING]
>
> Resetting the API key creates a new webhook URL. Update the URL in all third-party systems that use it, or they will receive `401 Unauthorized` errors.

***

## Provider adapters

When a webhook is created in the Integration Designer, the **Settings** tab now exposes:

* **Provider** — the dropdown offers three options. Like Webhook Behavior, the provider is a one-time selection: once saved it becomes immutable, and you create a new webhook to switch.

  | Dropdown label | API value | Signature validation |
  | - | - | - |
  | **Generic (API Key Only)** | `GENERIC` | None |
  | **Slack** | `SLACK` | `X-Slack-Signature` HMAC-SHA256 |
  | **Meta (WhatsApp, Messenger, Instagram, Threads)** | `META` | `X-Hub-Signature-256` HMAC-SHA256 |

* **Provider secret** — the signing secret(s) obtained from the provider. Stored encrypted; can be rotated independently of the API key. The field is labelled per provider and only appears once a provider that needs it is selected: **Signing Secret** for Slack, **Verify token** plus **App secret** for Meta. `Generic (API Key Only)` has no secret field.

![Provider dropdown on the webhook Settings tab, listing Generic, Slack and Meta](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/webhook_providers.png)

For SaaS-hosted secrets you can also reference a config parameter using the `${configParam.name}` placeholder syntax — placeholders are resolved at registration time the same way LLM provider secrets are.

### Generic provider

The default. No signature validation is performed; authentication relies on the FlowX `flowxApiKey` and HTTPS. Use `GENERIC` for systems that do not sign their webhooks, or where signature validation is handled inside the process via a business-rule action on the raw headers.

Slack and Meta are the only channels with dedicated adapters. Any other platform that can deliver an HTTPS POST — Microsoft Teams, Viber, Telegram, or an internal system — connects through the Generic provider; if that platform signs its requests, validate the signature inside the process from the raw headers.

### Slack provider

Performs Slack's standard signature check on every incoming request:

| Header | Purpose |
| - | - |
| `X-Slack-Signature` | HMAC-SHA256 signature of the version, timestamp, and raw body, prefixed with `v0=`. |
| `X-Slack-Request-Timestamp` | Unix timestamp; rejected if more than **5 minutes** out of skew. |

Requests that fail signature verification, that miss either header, or that arrive without a configured signing secret are rejected with `401 Unauthorized`.

Slack's URL-verification handshake is handled automatically — a `url_verification` POST with a `challenge` field receives the `challenge` value back as JSON, before the request is forwarded to the process. No process configuration is required for the handshake.

Selecting **Slack** adds a required **Signing Secret** field to the Settings tab. Find the value in your Slack app under **Basic Information** → **App Credentials** → **Signing Secret**. You can paste it directly or reference a config parameter.

![Webhook Settings tab with the Slack provider selected, showing the Signing Secret field](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/slack_provider.png)

### Meta provider

Covers WhatsApp, Messenger, Instagram, and Threads. Meta-platform integrations share a common subscription model and signature scheme; the `META` adapter handles both.

Meta webhooks require **two** secrets, configured as masked inputs in the **Settings** panel:

| Field | Purpose | Where to find it |
| - | - | - |
| **Verify token** | Used during the GET URL-verification handshake. | Any random string you choose. Paste the same value in **Meta** → **Webhooks** → **Verify token**. |
| **App secret** | Verifies the `X-Hub-Signature-256` HMAC on every POST. | **Meta Developer Dashboard** → your App → **Settings** → **Basic** → **App Secret**. |

Both values are stored together as a JSON-packed pair in the existing webhook secret field, encrypted at rest. Both are required; saving without either returns `WEBHOOK_META_SECRETS_REQUIRED 400`.

![Webhook Settings tab with the Meta provider selected, showing the Verify token and App secret fields](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.9/meta_provider.png)

**GET URL verification.** Meta sends a GET request with `hub.mode=subscribe`, `hub.verify_token`, and `hub.challenge` query parameters when you save the webhook in the Meta App configuration. The gateway:

1. Confirms `hub.mode=subscribe`.
2. Confirms `hub.challenge` is present.
3. Compares the received `hub.verify_token` to the configured **Verify token** using a constant-time check.
4. Echoes the `hub.challenge` value back as plain text on success.

A mismatch or missing parameter returns `403 Forbidden` — Meta does not finalise the subscription.

**POST signature verification.** Every POST is signed by Meta with an `X-Hub-Signature-256` header (`sha256=<hex>`). The gateway recomputes the HMAC-SHA256 of the raw body with the configured **App secret** and rejects mismatches with `401 Unauthorized`.

### Path / provider mismatch

If a request hits the URL for one provider but the webhook registration is configured with a different provider, the gateway returns `400 Bad Request`. Always use the URL shown in the **Get URL** modal — it carries the correct provider segment for the registration.

***

## Webhook payload

When an external system POSTs to the webhook URL, the following data is available in the process instance:

```json theme={"dark"}
{
  "webhookMessage": {
    "dateTime": "2026-03-23T15:30:00Z",
    "payload": {
      "orderId": "ORD-12345",
      "status": "completed",
      "amount": 299.99
    },
    "headers": {
      "Content-Type": ["application/json"],
      "X-Custom-Header": ["value"]
    }
  }
}
```

| Field | Type | Description |
| - | - | - |
| `dateTime` | string (ISO 8601) | Ingestion time at FlowX — when `webhook-gateway` received the request. If the provider includes its own event timestamp in the payload, use that for the original event time. |
| `payload` | object or string | The POST request body (parsed as JSON if valid, raw string otherwise) |
| `headers` | object | All HTTP headers from the incoming request. Each header maps to an array of values — access the first value with `webhookMessage.headers.Content-Type[0]` in process variables |

![webhookMessage paramValue on a process instance](https://s3.eu-west-1.amazonaws.com/docx.flowx.ai/5.6/webhookExample.png)

> [!NOTE]
>
> `headers` includes values added by the FlowX ingress in addition to what the caller sent — typically `X-Forwarded-For`, `X-Real-IP`, `X-Request-ID`, `X-Forwarded-Host`, `X-Forwarded-Proto`. These are useful for logging and tracing but should not be confused with provider-supplied headers.

> [!NOTE]
>
> The maximum payload size is **1 MB**. Requests exceeding this limit are rejected.

***

## Authentication

Webhook authentication uses API keys:

* Each webhook registration gets a unique **32-byte, base64-URL-encoded** API key
* The key is passed as the `flowxApiKey` query parameter in the webhook URL
* Validation uses **constant-time comparison** to prevent timing attacks
* Invalid or missing keys return `401 Unauthorized`
* Inactive webhooks return `404 Not Found`

> [!WARNING]
>
> FlowX validates provider signatures only for [providers with a built-in adapter](#provider-adapters). For the `GENERIC` provider — and for any third-party signature scheme without a dedicated adapter (`Stripe-Signature`, GitHub's `X-Hub-Signature-256` outside the Meta family, etc.) — signature headers are forwarded to the process in `webhookMessage.headers` but no validation is performed. Authentication for `GENERIC` registrations relies on the FlowX API key and HTTPS. The raw request body is not preserved after JSON parsing, which makes in-process HMAC verification impractical — for unsupported providers, design integrations assuming API key + HTTPS is the only authentication layer, or request a built-in adapter.

***

## Error handling

| HTTP Status | Cause | Resolution |
| - | - | - |
| `200 OK` | Webhook accepted and published to Kafka | — |
| `400 Bad Request` | The URL's provider segment doesn't match the webhook's **Provider** | Use the URL from the **Get URL** modal. A body that isn't valid JSON is accepted and reaches the process as a raw string in `payload` |
| `401 Unauthorized` | Invalid or missing API key | Check the `flowxApiKey` parameter matches the active webhook URL |
| `404 Not Found` | Webhook registration not found or deactivated | Verify the webhook is activated in Manage Triggers |
| `413 Payload Too Large` | Request body exceeds 1 MB | Reduce the payload size or split into multiple requests |
| `500 Internal Server Error` | Processing error | Check webhook-gateway service logs |

Failed webhook events appear in **Runtime** → **Failed Triggers** with details about the failure cause.

***

## Best practices

> [card] Use HTTPS
>
> Always use HTTPS URLs in production to protect the API key in transit

> [card] Validate payloads in your process
>
> Add validation logic in your process to handle unexpected or malformed payloads gracefully

> [card] Monitor Failed Triggers
>
> Regularly check the Failed Triggers section for webhook processing errors

> [card] Rotate keys periodically
>
> Use the Reset API Key feature to rotate webhook credentials, then update the URL in external systems

***

## Related resources

> [card] [Message Start Event](https://docs.flowx.ai/6.x/docs/building-blocks/node/message-events/message-catch-start-event.md)
>
> Configure message-based and webhook process triggers

> [card] [Message Catch Intermediate Event](https://docs.flowx.ai/6.x/docs/building-blocks/node/message-events/message-intermediate/message-catch-intermediate-event.md)
>
> Pause a process until an internal message or an external webhook resumes it

> [card] [Email Trigger](https://docs.flowx.ai/6.x/docs/platform-deep-dive/integrations/email-trigger.md)
>
> Start processes from incoming emails via IMAP

> [card] [Integration Designer](https://docs.flowx.ai/6.x/docs/platform-deep-dive/integrations/integration-designer.md)
>
> Build integration workflows and manage data sources

> [card] [Webhook Gateway setup](https://docs.flowx.ai/6.x/setup-guides/webhook-gateway-setup.md)
>
> Deploy and configure the webhook-gateway microservice


## Related topics

- [Message Start Event](https://docs.flowx.ai/6.x/docs/building-blocks/node/message-events/message-catch-start-event.md)
- [FlowX.AI 5.6.0 Release Notes](https://docs.flowx.ai/release-notes/v5.x/v5.6.0-march-2026/v5.6.0-march-2026.md)
- [FlowX.AI 5.9.0 Release Notes](https://docs.flowx.ai/release-notes/v5.x/v5.9.0-june-2026/v5.9.0-june-2026.md)
- [Webhook Gateway setup](https://docs.flowx.ai/6.x/setup-guides/webhook-gateway-setup.md)
- [Message catch intermediate event](https://docs.flowx.ai/6.x/docs/building-blocks/node/message-events/message-intermediate/message-catch-intermediate-event.md)


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