Skip to content

Zo API Reference ​

The extension talks to Zo's HTTP + SSE API at https://api.zo.computer. This is a distilled, reader-friendly version of the authoritative reference in extension/AGENTS.md.

The full OpenAPI spec is downloadable at https://www.zo.computer/docs-assets/openapi.json.

Base URL & authentication ​

  • Base URL: https://api.zo.computer
  • Auth: Authorization: Bearer <token> — tokens are zo_sk_... and are created at Zo → Settings → Advanced → Access Tokens.

Endpoints ​

POST /zo/ask ​

Send a message to Zo. Zo has full access to files, tools, and integrations — this is the "AI Brain" channel.

Request body:

FieldTypeRequiredDescription
inputstring✅Your message to Zo
conversation_idstring❌Continue an existing conversation thread
model_namestring❌Override model (see GET /models/available)
persona_idstring❌Override persona (see GET /personas/available)
output_formatobject❌JSON Schema for structured output — when set, output becomes an object instead of a string
streamboolean❌Enable SSE streaming. Defaults to false

Response (non-streaming):

json
{
  "output": "string | object",
  "conversation_id": "conv_..."
}

Streaming (stream: true) returns an SSE stream with Content-Type: text/event-stream; the conversation id is in the x-conversation-id response header. See Streaming for the event model.

GET /models/available ​

List the models you can use (includes BYOK configs). Requires auth.

Response:

json
{
  "models": [{
    "model_name": "anthropic:claude-haiku-4-5-20251001",
    "label": "Haiku 4.5",
    "vendor": "Anthropic",
    "description": "string | null",
    "type": "fast | capable | null",
    "context_window": 200000,
    "is_byok": false
  }]
}

GET /models/catalog ​

Full public model catalog. No auth required. Cached for 5 minutes.

Response extras: default_chat_model_id, featured_model_ids, featured_models_are_free, featured_model_labels, promo_end_date, deprecation_map.

GET /personas/available ​

List configured personas. Requires auth.

json
{
  "personas": [{
    "id": "a1b2c3d4",
    "name": "Technical Writer",
    "prompt": "System prompt text...",
    "model": "anthropic:claude-sonnet-4 | null",
    "image": "url | null"
  }]
}

MCP server — POST /mcp ​

Zo also exposes its full toolchain as an MCP server over streamable HTTP (live-verified 2026-08-18: server zo-tools v1.0.0, protocol 2024-11-05, 96 tools). The tools are not part of the REST OpenAPI — the MCP endpoint itself is their HTTP interface.

  • Same base URL + Bearer token as the REST API: POST https://api.zo.computer/mcp
  • Handshake: initialize (the response carries an mcp-session-id header) → notifications/initialized → tools/list / tools/call
  • Responses arrive as plain JSON or a single SSE data: frame — both carry the same JSON-RPC message
  • Tool groups: files (read_file, list_directory, grep_search, …), shell (bash), web (web_search, read_webpage, …), media, integrations (Gmail/Drive/Notion/…), automations/personas/rules, space routes, Stripe. There is no skills-listing tool — skills are folders under /home/workspace/Skills on the user's server, each with a SKILL.md (YAML frontmatter name: + description:).

The extension uses the MCP server for the composer reference pickers (guide): one bash round-trip enumerates the skills folder, ls -1F listings back the % file browser. Note the bash tool wraps stdout in a Python-repr CmdResult(...) with escaped newlines — extension/lib/pickers.js parses it between __ZO_BEGIN__/__ZO_END__ markers emitted by the extension's own commands.

The {reasoning, actions} envelope ​

Only the Co-browse mode requests structured actions. It asks Zo for a JSON array {"actions":[...]} in the prompt text (there is no output_format in the actual extension calls — Zo didn't support array types in the schema), then background.js parses it from the text response.

The action object may arrive in one of three shapes — all are normalized:

  • Key-first: {"click":{...}}
  • Type-first: {"type":"click",...}
  • Non-spec action variant: {"action":"click",...}

See Action Protocol.

SSE event types (live-captured) ​

These are the event types the live API actually emits (captured 2026-08-09). The older-documented FrontendModelResponse / End / Error events are never emitted by the live API — handlers remain only for synthetic test fixtures.

EventPurpose
PartStartEventStarts a content part. data: {event_kind:"part_start", index, part:{part_kind:"thinking|text|tool-call|tool-return", content:"<first piece>"|args}, previous_part_kind}. The first token must be routed by part_kind or it's lost
PartDeltaEventIncremental content delta (the workhorse). data: {event_kind:"part_delta", index, delta:{content_delta:"<text>", part_delta_kind:"thinking|text"}}. Route on part_delta_kind: thinking = live reasoning (index 0), text = answer (index 1)
FunctionToolCallEventA tool was invoked. data: {event_kind:"function_tool_call", part:{tool_name, tool_call_id, args}} — surfaced as the "Explored" channel
FunctionToolResultEventA tool returned. data: {event_kind:"function_tool_result", result:{content:{stdout,stderr,returncode}|string, outcome:"success"|"error", tool_call_id, tool_name}}
AgentRuntimeStreamChunkLifecycle metadata. data: {type:"status"|"persisted", status, data:{message_id}} — not rendered
completedTerminal signal. `data: {status:"succeeded"

Official features the extension doesn't (yet) use ​

FeatureOfficial supportExtension statePriority
output_formatFirst-class JSON SchemaPrompt-based JSON parse (fragile)P0
stream: trueSSE typed eventsUsed — primary pathP1 (done)
GET /models/catalog (no-auth)Public catalogOnly /models/available is usedP2
featured_models_are_freeFree-model flagNot checkedLow
deprecation_mapActive→successor mappingNot usedLow
Persona model overridePersona can set own modelNot honoredP3

Known gotchas ​

  • output_format doesn't support array property types — the extension prompts for JSON and parses from text, so the model sometimes returns plain text and the panel handles both.
  • Host permissions must include <all_urls> for scripting.executeScript to work on arbitrary pages — otherwise context capture fails silently.
  • Config lives in chrome.storage.sync, history in chrome.storage.local (capped at 50 entries).

Released under the MIT License.