The fields
Variables only work in the body. Any
{{token}} in the JSON body is substituted per
contact before sending. See the variables reference. The URL and
the headers are sent exactly as typed: a {{token}} there is not replaced, so don’t build
per-contact URLs or per-contact header values.
The Send test request button in the step’s panel shows a canned demo response. The
panel says so itself: “Demo response — live requests run when the flow engine is
enabled.” It does not call your endpoint. To test for real, publish the flow, walk
through it, and watch your endpoint’s logs and Logs.
Using the response
Each Save response to fields row plucks one value out of the JSON reply by its path (dots reach into nested objects, sodata.id reads {"data": {"id": …}}) and saves it as a
flow variable under the field name you chose. From the next step onward, use it like any
other token: name the field crm_id and write {{crm_id}} in a later message.
Two boundaries to know:
- The saved value lives in this run of the flow only. It is not written to the contact, and the next conversation starts without it. To keep something permanently, have your endpoint write it back through the REST API.
- A path that doesn’t exist in the reply (or a reply that isn’t JSON) saves nothing, and the token resolves to empty text later. Nothing errors.
{{_last_http_status}} and {{_last_http_error}} behind for the rest of the run. They aren’t
in the builder’s {} picker, so type them by hand. {{_last_http_status}} is the HTTP status
your endpoint returned (200, 404, 500), or 0 when the request never got a response: a
timeout, an unreachable host, or a body DMLY couldn’t render as JSON. {{_last_http_error}}
carries the reason in that 0 case, either the network error text or invalid_json_body. A
successful response doesn’t rewrite {{_last_http_error}}, so if an earlier HTTP Request step
in the same run set it, the old text is still sitting there; only read it alongside a
{{_last_http_status}} of 0. Neither token is set when the URL is refused outright, because
that stops the run.
A Condition step can’t branch on these. Conditions compare a fixed list of contact and
trigger fields, and none of those fields reads a variable a step saved. To see the value at
all, print it somewhere a later step writes: a Send Slack message step, or a Send to
Google Sheets row next to
{{flow_run_id}}.When the call fails, the flow does not
The step has a single output, and the run continues through it whatever happens: a timeout, an unreachable server, a500, a 404. The conversation is never held hostage by your
endpoint: the contact just moves to the next step, with any unsaved response fields left
empty.
This is about calls that were actually made. A URL the step refuses to call isn’t a failed
call, it stops the run: see What it refuses to call.
If a call silently “did nothing”, check Logs and your endpoint’s own
logs. Three lines are worth searching for:
HTTP Request rejected by the endpoint, with the status. Your endpoint answered and said no.HTTP Request failed, with the error text. The request never reached a reply: a timeout, a DNS failure, a refused connection.HTTP Request body is not valid JSON. Nothing was sent at all; see below.
What it refuses to call
The URL must be a publichttp:// or https:// address on the open internet. Requests to
localhost, private network addresses and other reserved ranges are blocked outright, and
unlike an ordinary network error, a blocked URL fails the run: the contact’s run stops at
that step and shows as failed in the automation’s analytics and in
Logs. If your endpoint lives on an office network or a dev machine,
publish it to the internet (a tunnel works) before pointing a flow at it.
Anything that isn’t an http(s) address at all is refused the same way, and fails the run the
same way: an empty Endpoint URL, free text, or the bare https:// a freshly added step
starts out with.
An empty URL publishes anyway
Publishing never checks this step’s URL. A missing or malformed endpoint only shows as an amber Needs attention badge on the node (Endpoint URL is missing or not http(s).), and
a freshly added step wears that badge from the start, because its URL box begins as just
https://. The badge also flags Body is not valid JSON. Clear both before you publish;
nothing else will stop you. See Publish a flow.
Neither badge is cosmetic, though, and at runtime the two behave in opposite ways:
- A URL still left as
https://, empty, or not anhttp(s)address fails the whole run when a contact reaches the step, exactly like a blocked address does. See What it refuses to call. - A body that can’t be rendered as JSON sends nothing at all, not an empty request. DMLY
writes
HTTP Request body is not valid JSONto Logs and the run carries on through the step’s single output, with the response fields empty and{{_last_http_error}}set toinvalid_json_body.
The badge’s JSON check is stricter than the engine. It runs a plain JSON parse on what you
typed, so a body with a token outside the quotes, like
{"qty": {{count}}}, wears the badge
even though the step renders and sends it correctly. If your body only “fails” because of a
numeric token, the badge is the thing that’s wrong, not your flow.Variables reference
Every token you can put in the JSON body.
Workspace webhooks
Event push for everything that happens in the workspace, no flow required.
Connect n8n
Point this step (or workspace webhooks) at an n8n workflow.
Build a flow
The canvas this step lives on.

