Skip to content

HTTP Step

The HTTP step makes requests to external APIs. The step editor has a familiar HTTP Client interface with tabs for Params, Headers, Body, and Advanced.

HTTP step editor showing URL, method, and tabbed configuration
FieldDescriptionExample
URLThe endpoint to call. Supports templates.https://api.example.com/users/{{ initial.userId }}
MethodHTTP method dropdownGET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
ConnectionOptional — pick a connection to handle auth automatically. See Using Connections for Auth below.oauth-google, salesforce, twilio, etc.

Params tab — Add query parameters as key-value pairs. These are merged with any parameters already in the URL.

Headers tab — Add request headers as key-value pairs:

HeaderValue
AuthorizationBearer {{ $env.API_TOKEN }}
X-Custom-Header{{ initial.tenantId }}

Select a body type from the dropdown:

JSON — A JSON editor for structured request bodies. Content-Type is set automatically.

HTTP step body tab with JSON editor for structured request bodies
KeyValue
customerId{{ initial.customerId }}
actionsync

Form — URL-encoded key-value pairs (application/x-www-form-urlencoded):

KeyValue
grant_typeclient_credentials
client_id{{ $env.CLIENT_ID }}

Raw — A text editor for XML, plain text, or any custom format. Set the Content-Type header manually in the Headers tab.

Multipart — For file uploads. Add fields with a name, value, and optional filename/content type.

FieldDefaultDescription
Timeout30 secondsMax wait time (up to 30 minutes)
Follow RedirectsonAutomatically follow 3xx redirects
Max Redirects10Maximum redirect hops
Retry Attempts2Number of times to retry on transient failures (0–10). See Retries below for what counts as transient.

The HTTP step retries transient failures automatically with exponential backoff. The Advanced tab exposes a single Retry Attempts field — set it to control how many times the step will retry before giving up. Everything else about the retry behavior (which methods, which status codes, which network errors) is handled by smart defaults you don’t have to configure.

What gets retried automatically:

  • Idempotent HTTP methodsGET, PUT, HEAD, DELETE, OPTIONS. The POST and PATCH methods are intentionally excluded by default so a non-idempotent operation isn’t accidentally re-applied. Exception: HTTP 429 Too Many Requests retries on every method, including POST/PATCH, because a 429 response means the server explicitly rejected the request before any work happened — there’s no risk of double-execution. See Error Handling → retryPolicy schema for the http.statusCodesAnyMethod field that controls this carve-out.
  • HTTP status codes408 Request Timeout, 413 Payload Too Large, 429 Too Many Requests, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout, and Cloudflare’s 521 Web Server Down / 522 Connection Timed Out / 524 A Timeout Occurred.
  • Network error codesETIMEDOUT, ECONNRESET, EADDRINUSE, ECONNREFUSED, EPIPE, ENOTFOUND, ENETUNREACH, EAI_AGAIN.
  • 4xx client errors (other than the ones listed above) — never retried. A 400 or 404 usually means a real problem with the request itself, not a transient blip.

Set Retry Attempts to 0 to disable retries entirely — useful when the endpoint isn’t idempotent and even a GET retry could cause issues (e.g., charging a credit card with GET for some reason).

For anything beyond Retry Attempts, use the step’s Error Handling panel. Pick a preset (Smart, Aggressive, Rate-limits-only) or switch to Custom mode to opt specific methods, status codes, or error codes in or out. See Error Handling → The mental model for the preset semantics.

After the step runs, reference the response in later steps:

{{ my-http-step.status }} // HTTP status code (200, 404, etc.)
{{ my-http-step.statusText }} // HTTP status text ("OK", "Not Found", etc.)
{{ my-http-step.ok }} // true if 2xx status
{{ my-http-step.body }} // Parsed response (JSON/XML auto-detected)
{{ my-http-step.body.users }} // Access nested fields from JSON response
{{ my-http-step.text }} // Raw response text
{{ my-http-step.headers }} // Response headers
{{ my-http-step.url }} // Final URL (after any redirects)
{{ my-http-step.redirected }} // true if any redirects were followed
{{ my-http-step.retryCount }} // How many retry attempts were made
{{ my-http-step.timings.phases.total }} // Total request time in ms
{{ my-http-step.raw.request }} // The outgoing request (method, url, headers, body) for debugging

Response bodies are automatically parsed as JSON or XML based on content type. If parsing fails, the raw text is returned in body.

The timings.phases object breaks the request into per-phase millisecond measurements — useful for diagnosing slow upstreams:

PhaseWhat it measures
waitTime spent in the agent queue before the connection started
dnsDNS lookup
tcpTCP handshake
tlsTLS handshake (HTTPS only)
requestTime spent sending the request
firstByteTime-to-first-byte from the server
downloadTime spent downloading the response body
totalEnd-to-end total

The HTTP step has a Connection field at the top of the request fields. Pick a connection from the dropdown and QuickFlo injects the right Authorization header for you — including automatic OAuth token refresh on 401/403, after which the step retries the request once with the fresh token.

Connection typeHeader injected
Any OAuth connection (Google, Slack, Salesforce, Custom OAuth, etc.)Authorization: Bearer <accessToken> (token-type-aware) — refreshed automatically on 401/403
Basic AuthAuthorization: Basic base64(username:password)
TwilioTreated as Basic Auth using AccountSid:AuthToken
API KeyWhatever header name and prefix the connection is configured with — defaults to Authorization: <apiKey>
Any other connection with an accessToken fieldAuthorization: Bearer <accessToken> (no auto-refresh — Custom OAuth is the path for refreshable tokens)

Pick the connection in the Connection dropdown on the request field row. You don’t need to add anything to the Headers tab — auth is applied automatically.

If you’d rather not use a connection, or you need to do something the connection injection doesn’t support (e.g., signature-based auth), you can still template credentials directly into headers:

HeaderValue
AuthorizationBearer {{ $env.API_TOKEN }}
Authorization{{ $connections.my-api.apiKey }}
X-Hmac-Signature{{ initial.body | hmac: $env.HMAC_SECRET }}

This pattern is also useful for HMAC-signed APIs (Twilio request signature, AWS Signature V4) where the auth header is computed per request rather than fixed.