Skip to content

Add a native type: http step for calling HTTP APIs from workflows #222

@jrob5756

Description

@jrob5756

Summary

Add a first-class HTTP step type so workflows can call REST APIs without shelling out through type: script + curl and parsing the response by hand.

Motivation

Lots of workflows need to fetch from or push to an HTTP endpoint between agent calls — pulling issue lists, posting comments, hitting a webhook, calling an internal service for ground-truth data. Today the only options are:

  1. type: script + curl — works but is verbose, has to manually parse JSON in a downstream Jinja2 expression, and loses type information.
  2. Wrap it in a tool — overkill for simple GET/POST.

A native HTTP step would be deterministic, fast (no subprocess fork), and put typed response data straight into context for routing and downstream prompts.

Proposed shape

agents:
  - name: fetch_issue
    type: http
    method: GET
    url: "https://api.github.com/repos/{{ workflow.input.repo }}/issues/{{ workflow.input.number }}"
    headers:
      Authorization: "Bearer {{ env.GITHUB_TOKEN }}"
      Accept: "application/vnd.github+json"
    timeout_seconds: 30
    retry:
      max_attempts: 3
      backoff: exponential
    routes:
      - when: "fetch_issue.output.status_code == 404"
        to: $end
      - when: "fetch_issue.output.status_code >= 500"
        to: notify_oncall
      - to: triage_agent

The step's output in context should expose:

  • status_code: int
  • headers: dict[str, str]
  • body: str (raw)
  • json_body: Any | None (parsed if Content-Type is JSON, else None)
  • elapsed_ms: float

Optional fields on the step: body / json_body (request body, mutually exclusive), params (query string), follow_redirects: bool, verify_ssl: bool.

Why now

  • We already have the surrounding machinery: retry:, routes:, output:, Jinja2 templating, context accumulation, event emission. The HTTP step plugs into all of it.
  • Lives next to ScriptExecutor in executor/ and uses the same dispatch path in the engine — no engine core changes.

Open questions

  • Should output: schema validation apply to json_body? (Probably yes when present.)
  • Default timeout? Suggest 60s to match common HTTP client defaults.
  • Do we ship with httpx (already async-native) or add another dependency? httpx is the obvious choice.
  • Auth helpers — start with header-based and let users template in tokens, or add typed auth: bearer | basic | none upfront?
  • Body templating: support both body: "raw {{ template }}" and json_body: { key: "{{ value }}" }, with the latter auto-serialized.

Acceptance criteria

  • type: http accepted by the YAML schema with the fields above
  • Validator rejects conflicting fields (e.g. body + json_body, missing url)
  • HTTP executor returns the documented output shape
  • Integrates with existing retry: policy (transient 5xx and timeouts retry)
  • Routes can match on status_code, headers, and json_body.*
  • Example added under examples/
  • Tests for: success path, 4xx/5xx routing, retry on transient failure, timeout enforcement, template rendering of url/headers/body

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:configYAML schema, loader, validatorarea:executorAgent and script execution, templates, outputenhancementNew feature or requestideaSpeculative feature proposal — not yet committed

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions