Fetch
Perform HTTP requests, upload blobs, and handle responses in your circuits
Introduction to Fetch
The circuit.core.fetch primitive lets you call any HTTP(S) endpoint, upload or download files, and process responses (JSON, text, binary, or Server-Sent Events). Configure it entirely via your circuit JSON—no direct code access is needed.
Step Definition
"steps": [
{
"name": "fetch_data",
"function": "circuit.core.fetch",
"input": { /* see below */ },
"blob_mappings": [ /* optional: inject blob data into body */ ],
"stream_mapping": [ /* optional for SSE */ ]
}
]input Properties
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The target endpoint URL (can include selectors, e.g. ${{ secrets.api_url }}/path). |
method | string | Yes | HTTP verb: GET, POST, PUT, DELETE, PATCH. |
headers | object<string,string> | No | Key/value map of headers; values may use selectors. |
body | object or string | No | JSON object or raw string for the request body; selectors allowed. |
timeout | number | No | Request timeout in seconds. |
body_options | object | No | Body encoding and transformation options (see below). |
query_params | object<string,string> | No | URL query parameters; values support ${{ }} expressions. Empty/null values are omitted. |
body_options
Controls how the request body is encoded and filtered:
| Field | Type | Default | Description |
|---|---|---|---|
encode_as | string | json | Encoding format: json (application/json) or form_urlencoded (application/x-www-form-urlencoded). |
skip_null_values | boolean | false | Remove null values from objects and null elements from arrays. |
skip_empty_arrays | boolean | false | Remove empty arrays from the body. |
skip_empty_objects | boolean | false | Remove empty objects from the body. |
Blob Mappings
blob_mappings is a step-level field (a sibling of input, not nested inside it) that injects blob data into the JSON request body. Each group inserts an array of objects at a specific container path.
| Field | Type | Required | Description |
|---|---|---|---|
to_attribute | string | Yes | Container path in the body where the array of objects will be inserted (e.g. image, messages[0].content). |
mappings | array | Yes | Field definitions for each object element in the array. |
Each entry in mappings:
| Field | Type | Required | Description |
|---|---|---|---|
from_selector | string | No* | Memory selector to resolve blob IDs or URLs from memory. |
to | string | Yes | Field path within each object element. Use [$] for array iteration (e.g. [$].url, image.url). |
encode_as | string | No | Encoding strategy: url (default), base64, or base64_nodata. |
value | any | No* | Static value to inject (alternative to from_selector). |
* Either from_selector or value must be provided.
"blob_mappings": [
{
"to_attribute": "messages[0].content",
"mappings": [
{ "from_selector": "${{ input.image_blob_id }}", "to": "[$].image_url", "encode_as": "url" },
{ "value": "user_upload", "to": "[$].type" }
]
}
]The engine resolves each blob by selector (or uses the static value), encodes it per encode_as, and injects the resulting array of objects at to_attribute in the request body.
Stream Mapping (SSE)
If your endpoint returns Server-Sent Events, you can capture and accumulate fragments via stream_mapping:
"stream_mapping": [
{
"sse_selector": "choices[0].delta.content",
"sse_name": "delta_accumulator"
}
]| Field | Type | Required | Description |
|---|---|---|---|
sse_selector | string | Yes | JMESPath selector against each event's JSON chunk. |
sse_name | string | Yes | Key under output.stream_accumulators where the value is accumulated. |
sse_event_type | string | No | SSE event type filter—only process chunks where json["type"] matches this string exactly (e.g. response.output_text.delta). If omitted, all chunks are processed. |
accumulator_mode | string | No | How to accumulate across chunks: last_value, string_concat, array_append, array_by_key, object_merge. If omitted, the default is chosen by value type. |
primary_key | string | No | When accumulator_mode is array_by_key, the key used to identify and merge array elements (e.g. index, id). |
merge_schema | object | No | Per-path merge rules for nested fields. Keys are dot-separated paths (e.g. function.arguments); each value specifies a mode (and optional primary_key for array_by_key). |
Outputs
After execution, the Fetch step populates its output object, which you can reference with selectors:
// Non-streaming
${{ fetch_data.output.json_body }} // parsed JSON
${{ fetch_data.output.text_body }} // raw text
${{ fetch_data.output.artifact_urls }} // array of URLs (binary blobs)
// Streaming
${{ fetch_data.output.stream_accumulators.delta_accumulator }}Use these values in subsequent steps or in your top-level outputs:
"outputs": [
{
"name": "data",
"value": "${{ fetch_data.output.json_body }}"
}
]Examples
Simple GET Request
{
"name": "get_user_profile",
"steps": [
{
"name": "profile",
"function": "circuit.core.fetch",
"input": {
"url": "https://api.example.com/users/${{ input.user_id }}",
"method": "GET",
"headers": {
"Authorization": "Bearer ${{ secrets.api_key }}"
}
}
}
],
"outputs": [
{ "name": "profileData", "value": "${{ profile.output.json_body }}" }
]
}POST with JSON Body
{
"name": "create_ticket",
"steps": [
{
"name": "ticket",
"function": "circuit.core.fetch",
"input": {
"url": "https://api.example.com/tickets",
"method": "POST",
"headers": { "Content-Type": "application/json" },
"body": {
"subject": "${{ input.subject }}",
"description": "${{ input.description }}"
}
}
}
],
"outputs": [
{ "name": "ticketId", "value": "${{ ticket.output.json_body.id }}" }
]
}File Upload & Fetch Binary
{
"name": "upload_and_fetch",
"steps": [
{
"name": "uploader",
"function": "circuit.core.fetch",
"input": {
"url": "https://my.blob.service/blobs",
"method": "POST"
},
"blob_mappings": [
{
"to_attribute": "files",
"mappings": [
{ "from_selector": "${{ input.blob_id }}", "to": "[$].url", "encode_as": "url" }
]
}
]
},
{
"name": "downloader",
"function": "circuit.core.fetch",
"input": {
"url": "https://my.blob.service/file-download/${{ uploader.output.artifact_urls[0] }}",
"method": "GET"
}
}
],
"outputs": [
{ "name": "downloadedUrl", "value": "${{ downloader.output.artifact_urls[0] }}" }
]
}Next: Adapters – integrate SSE, Webhooks, and custom protocols!