Make one bounded HTTP request. Use this node for public APIs, private services exposed through a bound connectivity service, ordered query APIs, and signed requests.
Status: Alpha.
Request configuration
| Setting | Description | Default |
|---|---|---|
| Method | GET, POST, PUT, PATCH, DELETE, HEAD, or OPTIONS | GET |
| URL | Public HTTPS URL, or an HTTP(S) private URL when a connectivity service is bound | — |
| Allowed origin | Optional exact scheme, host, and port guard for dynamic URLs | — |
| Query Params | Ordered key/value rows; repeated keys are preserved | — |
| Headers | Ordered request-header rows | — |
| Authentication | Bearer token, Basic, API key, connection, AWS SigV4, or signature | None |
| Body | None, JSON, Form, Text, or Raw | None |
| Content type | The media type a Raw body is sent under | — |
| Include Response Headers | Include sanitized response headers | Off |
| Fail on non-success status | Fail on non-2xx responses instead of returning them as data | On |
| Max response body (bytes) | The largest response body the request accepts; see Response size | 200 000 |
The node never follows redirects automatically. A 3xx response succeeds with bounded redirect evidence, including whether the target still satisfies the configured origin policy. A new request is always required to follow it.
JSON and +json responses are decoded when possible. Authorization, Set-Cookie, Cookie, and Location response-header values are redacted when response headers are included. The body is bounded; see Response size.
Response size
A response body larger than Max response body fails the node with a response_too_large error before any of it is exposed. The default is 200 000 bytes (200 KB). Raise it on the node, up to 4 000 000 bytes (4 MB), when an endpoint cannot be paged or filtered under the default; a larger value fails the save. The transport stops reading at the limit, so a refused response is never held in memory past it.
The error names the limit that was applied and, in details, the ceiling it can be raised to:
{"type": "response_too_large", "message": "HTTP response exceeded the 200 KB max_response_bytes limit",
"details": {"max_response_bytes": 200000, "max_response_bytes_ceiling": 4000000}}
Three other limits sit around this one:
- Connectivity-bound requests are capped at 700 000 bytes (700 KB). The request runs in a step container and hands its response back through the step's captured output, which the step runtime reads with a 1 MiB bound; the base64 encoding takes the rest. A larger Max response body is honored on direct requests and lowered to that cap on any request that runs through a connectivity binding, public host or not, and the error then says so:
"…exceeded the 700 KB limit of the connectivity transport", withconfigured_max_response_bytesandconnectivity_max_response_bytesindetails. - Run history stores at most 256 KiB per step output. Within one uninterrupted run a larger body flows to downstream nodes in full, and only the run view keeps a truncated preview of
responsemarked__truncated__. A run that suspends after this node (an approval, a wait, human input) resumes from that persisted preview, so downstream nodes then read the preview instead of the body. Extract what you need with Declared outputs, or reduce the body with a transform, before any suspending node. - Tools and app tools never carry this setting. A tool call built from an
http.requestTool reads at the default, because the result of every tool call is bounded to the same 200 KB after it runs (the result ceiling is measured over the whole delivered result, so the request would run and the answer would be withheld). An app or agent that needs a larger body reduced calls a workflow instead (rollout.app.workflow_runfor an app) whose HTTP request node raises the limit and whose next step projects the body down. HTML text and link projection (body_text,links) is computed for bodies up to 200 KB only.
Public and private destinations
Without a connectivity service, the destination must be a public HTTPS DNS hostname. Loopback, private, tailnet, IP-literal, credential-bearing, and plaintext HTTP URLs are rejected.
To reach a private service or tailnet host, bind a connectivity service such as Tailscale in the node's Services section. Private RFC1918 addresses, 100.64.0.0/10 tailnet addresses, MagicDNS names, and plaintext HTTP are then permitted. The request runs in the isolated network step beside that service; merely declaring a service is not enough—the runtime must supply the connectivity proxy contract.
When Allowed origin is set, every request must use that exact scheme, hostname, and effective port. Paths and query strings may vary. Userinfo and fragments are always rejected.
Header and query rows
A row is an object with a name and a value:
{"key": "Accept", "value": "application/json"}
key is the canonical name and name is accepted as an alias for it, so a row written by hand works either way. enabled is optional bookkeeping the row editor supplies: a row that omits it is sent, and only false turns a row off. All of these reach the wire:
{"key": "Accept", "value": "application/json", "enabled": true}
{"key": "Accept", "value": "application/json"}
{"name": "Accept", "value": "application/json"}
A row that names neither key nor name, or names one that is not text (null, a number), is refused when the workflow or Tool is saved, naming the accepted shape, rather than disappearing on the way to the request. A row whose name is present but blank is the state the editor writes the moment you add a row; it saves and sends nothing.
A header row's value is text. A {"$secret": "NAME"} reference is a query-parameter value; written on a header row it is refused at save. Send a credential through Authentication instead (Bearer token, or API key for a custom header), which resolves it and keeps it out of run history.
The same shape is published as headers.items and query_params.items in the node's config_schema, so node_types answers it without guessing.
Request body and its media type
The body is one object naming its type and content:
{"type": "json", "content": {"title": "finding"}}
A json content may be an object (encoded for you) or a string you already encoded. The request declares the media type its own encoding produced:
| Body type | Content-Type sent |
|---|---|
| JSON | application/json |
| Form encoded | application/x-www-form-urlencoded |
| Text | text/plain |
| Raw | none, unless content_type names one |
| None | none |
A Content-Type row in Headers always wins, in any casing. For a Raw body, declare the media type on the body itself instead of adding a headers row:
{"type": "raw", "content": "<order/>", "content_type": "application/xml"}
The declaration must be a real media type (application/xml, text/csv; charset=utf-8); anything else fails the save. Only a Raw body names one — JSON, Form encoded, and Text each name the media type their own encoding produces, and None sends no body to describe — so content_type on any other type fails the save too, rather than putting a header on the wire that lies about the payload. Changing the body type away from Raw drops the declaration with it.
The media type is applied before the request is signed, so a signature and connection signing cover the exact headers sent.
Ordered query parameters and secrets
Query rows stay in authored order, after any query already present in the URL. Repeated keys are not collapsed:
https://example.com/search?existing=1
artist_mbids=a
artist_mbids=b
The request is sent as:
https://example.com/search?existing=1&artist_mbids=a&artist_mbids=b
A query value can select a workspace secret. Its durable value is a tenant-secret reference, never plaintext. URL query values can use the exact reserved form:
https://example.com/flex?token={{ secret("FLEX_TOKEN") }}
Secret names use uppercase letters, digits, and underscores and must start with a letter. The resolved value is URL-encoded, registered for run redaction, and used only for the current request. Dry runs validate the placeholder without reading the secret store.
Authentication
Choose one method. Every credential is a workspace secret; plaintext tokens, passwords and keys are not accepted. The method owns what it sends: its header or query parameter replaces a row with the same name. Each resolved credential, and everything built from it, is redacted from run history.
- Bearer token sends
Authorization: Bearer <token>. Store the raw token. - Basic sends
Authorization: Basic <base64(username:password)>; the password is the secret. - API key sends the key under a header or query parameter name you choose, with an optional prefix (
Token abc123). - Connection uses a tenant-scoped connection and its provider-owned HTTP authentication contract.
- AWS SigV4 uses an AWS connection plus the signing service and optional region.
- Signature signs the request with a workspace secret: HMAC-SHA256/512, or RSA/ECDSA with a PEM key. Choose what is signed and in what order (
timestamp,method,path,query,body,header:NAME,literal:TEXT), the encoding, where the signature goes, an optional timestamp and nonce, an optional receive window, and an optional API key sent beside it. Start from a preset: Binance-style signs the query and sendsX-MBX-APIKEY; Header signature signs timestamp, method, path and body into a base64 header.
A signature covers the exact bytes sent on the wire, including the URL's existing query, authored rows in their order, the receive window and generated values.
Status handling
With Fail on non-success status enabled, a non-2xx response fails the node and exposes a structured http_error with the status and retry timing when available. Redirects remain the evidence-only exception described above.
Disable it when an error status is business data—for example, an API that uses 400 to mean an empty search window. The node then succeeds for every HTTP status and returns response.ok (true for 2xx, false otherwise), so a downstream rollout.if can branch explicitly.
Transport failures, invalid destinations, timeouts, malformed proxy responses, and responses above Max response body still fail regardless of this setting.
Inputs and outputs
The collapsed url and body input ports override their configured values when wired. Method, query rows, headers, and authentication remain governed node configuration.
| Output | Description |
|---|---|
| response.status | HTTP status code |
| response.body | Decoded JSON when applicable; otherwise the bounded response body |
| response.ok | Present when Fail on non-success status is disabled |
| response.headers | Sanitized rows when Include Response Headers is enabled |
| response.redirect | Evidence for a 3xx target; never automatic execution authority |
Declared outputs can extract and type fields beneath response for downstream nodes.
Per-item pacing
HTTP request runs once with whatever it receives, a list included. To send one request per item, set For each item to Each item in its Execution settings; every output then becomes a list with one value per item. In Each item mode, Execution & runtime includes:
- Delay between items (ms) — waits between attempts, from 0 to 60,000 ms. No delay is added after the final item.
- On item failure — Fail batch (default), Skip item, or Collect errors.
Collect errors keeps successful outputs and adds _errors entries with index, item, and a structured error, plus _failed_count. The workflow completes with errors while downstream nodes can still consume the successes.
Troubleshooting
A header or query row is missing from the request
- Check the row carries
key(or its aliasname) as text. A row carrying neither, or a name that isnullor a number, is refused when you save. - Check the row's name is not blank. A blank name saves and sends nothing.
- Check
enabledis notfalse. Omittingenabledsends the row.
The server answers 415 Unsupported Media Type
- Set the body type to JSON, Form, or Text so the request declares its media type, or name one with
content_typeon a Raw body. - A
Content-Typeheader row overrides all of that, so check for one that disagrees with the payload.
Private URL is rejected
- Bind the connectivity service in Services.
- Confirm the service is active and supplied the runtime proxy contract.
- Match the URL's scheme, host, and port to Allowed origin when one is configured.
Authentication fails
- Select workspace secrets rather than entering plaintext.
- For a signature, compare what is signed, the encoding, and the timestamp, signature, receive-window and API-key names with the provider's documentation.
- Confirm the selected connection belongs to the current tenant and permits the destination.
Response is too large
- Raise Max response body under Response options, up to 4 MB. The error's
details.max_response_bytesis the limit that was applied anddetails.max_response_bytes_ceilingthe most it can be. - On a connectivity-bound request the cap is 700 KB regardless of the setting; the error names the connectivity transport when that is what lowered it. Reach the endpoint publicly, or page it.
- On a Tool or app tool the limit is the 200 KB default and cannot be raised. Page the endpoint through one grant first: expose the
query_paramsobject, or a{{page}}slot in a fixed query row, so the caller requests each page. When a page still cannot fit, run a workflow that reduces the body instead. - Otherwise request a narrower payload, use pagination, or filter at the API.
Timeout
- Increase Timeout (ms) under Execution & runtime. The request transport and network step use the same node deadline.