# Market Watcher — API Base URL: `http://market-watcher.agents.mycelen.com` A live HTTP API for the **Market Watcher** agent, built on Irene. Every path is relative to the base URL. ## Auth - **key mode** — send the deploy key in the `X-Irene-Key` header. Add an optional `X-Irene-End-User` header to run many of *your* downstream users on one key (each gets a private history + workspace). - **users mode** — each end-user signs in (`POST /auth/start` → emailed code → `POST /auth/verify`), then mints a stable personal key (`iru_…`) and sends it as `X-Irene-Key`. Calls then run **as that user**, with their own connected accounts. - **open mode** — no key; anonymous callers are read-only. ## Sessions & memory A session is one conversation — its chat history **and** file workspace persist across turns. Get the id from the first `/stream` frame (or `/run`'s `session_id`) and pass it back in the next request to continue. ## Approvals (HIL) Dangerous tools pause for approval. On `/stream` you get a `paused` event → resolve via `POST /resume` (on a second connection). On `/run` they auto-allow / park-for-owner / deny by policy. **To skip approvals**, an authenticated non-visitor caller can send `approve_all: true` (or `approve_tools: [names]`) on `/run`/`/stream` — gated tools then run with no pause. (The owner can disable this per-agent; it never bypasses a `needs_connect`.) ## Per-user accounts (users mode) The agent acts as each user's OWN connected account. If a tool needs one the user hasn't connected, the stream emits `needs_connect` and the run ends — connect via `POST /me/connect/{kit}/start`, then re-send. ## Endpoints ### POST /run Send one message, get the full reply when it's done. Pass `session_id` back to continue a conversation (history + file workspace persist). Set `approve_all` to auto-approve every gated tool for this turn (or `approve_tools` for a subset). **Body** - `message` (string) *(required)* — What you want the agent to do. - `session_id` (string) — Continue a prior session. Omit to start fresh. - `approve_all` (boolean) — Auto-approve every gated tool this turn (skip HIL). - `approve_tools` (array) — Auto-approve only these tool names; others still pause. ```bash curl -X POST http://market-watcher.agents.mycelen.com/run \ -H 'Content-Type: application/json' \ -d '{"message": "find one popular post about python and give me the title", "approve_all": true}' ``` Response: ```json { "answer": "Here's a popular post: \"Is python still okay today?\"", "session_id": "f4842fc8\u2026", "usage": { "input": 1200, "output": 90 }, "awaiting_approval": [], "denied": [] } ``` ### POST /stream Same body as `/run`, but streams Server-Sent Events — each line is `data: ` and the stream ends with `data: [DONE]`. Append `content` chunks for prose, render `tool_call_*` for a live activity view, and on a `paused` event POST `/resume`. Event types: session · content · reasoning · tool_call_started · tool_call_completed · paused · resumed · needs_connect · done · error · partial_error · cancelled. **Body** - `message` (string) *(required)* — What you want the agent to do. - `session_id` (string) — Continue a prior session. - `approve_all` (boolean) — Auto-approve every gated tool this turn. - `approve_tools` (array) — Auto-approve only these tool names. ```bash curl -X POST http://market-watcher.agents.mycelen.com/stream \ -H 'Content-Type: application/json' \ -d '{"message": "draft a reply to my latest comment", "approve_all": true}' ``` Response (SSE): ``` data: {"type":"session","data":{"session_id":"f48…"}} data: {"type":"content","data":{"text":"Sure — "}} data: {"type":"done","data":{}} data: [DONE] ``` ### POST /resume When `/stream` emits a `paused` event the run blocks until you resolve it. POST this from a SECOND connection while the stream stays open. You may only resolve your own session's pauses. **Body** - `session_id` (string) *(required)* — From the paused event. - `tool_call_id` (string) *(required)* — From the paused event. - `decision` (string) *(required)* — allow | allow_for_session | answer | deny - `note` (string) — Optional note (allow). - `payload` (object) — Answer payload (decision=answer). - `reason` (string) — Reason (deny). ```bash curl -X POST http://market-watcher.agents.mycelen.com/resume \ -H 'Content-Type: application/json' \ -d '{"session_id": "f48\u2026", "tool_call_id": "tc_9", "decision": "allow"}' ``` Response: ```json { "ok": true, "decision": "allow" } ``` ### POST /nudge Inject a message into a turn that's already running (it folds into the live run). **Body** - `session_id` (string) *(required)* — The running session. - `text` (string) *(required)* — The nudge message. ```bash curl -X POST http://market-watcher.agents.mycelen.com/nudge \ -H 'Content-Type: application/json' \ -d '{"session_id": "f48\u2026", "text": "actually, keep it under 50 words"}' ``` Response: ```json { "ok": true } ``` ### GET /tools The agent's tools and which are `gated` (need approval) — so you know what `approve_all` would auto-run. ```bash curl http://market-watcher.agents.mycelen.com/tools \ -H 'Accept: application/json' ``` Response: ```json { "tools": [ { "name": "reddit_search_across_subreddits", "description": "Search Reddit across subreddits.", "gated": true } ] } ``` ### GET /sessions Every session belongs to the calling identity. Also: POST /sessions/new, DELETE /sessions/{id}, GET /sessions/{id}/messages (transcript), POST /sessions/{id}/compact, GET /sessions/{id}/context. ```bash curl http://market-watcher.agents.mycelen.com/sessions \ -H 'Accept: application/json' ``` Response: ```json { "sessions": [ { "id": "f48\u2026", "name": "Reddit cleanup", "updated_at": 1719500000 } ] } ``` ### GET /sessions/{sid}/messages The full message + tool-call history for one session. - `sid` (path) — Session id (path). ```bash curl http://market-watcher.agents.mycelen.com/sessions/SID/messages \ -H 'Accept: application/json' ``` Response: ```json { "messages": [ { "role": "user", "text": "hi" }, { "role": "assistant", "text": "hey" } ] } ``` ### GET /files Files the agent produced in a session's workspace. Download one with GET /files/raw?session_id=…&path=…. Workspaces persist across the caller's sessions. - `session_id` (query) — Session id. ```bash curl http://market-watcher.agents.mycelen.com/files?session_id=VALUE \ -H 'Accept: application/json' ``` Response: ```json { "files": [ { "path": "chart.png", "mtime": 1719500000 } ] } ``` ### POST /files Upload a file INTO a session's workspace (multipart/form-data, field `file`) — the agent then reads it with its file tools. 25 MB max (over → 413); a same-named file is auto-suffixed. Uploading to a session you don't own → 404. Do NOT set `Content-Type` yourself — let the client set the multipart boundary. **Body** (`multipart/form-data`) - `file` (file) *(required)* — The file to upload (binary). - `session_id` (query) — Session id. ```bash curl -X POST http://market-watcher.agents.mycelen.com/files?session_id=VALUE \ -F 'file=@/path/to/file' ``` Response: ```json { "session_id": "f48\u2026", "path": "report.pdf", "name": "report.pdf", "size": 20841 } ``` ### GET /approvals OWNER only: tool approvals parked by autonomous /run turns. ?status=all|pending|allowed|denied. - `status` (query) — Filter (default pending). ```bash curl http://market-watcher.agents.mycelen.com/approvals?status=VALUE \ -H 'Accept: application/json' ``` Response: ```json { "approvals": [ { "tool_name": "reddit_create_reddit_post", "status": "pending", "session_id": "f48\u2026", "created_at": "2026-06-28T\u2026" } ] } ``` ### GET /health Liveness + the agent's name and auth mode. No auth. ```bash curl http://market-watcher.agents.mycelen.com/health \ -H 'Accept: application/json' ``` Response: ```json { "ok": true, "agent": "reddit_multitenant", "name": "Reddit Multitenant", "auth_mode": "users" } ``` Full machine-readable spec: `GET /openapi.json`.