Running agents

Choose streaming or asynchronous execution and consume events and results.

Epho provides two submission modes. They differ only in how the client waits; both create the same chat and turn records and use the same worker-backed launch process.

Synchronous streaming

POST /api/v1/chat returns text/event-stream and keeps the request open while the turn runs.

Frame types:

TypePurpose
chatSent first with chat_id and turn_id
eventWraps one persisted lifecycle or agent event
doneSent last with status, output, error, and artifacts
data: {"type":"chat","chat_id":"01K...","turn_id":"01K..."}

data: {"type":"event","event":{"type":"status","phase":"starting","message":"Starting the agent…"}}

data: {"type":"done","status":"completed","output":"Done.","error":null,"artifacts":[]}

Epho also emits SSE keepalive comments during quiet periods so intermediaries do not close the connection.

Set cancel_on_disconnect to stop the turn if this streaming client disappears:

{
  "prompt": "Run the full integration suite.",
  "harness": "codex",
  "model": "gpt-5.5",
  "provider_api_key": "sk-proj-...",
  "cancel_on_disconnect": true
}

It defaults to false, allowing the worker to finish and persist the result after a dropped connection.

Asynchronous execution

POST /api/v1/chat/async queues the turn and returns 202 with its identifiers. Use this mode for background jobs, automation, and any caller that should not hold an HTTP connection.

{
  "chat_id": "01K...",
  "turn_id": "01K..."
}

Reconnectable streaming

Subscribe after an asynchronous submission—or reconnect after losing a stream:

curl -N https://app.epho.io/api/v1/chat/$CHAT_ID/events/subscribe \
  -H "Authorization: Bearer $EPHO_API_KEY"

The subscription:

  1. emits the chat and latest turn identifiers;
  2. replays persisted events from earlier turns;
  3. live-tails the newest turn;
  4. closes with a done frame.

Reading persisted state

Use the chat-level endpoints to read the whole conversation:

GET /api/v1/chat/{chat_id}/events
GET /api/v1/chat/{chat_id}/response

Use the turn-level endpoints when your application already has a turn_id:

GET /api/v1/chat/{chat_id}/turns/{turn_id}/events
GET /api/v1/chat/{chat_id}/turns/{turn_id}/response

An in-flight response has status: "processing" and output: null.

Stopping a run

curl -X POST \
  https://app.epho.io/api/v1/chat/$CHAT_ID/turns/$TURN_ID/stop \
  -H "Authorization: Bearer $EPHO_API_KEY"

The endpoint returns 202 immediately. The worker marks the turn canceled and tears down its sandbox.