Step HTTP (:http)
Quick Answer
Use this reference for the :http step schema, required fields, persistence behavior, and large-payload safety defaults.
Use for HTTP APIs. Prefer :connection with a :http-api slot.
Canonical Shape
Core fields:
| Field | Type | Required | Notes |
|---|---|---|---|
:connection | keyword/string | Yes* | Slot or connection id |
:url | string | Yes* | Full URL (ad hoc request) |
:path | string | No | Appended to connection base URL |
:method | keyword | No | :get, :post, :put, :patch, :delete |
:headers | map | No | Header map |
:query | map | No | Query params |
:json | map | No | JSON body |
:form | map | No | URL-encoded form body |
:multipart | vector | No | Multipart parts |
:body | any | No | Raw body |
:body-from-ref | map | No | Load body from blob ref |
:content-type | keyword/string | No | Body content type |
:accept | keyword | No | Accept header helper |
:response-as | keyword | No | :auto, :json, :text, :bytes, :edn, :xml |
:parser-fn | form | No | Custom parse function |
:template | keyword | No | :http-request template id |
:data | map | No | Template variables |
:auth | map | No | Step-level auth (if not from connection) |
:client-opts | map | No | Transport overrides |
:timeout | int | No | Timeout seconds (common option) |
:retry | map | No | Retry policy (common option) |
:persist | map | No | Persist large/binary response refs |
* Provide either :connection or :url.
Limits And Behavior
- Request body modes are mutually exclusive in practice: prefer one of
:json,:form,:multipart,:body,:body-from-ref. - When both
:jsonand:bodyare set,:jsonwins. :response-as :autouses the responseContent-Typeto choose:json,:text, or:bytes.- Results are inlined up to the 256 KB limit; larger payloads require
:persist. - If payload size is unknown/unbounded (exports, pagination, generated files), prefer
:persistfrom the start. - If the persisted response is just a temporary runtime artifact, make that explicit with
:persist {:type :blob :tier :ephemeral}instead of relying on the retained default. - Binary responses are not inlined; use
:persist {:type :blob ...}for any binary body. - If the response is truncated, the step fails unless
:persist {:type :blob ...}is set. - Use
:persist {:type :blob}for large payloads; downstream function steps can hydrate persisted HTTP responses explicitly with:input {:resp http-step-result}and:load [:resp]. - Prefer
:tier :ephemeralfor downloads, exports, generated media, and other response blobs that only need to survive the current workflow or near-term consumption. - Use
:persist {:type :blob :path ... :filename ...}when you want artifacts grouped under a stable relative subpath beneath the default runtime-managed pathworkspaces/<ws>/persist/<flow>/<step>/<uuid>/.... - Use
:persist {:type :blob :slot <slot> ...}when installers should control the storage binding and storage root. - To download binaries safely, set
:response-as :bytesand:persist {:type :blob :tier :ephemeral}. In this mode Breyta can stream the HTTP response directly to blob storage (avoids buffering the full payload in memory). - For streamed blob responses, Breyta enforces persist-tier write limits by default (for example, ephemeral up to 4 GB) instead of the default inline HTTP response limit.
- For retained artifacts, switch back to the default retained tier intentionally; do not use retained storage just because a response is large.
:tier :ephemeralis the HTTP-step option for temporary streamed response blobs; downstream non-HTTP persists still use the retained default.- If you set
:client-opts {:max-response-bytes ...}explicitly, streamed blob responses enforce both limits (the lower of:max-response-bytesand the persist-tier write limit). - Leave
:client-opts :max-response-bytesunset when you want streamed blob persists to use the full persist-tier cap. The explicit override remains bounded for buffered, non-streaming response safety. - Non-streaming responses (e.g. JSON parsing) are buffered in memory up to
:max-response-bytes. - Binary blobs persisted from
:httpare searchable by metadata/path context;:ephemeraltier does not extract raw content text by default. - To make binary blobs searchable by domain text, use
:persist {:type :blob :search-index {...}}(for example:text,:tags,:source-label). :persist :pathand:persist :filenamesupport{{...}}interpolation from resolved step params (for exampledata.*,input.*,query.*) plus runtime fields likeworkspace-id,flow-slug, andstep-id.:persist :pathmust be a relative subpath. Do not include a leading/or...- When
:persist :slotpoints at a blob-storage slot, platform-backed writes land atworkspaces/<ws>/storage/<root>/<path>/<filename>. - For end-user installations,
<root>defaults toinstallations/<profile-id>/<prefix>from the slot config until the installer explicitly saves another root. - Connected persists do not add the default
persist/<flow>/<step>/<uuid>segments. :persist :slotalways uses a local blob-storage slot declared in:requires, such as:archive.- Two flows share persisted HTTP outputs only when installers explicitly point their local blob-storage slots at the same concrete storage root.
- Blob-storage slots always synthesize required setup for the storage root. Authors can customize it with
:config {:prefix ...}on the slot, but cannot turn it off. - Templates only cover request shape; step-level keys like
:persiststay on the step. - Auth must use secret references. Inline tokens or API keys are rejected.
- Prefer connection auth (
:requireswith:auth), or set step-level auth with:auth {:type :bearer|:api-key :secret-ref :my-secret}. :body-from-refand multipart:from-refare preferred for large uploads from prior persisted artifacts.
Auth Variant: Google Service Account (:google-service-account)
Use this when you need unattended OAuth access to Google APIs on a schedule (e.g., Drive folder sync). The flow mints access tokens from a service account JSON secret.
Example (Google Drive list):
(flow/step :http :list-drive-files
{:type :http
:title "List Drive files"
:url "https://www.googleapis.com/drive/v3/files"
:method :get
:query {:q "'<folder-id>' in parents and trashed = false"
:fields "nextPageToken,files(id,name,mimeType,modifiedTime,size,driveId)"
:pageSize 1000
:supportsAllDrives "true"
:includeItemsFromAllDrives "true"}
:auth {:type :google-service-account
:secret-ref :google-drive-service-account
:scopes ["https://www.googleapis.com/auth/drive.readonly"
"https://www.googleapis.com/auth/drive.metadata.readonly"]}})
Canonical Example
;; In the flow definition:
;; :templates [{:id :download-report
;; :type :http-request
;; :request {:path "/reports/latest" :method :get}}]
;; :functions [{:id :http-headers
;; :language :clojure
;; :code "(fn [input] {\"X-Request-Id\" (:request-id input)})"}]
'(let [headers (flow/step :function :build-http-headers
{:ref :http-headers
:input (flow/input)})
users (flow/step :http :get-users
{:connection :api
:path "/users"
:method :get
:query {:limit 10}
:headers headers
:retry {:max-attempts 3
:backoff-ms 500}})
report (flow/step :http :download-report
{:connection :api
:template :download-report
:data {:customer-id "cust-77"
:report-id "rep-42"
:run-date "2026-03-23"}
:response-as :bytes
:persist {:type :blob
:slot :archive
:path "{{data.customer-id}}/{{data.run-date}}"
:filename "summary-{{data.report-id}}.pdf"
:tier :ephemeral}})]
{:users users
:report report})
Author the matching blob-storage slot like this:
{:requires [{:slot :archive
:type :blob-storage
:label "Archive storage"
:config {:prefix {:default "reports"
:label "Folder prefix"
:placeholder "reports/customer-a"}}}]}
With :persist {:slot :archive ...}, platform-backed writes land under:
workspaces/<ws>/storage/<root>/<path>/<filename>