ModelWorks logoModelWorks

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

JSON
"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

FieldTypeRequiredDescription
urlstringYesThe target endpoint URL (can include selectors, e.g. ${{ secrets.api_url }}/path).
methodstringYesHTTP verb: GET, POST, PUT, DELETE, PATCH.
headersobject<string,string>NoKey/value map of headers; values may use selectors.
bodyobject or stringNoJSON object or raw string for the request body; selectors allowed.
timeoutnumberNoRequest timeout in seconds.
body_optionsobjectNoBody encoding and transformation options (see below).
query_paramsobject<string,string>NoURL query parameters; values support ${{ }} expressions. Empty/null values are omitted.

body_options

Controls how the request body is encoded and filtered:

FieldTypeDefaultDescription
encode_asstringjsonEncoding format: json (application/json) or form_urlencoded (application/x-www-form-urlencoded).
skip_null_valuesbooleanfalseRemove null values from objects and null elements from arrays.
skip_empty_arraysbooleanfalseRemove empty arrays from the body.
skip_empty_objectsbooleanfalseRemove 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.

FieldTypeRequiredDescription
to_attributestringYesContainer path in the body where the array of objects will be inserted (e.g. image, messages[0].content).
mappingsarrayYesField definitions for each object element in the array.

Each entry in mappings:

FieldTypeRequiredDescription
from_selectorstringNo*Memory selector to resolve blob IDs or URLs from memory.
tostringYesField path within each object element. Use [$] for array iteration (e.g. [$].url, image.url).
encode_asstringNoEncoding strategy: url (default), base64, or base64_nodata.
valueanyNo*Static value to inject (alternative to from_selector).

* Either from_selector or value must be provided.

JSON
"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:

JSON
"stream_mapping": [
  {
    "sse_selector": "choices[0].delta.content",
    "sse_name": "delta_accumulator"
  }
]
FieldTypeRequiredDescription
sse_selectorstringYesJMESPath selector against each event's JSON chunk.
sse_namestringYesKey under output.stream_accumulators where the value is accumulated.
sse_event_typestringNoSSE 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_modestringNoHow 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_keystringNoWhen accumulator_mode is array_by_key, the key used to identify and merge array elements (e.g. index, id).
merge_schemaobjectNoPer-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:

JSON
// 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:

JSON
"outputs": [
  {
    "name": "data",
    "value": "${{ fetch_data.output.json_body }}"
  }
]

Examples

Simple GET Request

JSON
{
  "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

JSON
{
  "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

JSON
{
  "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!

On this page

On this page