Outbound Webhook Step
Send HTTP requests to external endpoints during a workflow, optionally capture the response for later steps, or redirect the participant to an external URL.
The Outbound Webhook step sends an HTTP request to an external URL while a workflow runs. Use it to notify another system, create or update a record in a third-party API, or upload a file. You can run the request in the background, or wait for a response and pass response data to steps that follow.
You can also turn on Redirect users to send the participant to an external URL in their browser (for example a payment page, portal, or another workflow). When Redirect users is on, the workflow ends after this step.
For mapping placeholder syntax, see Mapping. For Repeating Records in JSON request bodies, see Repeating Records in JSON bodies.
Waiting for a response
Set waitForResponse to true if the workflow should pause until the HTTP call finishes. When the external service returns a body (and a content type is present), Streamline stores that response so later steps can map values from it. response.statusCode is always available as a numeric field when the response is captured.
For JSON responses, the body is flattened into dot-notation paths that start with body. (for example, body.id or body.user.email). Non-JSON bodies, or JSON that fails to parse, are exposed as a single string field named body.
If waitForResponse is false, the workflow does not capture the HTTP response for mapping into later steps.
Continue on error
When continueOnError is true, HTTP error status codes (4xx/5xx) or network failures do not stop the workflow. The step can finish with a completed-with-errors state instead of failing the run. Default is false (failures stop the step).
When Redirect users is on, continueOnError must be false.
Redirect
Turn on Redirect users to send the participant to the Endpoint URL in their browser. After this step, the workflow is complete — later steps do not run.
Requirements:
- Method must be
GET continueOnErrormust befalse- Endpoint URL must be an
http://orhttps://URL (static, or mapped from an earlier step)
To send someone to another workflow: first use a normal outbound webhook (waitForResponse: true) to get a resume URL, then use a second outbound webhook with Redirect users on and that URL mapped into the Endpoint URL.
Configuration
| name | type | required | description |
|---|---|---|---|
request | object | yes | The HTTP request to send. See Request below. |
waitForResponse | boolean | no | When true, the workflow waits for the HTTP response before continuing. Required if later steps should map response values. Default: false. |
continueOnError | boolean | no | When true, the workflow continues when the request fails or returns an error status. Default: false. Must be false when Redirect users is on. |
listeningModeEnabled | boolean | no | When true, the next test request captures the response for field extraction. Streamline resets this to false after extraction completes. Default: false. |
requestPayload | string | no | String snapshot of the outbound request. Streamline sets this when a test succeeds; you can also set it when updating the project. Field extraction reads this together with responsePayload. |
responsePayload | string | no | String snapshot of a sample response body. Streamline sets this when a test succeeds; it feeds field extraction. Default: empty string. |
mappableFields | array | no | Field descriptors for mapping into later steps. See Mappable fields. Default: empty array. |
redirect | object | no | Set when Redirect users is on. See Redirect. |
Request
The request object defines the HTTP call.
| name | type | required | description |
|---|---|---|---|
method | string | yes | HTTP method: GET, POST, PUT, or DELETE. Must be GET when Redirect users is on. |
url | string | yes | Endpoint URL. Path placeholders use the :name form (for example :userId); supply values in pathParams. You can map a full URL into this field. For redirect, the Endpoint URL must be http:// or https://. |
authentication | object | yes | Authentication settings. See Authentication below. |
pathParams | array | no | Path parameter values. Each item has key and value (values support mapping). |
queryParams | array | no | Query string parameters. Each item has key and value (values support mapping). |
headers | array | no | Custom HTTP headers. Each item has key and value (values support mapping). Header names are normalized to lowercase. |
body | object | varies | Request body. See Body modes. Required for POST and PUT. Optional for DELETE. Not used for GET. |
Authentication
| name | type | required | description |
|---|---|---|---|
type | string | yes | NONE (no authentication) or AUTHENTICATED (use a stored connection). |
connectionId | string | yes | ID of the stored connection when type is AUTHENTICATED. Use null when type is NONE. |
Body modes
The shape of body is determined by requestMode.
RAW
Send JSON or plain text. String values support mapping placeholders.
| name | type | required | description |
|---|---|---|---|
requestMode | string | yes | Must be RAW. |
contentType | string | yes | JSON or TEXT. |
content | string or object | no | Body content. For JSON, either a string or an object whose string values support mapping, including syntax for Repeating Records. |
MULTIPART
Send multipart/form-data. Each part is either text (string value) or a file resolved from a file reference or mapping placeholder.
| name | type | required | description |
|---|---|---|---|
requestMode | string | yes | Must be MULTIPART. |
content | array | yes | Form fields: each item has key and value (values support mapping). |
RAW_BINARY
Send a single file as the entire request body.
| name | type | required | description |
|---|---|---|---|
requestMode | string | yes | Must be RAW_BINARY. |
rawBinaryFile | varies | yes | A file reference or a mapping placeholder (e.g. {{Form.uploadedDocument}}). See File object. |
File object
Use when referencing a file by ID or download URL (instead of only a mapping placeholder).
| name | type | required | description |
|---|---|---|---|
id | string | yes (variant A) | File identifier. Use either id or downloadUrl, not both. |
downloadUrl | string | yes (variant B) | File download URL. Use either id or downloadUrl, not both. |
name | string | yes | File name. |
metadata | object | no | Optional: contentType, size, createdAt, updatedAt. |
Mappable fields
mappableFields declares which response properties later steps can map and supplies type hints. Each key must match the flattened JSON path your endpoint returns, without the body. prefix (for example id or user.email, not body.id).
Supported type values include: string, number, boolean, date, datetime, object, array, and file.
| name | type | required | description |
|---|---|---|---|
key | string | yes | Flattened JSON key without the body. prefix (e.g. status, user.email). |
label | string | yes | Human-readable label for the field. |
type | string | yes | Data type (see list above). |
exampleValue | varies | no | Optional example value (string, number, boolean, or null) stored with the field and shown in Streamline where sample values are displayed. |
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 update the project.
Option A: Extract from a test request
To derive fields from a real HTTP call, use the outbound webhook test endpoint. Append ?test=true to the step's webhookSessionStartUrl from the project response:
POST {webhookSessionStartUrl}?test=true
Use the same authentication as for other /v1/sessions requests (bearer token). The body is a JSON array of objects with id, type, and sampleValue for each mapped input the test request should resolve; send [] when there are no mapped inputs. A successful response includes responsePayload, requestPayload, and extractedFields. On your next project update, persist extractedFields as mappableFields, and persist responsePayload and requestPayload so field extraction has both snapshots.
Option B: Set mappable fields directly
If you already know the response structure, you can include mappableFields in the step configuration when you create or update the project via PATCH /v1/projects/{projectId}:
PATCH /v1/projects/{projectId}
{
"workflow": {
"steps": [
{
"type": "outbound_webhook",
"description": "Notify CRM",
"config": {
"request": { "..." : "..." },
"waitForResponse": true,
"mappableFields": [
{ "key": "id", "label": "User ID", "type": "string" },
{ "key": "status", "label": "Status", "type": "string" }
]
},
"edges": []
}
]
}
}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 update the step's mappableFields array in the project. Later steps that reference these fields by key use whatever is currently stored on the step.
Examples
Simple GET with no authentication or body:
{
"request": {
"method": "GET",
"url": "https://api.example.com/users",
"authentication": { "type": "NONE", "connectionId": null }
}
}Redirect to a fixed URL (Redirect users on):
{
"request": {
"method": "GET",
"url": "https://example.com/thank-you",
"authentication": { "type": "NONE", "connectionId": null }
},
"continueOnError": false,
"redirect": {
"enabled": true
}
}Redirect to a URL from an earlier step (Redirect users on):
{
"request": {
"method": "GET",
"url": "{{`Notify next workflow`.`resumeUrl`}}",
"authentication": { "type": "NONE", "connectionId": null }
},
"continueOnError": false,
"redirect": {
"enabled": true
}
}POST with a raw JSON body (string form):
{
"request": {
"method": "POST",
"url": "https://api.example.com/webhook",
"authentication": { "type": "NONE", "connectionId": null },
"body": {
"requestMode": "RAW",
"contentType": "JSON",
"content": "{\"message\": \"Hello\"}"
}
}
}POST with connection auth, path/query parameters, JSON object body with mappings, and response mapping:
{
"request": {
"method": "POST",
"url": "https://api.example.com/users/:userId",
"authentication": { "type": "AUTHENTICATED", "connectionId": "my-api-connection" },
"pathParams": [
{ "key": "userId", "value": "{{`Form`.`userId`}}" }
],
"queryParams": [
{ "key": "format", "value": "json" }
],
"headers": [
{ "key": "X-Custom-Header", "value": "custom-value" }
],
"body": {
"requestMode": "RAW",
"contentType": "JSON",
"content": {
"name": "{{`Form`.`userName`}}",
"email": "{{`Form`.`userEmail`}}",
"metadata": {
"source": "workflow",
"requestId": "{{`Form`.`requestId`}}"
}
}
}
},
"waitForResponse": true,
"mappableFields": [
{ "key": "id", "label": "User ID", "type": "string", "exampleValue": "usr-12345" },
{ "key": "status", "label": "Status", "type": "string", "exampleValue": "created" }
]
}Multipart POST with form fields:
{
"request": {
"method": "POST",
"url": "https://api.example.com/submit",
"authentication": { "type": "NONE", "connectionId": null },
"body": {
"requestMode": "MULTIPART",
"content": [
{ "key": "name", "value": "{{`Form`.`userName`}}" },
{ "key": "description", "value": "Static description" }
]
}
}
}Binary file upload with a file object:
{
"request": {
"method": "POST",
"url": "https://api.example.com/upload",
"authentication": { "type": "AUTHENTICATED", "connectionId": "file-api" },
"body": {
"requestMode": "RAW_BINARY",
"rawBinaryFile": {
"id": "file-123",
"name": "document.pdf",
"metadata": {
"contentType": "application/pdf",
"size": 1024000
}
}
}
}
}Binary file upload with a mapping placeholder:
{
"request": {
"method": "PUT",
"url": "https://api.example.com/files",
"authentication": { "type": "NONE", "connectionId": null },
"body": {
"requestMode": "RAW_BINARY",
"rawBinaryFile": "{{`Form`.`uploadedDocument`}}"
}
}
}DELETE with an optional body:
{
"request": {
"method": "DELETE",
"url": "https://api.example.com/resources/:resourceId",
"authentication": { "type": "AUTHENTICATED", "connectionId": "api-connection" },
"pathParams": [
{ "key": "resourceId", "value": "{{`Form`.`resourceId`}}" }
],
"body": {
"requestMode": "RAW",
"contentType": "JSON",
"content": "{\"reason\": \"cleanup\"}"
}
}
}Repeating Records in JSON bodies
When requestMode is RAW and contentType is JSON, the outbound body can include Repeating Records from earlier steps.
The examples below assume an earlier step named Incoming webhook exposes Repeating Records with field labels such as Order ID, Customer Name, Line items, Product, Quantity, Variant SKU, and Variant Name.
Pass Repeating Records through unchanged
Use a single placeholder when the destination should receive the same array shape that came from the earlier step.
{
"request": {
"method": "POST",
"url": "https://api.example.com/orders",
"authentication": { "type": "NONE", "connectionId": null },
"body": {
"requestMode": "RAW",
"contentType": "JSON",
"content": {
"orderId": "{{`Incoming webhook`.`Order ID`}}",
"customerName": "{{`Incoming webhook`.`Customer Name`}}",
"lineItems": "{{`Incoming webhook`.`Line items`}}"
}
}
}
}lineItems resolves to an array, not a JSON string. For example, if the source step has two Line items rows, the outbound body resolves to:
{
"orderId": "ord-1001",
"customerName": "Acme Corp",
"lineItems": [
{
"Product": "Widget A",
"Quantity": 2,
"Variant SKU": "WA-RED",
"Variant Name": "Widget A - Red"
},
{
"Product": "Widget B",
"Quantity": 1,
"Variant SKU": "WB-BLUE",
"Variant Name": "Widget B - Blue"
}
]
}Output a new array shape
Use a one-item array template when the destination needs a different shape. Streamline expands that template once for each record in Repeating Records.
Keep all mapped field labels in the same array template tied to the same Repeating Records data set. Do not mix fields from different Repeating Records data sets in one template.
{
"request": {
"method": "POST",
"url": "https://api.example.com/orders/export",
"authentication": { "type": "NONE", "connectionId": null },
"body": {
"requestMode": "RAW",
"contentType": "JSON",
"content": {
"items": [
{
"name": "{{`Incoming webhook`.`Product`}}",
"count": "{{`Incoming webhook`.`Quantity`}}",
"channel": "ERP_EXPORT"
}
]
}
}
}
}For example, if the source step has two Line items rows, the outbound body resolves to:
{
"items": [
{
"name": "Widget A",
"count": 2,
"channel": "ERP_EXPORT"
},
{
"name": "Widget B",
"count": 1,
"channel": "ERP_EXPORT"
}
]
}Output nested Repeating Records
Nested arrays work the same way. Add a nested one-item array template inside the parent template, and keep each level's placeholders aligned to that level's Repeating Records label.
The same rule applies at each level: one template, one Repeating Records data set. For example, a parent row should not combine fields from Line items with fields from a different top-level array, and a nested variantRows template should only use fields from the matching nested array.
{
"request": {
"method": "POST",
"url": "https://api.example.com/orders/export",
"authentication": { "type": "NONE", "connectionId": null },
"body": {
"requestMode": "RAW",
"contentType": "JSON",
"content": {
"orderId": "{{`Incoming webhook`.`Order ID`}}",
"exportedRows": [
{
"product": "{{`Incoming webhook`.`Product`}}",
"quantity": "{{`Incoming webhook`.`Quantity`}}",
"variantRows": [
{
"sku": "{{`Incoming webhook`.`Variant SKU`}}",
"variantName": "{{`Incoming webhook`.`Variant Name`}}"
}
]
}
]
}
}
}
}For example, if the source step has two Line items rows (the first with two variants and the second with one), the outbound body resolves to:
{
"orderId": "ord-1001",
"exportedRows": [
{
"product": "Widget A",
"quantity": 2,
"variantRows": [
{ "sku": "WA-RED", "variantName": "Widget A - Red" },
{ "sku": "WA-BLUE", "variantName": "Widget A - Blue" }
]
},
{
"product": "Widget B",
"quantity": 1,
"variantRows": [
{ "sku": "WB-GREEN", "variantName": "Widget B - Green" }
]
}
]
}Use the Repeating Records label, such as Line items, when you want to pass an entire array through. Use field labels such as Product, Quantity, Variant SKU, and Variant Name when you want Streamline to build custom objects inside an array template.
Updated 7 days ago
