Incoming Webhook Step

Trigger a new workflow execution when an external system sends an HTTP request to a generated endpoint.

The Incoming Webhook step is a trigger — it must be the first step in a workflow and it starts a new execution each time an external system sends an HTTP request to the generated endpoint. Use it to integrate with third-party systems that push data rather than respond to API calls.

For mapping placeholder syntax, see Mapping.

When you create a project whose workflow starts with an Incoming Webhook step, the project response includes a webhookSessionStartUrl in the __meta object. That is the URL your external system calls to start a session. The URL uses the step's stable publicId, so it does not change when you update the workflow.

Authentication

The step's authenticationType controls how Streamline validates incoming requests.

authenticationTypebehaviour
NONEThe endpoint is public. Any caller can trigger a session without credentials.
PATCallers must include a valid bearer token in the Authorization header. Streamline validates the token against the organization's authentication provider.
HMACCallers must sign the raw request body with a shared secret and include the signature in the Streamline-Signature header. Requires an integrationId referencing a configured integration that holds the HMAC secret.

When authenticationType is HMAC, you must also provide integrationId (the integration that stores the shared secret). For NONE and PAT, integrationId is optional.

HMAC signature

For HMAC-authenticated webhooks, every request must include a Streamline-Signature header containing the HMAC-SHA256 digest of the raw request body:

Streamline-Signature: sha256={hex}
DetailValue
AlgorithmHMAC-SHA256
HeaderStreamline-Signature: sha256={hex}
InputExact raw request body bytes (UTF-8)
SecretThe shared secret from the integration referenced by integrationId

Streamline recomputes the digest from the raw bytes it receives and compares it to the value in the header. If the digests do not match, the request is rejected with 401. If the header is missing or malformed (e.g. missing the sha256= prefix), the request is rejected with 400.

HMAC-authenticated webhooks are commonly used with embedded workflows, where your server signs the payload and starts the session before passing the resumeUrl to a client-side iframe. For a complete walkthrough — including code examples in Node.js, Python, Java, and cURL — see .

Configuration

nametyperequireddescription
authenticationTypestringyesHow Streamline authenticates incoming requests. One of NONE, PAT, or HMAC.
integrationIdstringconditionalIntegration holding the authentication secret. Required when authenticationType is HMAC; optional otherwise.
methodstringyesHTTP method to accept: GET or POST.
listeningModeEnabledbooleannoSet to true to put the step in listening mode so it captures a sample request and extracts fields. Streamline resets this to false after extraction completes. Default: false.
requestPayloadstringnoJSON string snapshot of the captured test request (body, query parameters, and headers). Streamline writes this when a test request succeeds. Default: empty string.
mappableFieldsarraynoFields extracted from the captured payload, available for mapping in later steps. See Mappable fields. Default: empty array.
groupNodesarraynoEntity group descriptors for repeating collections within the payload. See Group nodes.

Mappable fields

Each entry in mappableFields describes a field from the incoming request that later steps can map. Field keys are prefixed with body., query., or headers. depending on where they appear in the request.

nametyperequireddescription
keystringyesThe field path in the captured request (e.g. body.orderId, query.status, headers.x-request-id).
labelstringyesHuman-readable label for the field.
typestringyesData type: string, number, boolean, date, datetime, time, object, or array.
exampleValuestring, number, boolean, or nullnoSample value stored with the field and shown where Streamline displays sample data.
fieldCategorystringnoSet by Streamline during extraction. REGULAR — a top-level scalar value (e.g. body.status). FLATTENED — a value from a nested object, flattened to dot-notation (e.g. body.address.city). WILDCARD — a value inside a repeating array, referenced with a wildcard path (e.g. body.items.*.name).

Populating mappable fields

mappableFields is a regular configuration property — you can populate it automatically from a test request, or write the array directly when you create or update the project.

Option A: Extract from a test request

This is the fastest path when you have a representative payload. The flow is:

  1. Enable listening mode — update the step configuration in the project and set listeningModeEnabled to true.
  2. Send a test request — append ?test=true to the step's webhookSessionStartUrl (from the project response's __meta object) and send a POST or GET:

POST {webhookSessionStartUrl}?test=true

The request body, query parameters, and headers you send become the sample payload. Authentication follows the step's authenticationType — for NONE, no credentials are needed; for PAT, include a bearer token; for HMAC, include the Streamline-Signature: sha256={hex} header (see HMAC signature).

