Outbound Webhook Step
Send HTTP requests to external endpoints during a workflow, and optionally capture the response so you can map values into later steps.
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.
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).
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. |
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. |
Request
The request object defines the HTTP call.
| name | type | required | description |
|---|---|---|---|
method | string | yes | HTTP method: GET, POST, PUT, or DELETE. |
url | string | yes | Endpoint URL. Path placeholders use the :name form (for example :userId); supply values in pathParams. |
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 }
}
}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.
