Skip to main content

Reference recipe

Overview

This recipe is a single runnable TypeScript filereference-recipe.ts — that demonstrates a full integration with the Agent Harness v0 API:
  • Create a run with POST /v1/runs and an idempotency key
  • Stream progress with SSE (GET /v1/runs/{id}/events/stream) using @microsoft/fetch-event-source (Bearer auth)
  • Handle run.awaiting_input by calling POST /v1/runs/{id}/signal with actions approve, reject, or submit_input
  • Observe terminal events: run.worker.succeeded, run.worker.failed, run.cancelled, run.limit_exceeded
  • Confirm final run status with GET /v1/runs/{id}
  • Query GET /v1/usage for aggregated usage in the current billing period
Events on the wire match the public envelope: { seq, type, timestamp, payload: { redacted, value } } (no internal event id). See Events reference for every type and payload field.

Prerequisites

  • Node.js 20+
  • An API key (key_id:secret) — see Authentication
  • Install the SSE helper (required for Authorization headers):
npm install @microsoft/fetch-event-source
From a checkout of this repository, the dependency is also listed in package.json as a devDependency so npm install at the repo root is enough for npx tsx.

Running the recipe

npm install @microsoft/fetch-event-source
API_BASE=https://api.example.com API_KEY=key_abc:secret_xyz npx tsx documentation-external/recipes/reference-recipe.ts
  • Use API_BASE=http://localhost:3000 (or your local API URL) for development.
  • Set DEMO_MODE=true to automatically send approve when the run emits run.awaiting_input, without typing on stdin.

Section-by-section walkthrough

Configuration

The script reads API_BASE, API_KEY, and optional DEMO_MODE from the environment. It exits early if API_KEY is missing. Adjust defaults in the file if you prefer a config file or secret manager.

API helper (apiRequest)

apiRequest wraps fetch with:
  • Authorization: Bearer <key_id>:<secret>
  • JSON request/response handling
  • A typed error message on non-2xx using { error, reason_code, request_id }
  • Retries on 5xx responses (up to three backoff attempts with exponential delay)

Creating a run (createRun)

POST /v1/runs sends a minimal input / metadata object and a fresh idempotency-key header (via crypto.randomUUID()). Customize the body to pass workspace_id, subject_id, or other allowed create fields per OpenAPI.

Streaming events (streamEvents)

Opens GET /v1/runs/{id}/events/stream with fetchEventSource, listens for SSE event name run_event, and parses JSON into the public event shape. Dispatch highlights:
  • step.progress with kind === "content_delta" — streams assistant text to stdout
  • step.done — logs outcome: succeeded, retry_step, or fail_run
  • run.tool.invoked — logs tool_name and tool_outcome
  • run.awaiting_input — prompts for an action (unless DEMO_MODE) then calls sendSignal
  • Terminal types close the stream via AbortController
Handlers are serialized on a promise chain so stdin prompts do not race concurrent SSE messages.

Handling awaiting_input (sendSignal)

POST /v1/runs/{id}/signal with body:
{
  "action": "approve",
  "idempotency_key": "<uuid>"
}
Actions must be exactly approve, reject, or submit_input. Optional payload is used for submit_input.

Querying usage (queryUsage)

After the run finishes, GET /v1/usage returns usage rows (event counts and total_quantity) and period bounds. Omitting start / end defaults to the current UTC month through now.

Polling fallback

The file includes a commented-out pollEvents function that uses GET /v1/runs/{id}/events?cursor= with a one-second interval. Uncomment it and call it from main() instead of streamEvents if SSE is blocked (corporate proxies, some serverless environments).

Customizing for production

  • Replace console.log with structured logging (include request_id, run.id, customer_id from your auth context where applicable).
  • Add cursor-based resume for SSE (pass cursor query param on the stream if you reconnect after disconnect).
  • Replace stdin prompts with a dashboard approval flow or webhook for awaiting_input.
  • Layer circuit breakers and tighter retry policies on top of apiRequest for your SLOs.
  • Persist run IDs and correlation IDs for audit and support.
  • Set workspace_id and subject_id on run creation for billing attribution (see Usage).