Streamline validates that the step is in listening mode, stores the payload as requestPayload on the step configuration (sensitive headers such as Authorization and Cookie are stripped before storage), and starts field extraction in the background. The endpoint returns immediately with:

{
  "success": true,
  "message": "Test webhook received successfully. Field extraction is being processed in background."
}

If the step is not in listening mode, the request is rejected with a 400 error. If an extraction is already in progress, the request is rejected until the current extraction completes.

  1. Retrieve the extracted fields — once extraction finishes, Streamline writes the results to mappableFields on the step configuration and resets listeningModeEnabled to false. Fetch the project via GET /v1/projects/{projectId} to read the populated mappableFields and requestPayload from the step's config.

The extracted fields can then be referenced in later steps using the step's description as the mapping source (e.g. {{Incoming webhook.body.orderId}}).

If the results are not what you expected, enable listening mode again, send a different payload, and Streamline re-extracts.

Option B: Set mappable fields directly

If you already know the payload structure, you can skip listening mode entirely and include mappableFields in the step configuration when you create or update the project:

PATCH /v1/projects/{projectId}

{
  "workflow": {
    "steps": [
      {
        "type": "webhook",
        "description": "Incoming webhook",
        "config": {
          "authenticationType": "NONE",
          "method": "POST",
          "mappableFields": [
            { "key": "body.orderId", "type": "string", "label": "Order ID" },
            { "key": "body.amount", "type": "number", "label": "Amount" },
            { "key": "body.status", "type": "string", "label": "Status" }
          ]
        },
        "edges": []
      }
    ]
  }
}

Each key must match the path that will appear in the incoming request at runtime (prefixed with body., query., or headers.).

Customizing after extraction

Whether fields were extracted automatically or written directly, you can modify them at any time by updating the project. Add fields, remove fields, rename labels, or change types — then include the updated mappableFields array in the step's config. Later steps that reference these fields by key use whatever is currently stored on the step.

Group nodes

When the captured payload contains arrays, Streamline creates groupNodes entries that describe the repeating structure. Each group node identifies an array path so that later steps can iterate over collection items.

nametyperequireddescription
idstringyesUnique identifier for the group node.
labelstringyesHuman-readable name for the collection.
pathstringyesDot-notation path to the array in the payload (e.g. body.orders).
wildcardPathstringyesPath with * replacing array indices (e.g. body.orders.*).
fieldsarraynoScalar fields within each item of the collection. Each entry has path (string) and type (string). Default: empty array.
itemCountnumbernoNumber of items found in the sample payload for this collection.
isRepeatingbooleannoWhether this collection repeats (always true for detected arrays). Default: false.
parentPathstring or nullnoPath of the parent group node for nested arrays. null for top-level collections.
displayRootbooleannoWhether this node is the top-level entry point when the payload contains nested arrays.

Examples

Let's start with an unconfigured webhook trigger — no fields extracted yet.

{
  "authenticationType": "NONE",
  "method": "POST",
  "listeningModeEnabled": false,
  "requestPayload": "",
  "mappableFields": []
}

Here's what the step configuration looks like after sending a sample payload and extracting fields. Notice that listeningModeEnabled is back to false and the fields use body. prefixed keys.

{
  "authenticationType": "NONE",
  "method": "POST",
  "listeningModeEnabled": false,
  "requestPayload": "{\"body\":{\"orderId\":\"ord-789\",\"customerEmail\":\"[email protected]\",\"amount\":149.99,\"status\":\"placed\"},\"query\":{},\"headers\":{}}",
  "mappableFields": [
    { "key": "body.orderId", "type": "string", "label": "Order ID", "exampleValue": "ord-789" },
    { "key": "body.customerEmail", "type": "string", "label": "Customer Email", "exampleValue": "[email protected]" },
    { "key": "body.amount", "type": "number", "label": "Amount", "exampleValue": 149.99 },
    { "key": "body.status", "type": "string", "label": "Status", "exampleValue": "placed" }
  ]
}

Here is a PAT-authenticated webhook that accepts GET requests.

{
  "authenticationType": "PAT",
  "method": "GET",
  "listeningModeEnabled": false,
  "requestPayload": "",
  "mappableFields": []
}