epho spins up claude code, codex, or opencode in the cloud — your repos cloned, your files attached, your mcp servers wired in. you POST a prompt, the work streams back. no sdk, no daemon, no infra on your side.
no provider key handy? the opencode/…-free models run without one.
$ epho explain --how-it-works
one request boots a fresh cloud environment with the harness you picked, clones your repos into it, connects your tools, hands it your prompt, and pipes everything back as it happens.
┌───────┐ │ you │ └───────┘
your script
POST /api/v1/chat
┌────────┐ │ epho │ └────────┘
boots & routes
runs
┌─────────────┐ │ claude code │ ├─────────────┤ │ codex │ ├─────────────┤ │ opencode │ └─────────────┘
in a cloud box
text/event-stream — tokens, tool calls, diffs
01
one endpoint, one json body. pick a harness and model, write a prompt, pass your provider_api_key.
02
a sandbox boots with the harness installed, your repos cloned, your input_files staged and your mcp_servers connected.
03
sse streams back the whole run — tool calls, edits, the final answer. or POST to /chat/async and pick the result up later.
$ epho chat --resume
a chat is a durable conversation with a sandbox attached. the box stops when a turn ends and resumes for the next one — same filesystem, same checkout, same agent session. hand back the chat_id and keep going.
# turn 1 — no chat_id, so this creates one{"harness": "claude","model": "claude-opus-5","prompt": "Add a /health endpoint.","repos": [{ "url": "…/acme/api", "token": "ghp_…" }]}← {"type":"chat","chat_id":"01K7QF…","turn_id":"01K7RD…"}# turn 2 — same box, still warm, still knows what it did{"chat_id": "01K7QF…","prompt": "Now add a readiness probe next to it."}← {"type":"chat","chat_id":"01K7QF…","turn_id":"01K7SB…"}
set once
harness · model · effort · system_prompt · instance · mcp_servers · env — configured when the chat is born, inherited by every turn after
sent every turn
prompt · provider_api_key · repos · input_files — free to change mid-conversation. rotate a repo token, attach a new file, point at a different branch
if the box is gone
a replacement sandbox boots and the agent's session snapshot is restored — the conversation outlives the machine it ran on
$ epho context --what-goes-in
a prompt on its own is a party trick. the code, the files, the tools and the secrets all ride on the same request — this is every field that carries them.
POST /api/v1/chat{"prompt": "Reconcile q3.csv against the ledger, file what’s off.",↳ the only thing you actually have to write"repos": [{"url": "…/acme/ledger", "branch": "main", "token": "ghp_…"}],↳ up to 32, cloned before the agent starts · private ones take a token"input_files": [{"name": "q3.csv", "url": "https://…"}],↳ up to 20, base64 or url · lands in /tmp/inputs, path appended to the prompt"mcp_servers": [{"name": "linear", "command": "npx", "args": […]}],↳ a local command or a remote url · set once, inherited by every turn"env": {"DATABASE_URL": "postgres://…"},↳ baked into the sandbox at creation"system_prompt": "Be meticulous. Never force-push.",↳ stacked on top of the harness’s own system prompt"effort": "high"↳ low · medium · high · xhigh, where the harness supports it}
$ epho features --why
the whole product is one endpoint and good defaults. everything below falls out of that.
$ epho compare raw-sandbox
you can absolutely run claude code yourself on a raw sandbox — e2b, daytona, a vm you rent. people do. here's what you sign up for.
what you start with
raw sandboxan empty linux box
ephoa running agent — harness installed, warmed up
setup
raw sandboxinstall the harness, wire auth, babysit the process
ephoone POST. that's the setup
a dead box mid-run
raw sandboxthe run dies with it. you notice, you retry
ephothe run re-queues on a fallback sandbox backend
your provider key
raw sandboxbaked into the box's env
ephorides along per-request, torn down with the run
a follow-up task
raw sandboxkeep the box alive yourself, or lose the context
ephopass chat_id — same fs, same agent session
tools and secrets
raw sandboxinstall mcp servers, ship env vars, redo it each boot
ephodeclare them once — every turn inherits them
getting files back
raw sandboxscp them out before the box dies
ephoartifacts arrive on the done event, presigned
ten agents at once
raw sandboxyou build the pool, the queue, the retries
ephofire ten requests
need full machine control? use a sandbox. need work done? use epho.
$ epho pricing --per-second
you pay for the sandbox resources your run uses. the meter runs from boot to teardown and stops when the run does. nothing idles, nothing is stored, nothing keeps billing.
cpu
$0.0000164
per vCPU · per second
memory
$0.0000053
per GiB · per second
disk
$0.000000036
per GiB · per second
default instance: 2 vCPU · 2 GiB · 10 GiB ≈ $0.0000438/s · a one-minute run ≈ $0.0026
starting credit: $10 on signup — roughly 60 hours of the default instance
model tokens: never billed by epho. they ride on your provider_api_key, at your provider's price.
the meter: read it any time with GET /credits — a turn is refused with 402 once the balance hits zero.
$ epho api --spec
copy it, swap in your key, change the prompt. you're now running coding agents in the cloud. everything after this is optional.
$ curl -N https://app.epho.io/api/v1/chat \-H "Authorization: Bearer sk_live_••••••••" \-H "Content-Type: application/json" \-d '{"harness": "claude","model": "claude-sonnet-5","prompt": "Fix the flaky test in auth_test.go.","provider_api_key": "sk-ant-••••••••","repos": [{"url": "https://github.com/acme/api"}]}'# response — text/event-streamdata: {"type":"chat","chat_id":"01K7QF…","turn_id":"01K7RD…"}data: {"type":"event","event":{"type":"status","phase":"provisioning"}}data: {"type":"event","event":{"type":"status","phase":"starting"}}data: {"type":"event","event":{"type":"text","text":"patched race in token refresh"}}data: {"type":"done","status":"completed","output":"…","artifacts":[]}
harness
"claude" | "codex" | "opencode" — which agent boots
model
string — a model that harness supports. bare id for claude and codex, provider/model for opencode
prompt
string — what you want done, in plain words
provider_api_key
string — your key for the model behind the harness. not needed for opencode/…-free
$ epho examples --build
hand one of these to your own coding agent. each is a real app built on the endpoint above, small enough to finish in an afternoon.
pr reviewer
List a repo’s open pull requests. A review button streams an epho agent’s read of the diff into a side panel — diff passed in as an input file, repo cloned at the base branch. Reviews run concurrently.
issue → pr
Turn any github issue labelled agent into a pull request. Fire the run with POST /chat/async and comment the result back on the issue when the webhook lands.
csv → chart
Drop a csv in the browser and hand it to an epho agent as an input file. Render whatever chart it writes back as an artifact, and show the agent’s reasoning as it streams.
slack teammate
A slack bot with one epho chat per thread. Stream the agent’s work into the thread as it happens, and reuse the chat_id so follow-ups land in the same warm sandbox.
every copy ships with a pointer to https://epho.io/docs/api and a reminder to keep your key server-side — so your agent reads the spec before it writes anything.
$ epho faq
you don't need one — it's a single POST. if you really want typed helpers, paste the curl into your favorite llm and ask. it'll manage.
you do. the request carries your provider_api_key, so tokens land on your provider bill at your provider's price. no markup, no middleman.
sandbox time is metered from boot to teardown. size the box yourself with the instance key. every run reports its own cost_usd in the final event.
whatever the harness does on your machine: read repos, write code, run tests, iterate on failures. it just does it in the cloud, on demand, in parallel.
yes — each chat gets its own isolated environment, so fan out as wide as you like. teams start with five active turns; anything past the cap sits in a queue, emits a queued status and costs nothing until a slot opens. one live turn per chat, though — parallel work wants parallel chats.
that's what a chat is. pass the chat_id back with a new prompt and the sandbox resumes — same filesystem, same checkout, same agent session. if the underlying box is gone, epho boots a replacement and restores the session snapshot, so the conversation survives the machine.
pass up to 32 entries in repos, each with an optional branch and a per-repo token. github, gitlab and bitbucket tokens are all handled. repos are per-turn, so you can rotate a token or add a new repo halfway through a conversation.
yes — mcp_servers takes either a local stdio process (command + args) or a remote server (url + headers). linear, figma, your own internal server. they're set when the chat is created and every later turn inherits them.
no. POST to /chat/async and you get 202 with the ids immediately, then collect the result from /chat/{id}/response or set a webhook_url. even on the streaming endpoint a dropped pipe doesn't kill the run — reconnect to /events/subscribe and it replays what you missed.
both. input_files puts up to 20 files in the sandbox — inline base64 or a url we fetch — and appends their paths to your prompt. anything the agent produces comes back as artifacts with a presigned download url, good for about ten minutes and re-mintable from the response endpoint.
yes — the opencode zen models ending in -free run without one. pick "harness": "opencode" and a -free model, drop provider_api_key entirely, and your epho key is the only credential in the request.
it's used for that run and nothing else. you re-send it on every turn, including resumes, and the sandbox stops the moment the turn ends.
something not covered here? [email protected]
two minutes from now you could be watching an agent work from your terminal.