The app
Quick start
Three steps. The second is the only one with a choice in it.
-
1
Download and open it
Grab the build for your platform from the download page, drag it into Applications on macOS, and open it. It is signed and notarized, so there is nothing to approve.
-
2
Give it a model
Open Settings → Model providers and paste a key, or sign in where the provider allows it. Or open Models and download one to run on this machine, with nothing else to install. Keys and local models are covered below.
-
3
Ask, or hand it something
Type in the message box and send. Ask a question and it answers; ask for something to be done and it does it, in a folder you pick. See letting it do the work.
Install on macOS
- Open the
.dmg. - Drag Eaon into your Applications folder.
- Open it from Applications.
Requires macOS 12 or later. The build is Universal, so Apple silicon and Intel each run their own native slice.
Opening it the first time
Nothing to do. Apple signs and notarizes Eaon, and the notarization ticket ships stapled inside the download. Drag it to Applications and double-click: no right-click trick, no Terminal command, nothing to approve under Privacy & Security.
Updates
Eaon checks for a new version, downloads it in the background, and tells you when it is ready. Restart when it suits you; settings and chats stay as they are. Mac copies of 2026.5 and of the older 2026.4 app update to 2026.6 on their own. To try new versions early, turn on Install beta updates in Settings → General; a beta is only downloaded when you press its button, and Switch to stable goes back.
Coming from the old Windows or Linux app
Windows and Linux copies of the old cross-platform app (2026.4 and earlier) cannot update into this one. Download the installer instead.
Windows and Linux
Every platform ships from the same release, at 2026.6.2.
Windows
Run Eaon-2026.6.2-setup.exe. One installer covers x64 and ARM. It is not
code-signed yet, so SmartScreen shows a blue "Windows protected your
PC" box on first run: click More info, then Run anyway. That is
expected, not a sign anything is wrong.
Linux
Take the AppImage for your machine, Eaon-2026.6.2.AppImage for x64 or
Eaon-2026.6.2-arm64.AppImage for arm64, make it executable and run it.
Nothing else needs installing first, Ubuntu 24.04 included.
chmod +x Eaon-2026.6.2.AppImage ./Eaon-2026.6.2.AppImage
On Debian or Ubuntu you can install the .deb instead:
sudo apt install ./eaon-desktop_2026.6.2_amd64.deb
Chat, Workers and the ADE
Three tabs sit at the top of every screen.
- Chat (⌘1) answers questions and does the work you hand it.
- Workers (⌘2) are always-on agents that keep going in the background.
- ADE (⌘3) is where coding agents work side by side, each in a real terminal, in sessions on branches of their own. It’s part of Eaon, in the same window as the other two.
Picking a model
The model chip sits in the message box, beside the mic and Send. Click it for the picker: a tab for each provider you have linked, plus a Starred tab, a search, and the models themselves. ⌘1 to ⌘9 picks a model in view.
- Switch any time. Pick another model mid-conversation and the next reply comes from it, with the thread behind it.
- Star the models you use most and they get a tab of their own.
- Effort is the slider at the bottom of the picker, from Low to Max. It only stops on the levels the current model accepts.
- Link accounts with the + at the end of the tabs.
- Providers on and off. Each provider has its own switch under Settings → Model providers.
Keys and sign-ins
- Open Settings → Model providers.
- Pick a provider and paste its key, or press Sign in where it has one.
- Its models appear in the model list. Lists come from provider data and refresh daily.
There are 55 providers. ChatGPT, Hugging Face and Poe let you sign in instead of using a key. The + button adds any other OpenAI-compatible endpoint by its base URL, and each provider can hold fallback keys, which Eaon tries in order when the main one fails. Removing a model hides it, so it can be restored.
Keys are encrypted with your system keychain before they are written to disk, in the app’s own folder. Eaon sends a key only to its own provider, on a request you made.
Running models locally
Eaon runs local models on its own llama.cpp. Open Models: the Library lists open models, recommended ones first, each with a badge for how well it fits this machine’s memory. Press Get and it downloads from Hugging Face, after checking your free disk space, and loads the first time you use it. Browse Hugging Face finds any other GGUF, and Downloaded lists what you have.
Already run a local server? Eaon finds its models on its own, at these addresses:
- Ollama on
127.0.0.1:11434 - LM Studio on
127.0.0.1:1234 - llama.cpp (llama-server) and MLX (mlx_lm.server) on
127.0.0.1:8080 - vLLM on
127.0.0.1:8000, and Jan on127.0.0.1:1337
With a local model and web search off, the prompt and the reply stay on the machine. Web search starts on, at Snippets: set it to Off under Settings → Configuration.
Letting it do the work
Chat is an agent. Ask for something and it does it, in a folder: press +
→ Work in a folder to pick one, or leave it on ~/Eaon. It
reads, edits and writes files, runs commands with live output, starts background
servers and deletes to the Trash. Changes outside the folder ask first.
How much it asks is under + → Permissions:
- Ask first, the default, stops before every action.
- Auto-approve only asks before actions that look unsafe.
- Full autonomy runs any command and change on its own, and still stops for anything that can’t be undone: sudo, erasing a disk, force-pushing, passwords and payments.
When it asks, a card says what is about to happen and shows it, red for anything that deletes things or reads credentials. ⏎ approves and esc denies.
Plan, Swarm and Goal
- Plan first researches read-only and asks you to approve a plan, then works through a checklist.
- Swarm splits the work across two to six sub-agents.
- Goal keeps going until the goal is checked done or blocked, for up to 8 hours or until a time you pick, keeping the computer awake meanwhile.
- Code index (Settings → Code index) lets it search your project by meaning, not only by keyword. The index stays on this machine.
Workers
Open the Workers tab and press New worker. Give it a name, a colour, a personality and a job. A worker keeps one thread that never ends, and wakes when you write, when another worker sends it mail, or on a heartbeat it schedules itself.
- Freedom. Autonomous (the default), Careful or Look only. An autonomous worker still asks before it spends money, types a card number or password, uses sudo, erases a disk or force-pushes.
- Routines. Named, timed jobs such as “daily at 08:30, write the morning brief”.
- Questions it asks don’t block it, and it notifies you when it has news.
- Teams. Press New team, or ask Chat for one. Up to four workers run at the same time in a group chat: your posts reach all of them (or only the ones you @mention), and a worker’s post wakes only the colleagues it @mentions.
- Chat apps. Under Settings → Chat apps, pair a worker with Discord, Telegram or WhatsApp using a code, then talk to it from your phone.
/status,/stop,/wakeand/approvecontrol it. WhatsApp uses an unofficial client, so a spare number is safest. - Email. A worker can have its own address, and always asks you before sending.
Browser, computer, iPhone, voice and payments
- Eaon’s browser. Switch it on under + → Browser. It opens in a panel beside the chat (⇧⌘B), separate from your Chrome. Press Take control to sign it in, then hand it back.
- Your own browser. Settings → Browser control sets up Browser Use in one click, with no extension. Your browser lets Eaon in through its own “Allow remote debugging” switch and asks you to allow each connection. Chrome, Edge, Brave, Arc, Comet, Vivaldi and Chromium work. The agent uses a tab of its own, and buying, sending, and card or password fields still ask first. The old Eaon extension isn’t used any more; you can remove it.
- Computer use. Settings → Computer use walks you through the macOS permissions. Then Chat and workers can use your apps. It runs on Cua Driver, which works through each app’s accessibility (the real buttons, fields and menus) and mostly without taking over your mouse; Settings → Computer use shows which engine is in use. One run holds the pointer at a time, an indicator says who, and ⌃⌥⌘. stops it.
- Your iPhone. It can boot and drive the iOS Simulator, on screen so you can watch, or use your real phone through iPhone Mirroring.
- Payments. Settings → Payments holds a card it can pay with. Approve every purchase, or let it pay on its own once you have read and accepted the waiver.
- Connect apps. Settings → Connect apps lets other AI apps on your computer use Eaon’s models through its gateway. Settings → Usage shows requests, tokens and estimated cost per model, day by day.
- Voice. Press the mic in the message box, talk, and press the tick. The words land in the box; nothing is sent until you send it. It needs an OpenAI or Groq key.
- Images. Ask for one. It uses your OpenAI or Gemini key and saves the image in the work folder.
Search and shortcuts
The search button at the top of the sidebar finds a chat by its title or by anything
said in it. In the message box, / lists everything the + menu does plus
your skills, and @ switches on a plugin.
| Keys | Does |
|---|---|
| ⌘1 ⌘2 ⌘3 | Chat, Workers, ADE |
| ⌘N | New chat |
| ⇧⌘N | New temporary chat |
| ⌥⌘N | New window |
| ⇧⌘A | Archive the chat |
| ⌘, | Settings |
| ⌘B | Show or hide the sidebar |
| ⇧⌘B | Show or hide the browser panel |
| ⌘⇧P | Plugins |
| ↵ | Send |
| ⇧↵ | New line |
Chats and history
- Where it lives. Every conversation is saved in the app’s own folder on this machine. There is no Eaon account and no cloud copy.
- Pin the chats you keep coming back to. Pinned ones sort to the top of Recents.
- Rename, archive or delete from a chat’s right-click menu. Archived chats can be restored.
- Under a reply: try again, fork the chat from there, reply with a quote, or read it aloud. Copy transcript is in the chat’s … menu.
- Projects group chats with their own instructions, and the Library holds every file you have attached.
Opening the ADE
The ADE is Eaon’s third tab: press ⌘3, or pick ADE at the top of the window. It’s an agentic development environment, a place where coding agents work side by side, each in a real terminal on your login shell. There’s nothing extra to install: it comes with Eaon on macOS, Windows and Linux.
Its sidebar lists your projects and their sessions, plus New session, Pull requests and Linear. Open a folder and it becomes a project, with the folder itself as its first session.
Sessions and branches
A session is one piece of work, in a folder of its own. New session asks what
you’re working on and which agent to start with, makes a branch from that (you can
edit it), and checks it out in a git worktree
under ~/Eaon/worktrees, so your project folder isn’t touched and two
agents never edit the same files. A name that starts with Fix gets a
fix/ branch (“Fix CI checks detail link” becomes
fix/ci-checks-detail-link); anything else gets feature/.
Each session in the sidebar shows its branch and one of three states:
- Working: an agent in it is busy.
- Waiting for you: its terminals are open and nothing is running.
- Nothing running: no terminal is open in it.
An empty session can reopen a past Claude Code or Codex conversation from its folder (Reopen a past conversation…). Settings → ADE → Import sessions finds the folders those CLIs already ran in and adds a session for each; nothing in either CLI changes.
Removing a session closes its terminals, and can remove its worktree too. The branch always stays, and git’s refusal to delete uncommitted work is respected.
Agents and the grid
A session’s terminals sit in a grid. Each one runs one of these:
- Claude Code, Codex, OpenCode and Antigravity, the CLIs you install yourself, signed in the way you signed them in.
- Eaon Code, which Settings → Eaon Code installs and updates for you.
- Eaon CLI, a coding agent that uses only the open-source models you’ve downloaded in Eaon. Apple silicon only for now.
- A plain shell.
An agent that isn’t installed is greyed out in the New terminal menu; hover it to see how to get it. There’s no limit on terminals: the grid goes up to four columns wide and adds rows.
- Resize: drag the line between two terminals, or focus it and use the arrow keys. Double-click it to even them out. Sizes are kept per folder.
- Rearrange: drag a terminal by its title onto another to swap them, or use Move left and Move right in its menu.
- Fill the grid: double-click a title. Double-click again to go back.
- Drop files on a terminal and their paths are typed in, quoted where needed. Claude Code attaches a dropped image.
A terminal’s menu also has Rename, Restart, Clear and Close.
Pull requests and Linear
Reviewing a pull request
The Pull requests page lists the pull requests you opened and the ones waiting
for your review. It uses the GitHub CLI, so gh needs to be installed and signed in. Pick one and press
Review: it’s checked out in a session of its own, and Claude Code (or Codex)
reads the change without running it. Its review shows on the page as a summary, a
verdict and line comments. Edit them, then post the review to GitHub as a comment, an
approval or a request for changes. Nothing is posted until you choose to.
Starting a Linear issue
The Linear page lists the open issues assigned to you. Connect it with a Linear API key, which is kept in Eaon’s encrypted vault. Start makes a session on the branch Linear names for the issue, gives Claude Code (or Codex) the issue as its task, and moves the issue to In Progress. When that branch’s pull request opens, it’s linked on the issue and the issue moves to In Review.
Accounts, usage and themes
- Accounts. Settings → Accounts lists the accounts Claude Code and Codex can run as, adds more (each signs in with the CLI’s own login, in a folder of its own), and picks the one the ADE’s terminals use. Your Claude plan’s limits keep working in Claude Code here.
- Usage. The meter in the ADE’s top bar shows how much of the plan is used this session and this week. Open it for every limit and when each resets.
- Themes. Type
/themein any terminal, or go to Settings → ADE → Terminal theme. There are twenty-seven, from Everforest and Nord to Gruvbox Light, and most draw a scene behind the text; a switch there turns scenes off. Each theme is previewed on every terminal before you keep it, and restyles the app to match. Eaon puts back the app’s own look.
ADE troubleshooting
An agent is greyed out
Eaon looks for each CLI on your login shell’s PATH. Install it, or fix your PATH, then open a new terminal. Hover the greyed-out entry for how to install it.
A terminal says macOS keeps Eaon out of a folder
When Eaon isn’t allowed into Downloads, Documents or the Desktop, nothing it starts there can read the folder. The terminal says which switch to turn on; press Open Privacy Settings and allow Eaon under Privacy & Security → Files and Folders.
Pull requests are empty
The page reads them with the GitHub CLI. Run gh auth status in a terminal;
if it isn’t signed in, run gh auth login.
Coming from Eaon ADE
Eaon ADE, the separate app, is retired. Its last version, 1.1.5, stays on GitHub but won’t be updated. The ADE in Eaon replaces it: download Eaon and open the ADE tab.
What’s different: your work is in sessions on their own branches rather than workspaces of panes, and pull request review, Linear and Claude Code and Codex accounts are new. The Conductor, the Board, voice dictation and the side browser didn’t come across.
Terminal
Eaon CLI
An agentic coding agent in your terminal: plan mode, sub-agents and real tools, for any model, local or hosted. Nothing to install if you have Node.
# run it without installing npx eaon-cli # or install it globally. Note the command is `eaon`, not `eaon-cli` npm install -g eaon-cli eaon
- Package
eaon-clion npm- Command
eaon- Requires
- Node 18.17 or newer
- Licence
- MIT
On first run it asks you to set up a key, opening a local page in your browser where you can paste an Eaon key, add your own provider keys, or import what Eaon Desktop already has. Press Esc to skip that and use a local Ollama model instead.
It needs an interactive terminal. For scripts and CI, use -p with
--auto, which is the only non-interactive form it accepts.
eaon -p "summarise what changed in the last commit" --auto
CLI commands
Type these at the prompt. Type an unrecognised /name and it goes to the
model as ordinary text instead of raising an error.
| Command | Does |
|---|---|
| /help | Every command and shortcut |
| /model [name] | Switch model; no argument opens a picker |
| /models | List hosted, your-own-key and local models |
| /pull <name> | Download an Ollama model |
| /permission [plan|sandboxed|auto] | Change what it may do without asking. Alias /perm |
| /plan | Jump straight to plan mode |
| /link | Set keys and providers in the browser |
| /diff | Show uncommitted git changes |
| /rewind [id] | Restore an earlier point; no argument lists them |
| /resume [id] | Reopen a session. Alias /sessions |
| /clear, /new | Start a fresh session |
| /compact | Summarise the conversation to reclaim context |
| /context | What is currently in context |
| /cost | Token and cost estimate for this session |
| /status | Model, mode and session state. Alias /stats |
| /init | Write an EAON.md for this project |
| /memory | Open EAON.md |
| /export [path] | Save the transcript as Markdown |
| /copy | Copy the last reply |
| /bashes | Background commands. Alias /jobs |
| /doctor | Check Node, Ollama, your key and the config file |
| /config | Config path and settings, keys redacted |
| /exit | Quit. Alias /quit |
Input prefixes
The CLI handles ! and # itself. Neither reaches the model,
and neither interrupts a turn in flight.
| Prefix | Does |
|---|---|
| !command | Run a shell command; the output joins the conversation |
| @path | Reference a file, with autocomplete. Its contents are included |
| #note | Append a note to the project's EAON.md |
| /name | A slash command, with autocomplete |
Keys
| Keys | Does |
|---|---|
| ⇧Tab | Cycle plan → sandboxed → auto |
| Esc | Interrupt the current turn |
| ↵ while working | Queue the message instead of interrupting |
| Tab | Accept the highlighted completion |
| ↑ ↓ | Command history |
| CtrlC twice | Exit |
\ then ↵ | Newline in the composer |
CLI flags
| Flag | Does |
|---|---|
| -v, --version | Print the version |
| -h, --help | Print usage |
| -p, --print <prompt> | One-shot, non-interactive. Reply on stdout, tool and status lines on stderr. Requires --auto |
| --model <key> | Start with a given model, e.g. ollama:deepseek-r1:7b |
| --auto | Start in auto permission mode, skipping confirmations |
| --permission-mode <mode> | plan, sandboxed or auto. Prefix-matched. Defaults to sandboxed |
| --cwd <path> | Project root. Defaults to the current directory |
| --max-steps <n> | Cap tool-call steps per turn. Default 40 |
| -c, --continue | Resume the most recent session in this project |
| -r, --resume <id> | Resume a specific session; an id prefix is enough |
| --welcome | Force the first-run screen |
There are no subcommands. A bare argument is ignored, so
eaon "fix the test" opens the interactive UI instead of running the
prompt. Use -p for that.
CLI configuration
Settings live in ~/.eaon/cli/config.json, which the CLI writes to a temp
file and renames, so a crash mid-save cannot corrupt it. Sessions sit in
~/.eaon/cli/sessions.
| Variable | Does |
|---|---|
| EAON_AQUA_API_KEY | Your Eaon API key. Takes precedence over the config file |
| EAON_OLLAMA_URL | Ollama base URL. Default http://127.0.0.1:11434 |
| EAON_CONFIG_DIR | Move the whole config, session and log directory |
| EAON_NO_BROWSER | Never launch a browser; print the URL instead |
API
The Eaon API
Eaon's gateway speaks the OpenAI chat-completions protocol, so anything that can talk to OpenAI can talk to Eaon by changing two settings: the base URL and the key.
- Base URL
https://api.eaon.dev/v1- Protocol
- OpenAI-compatible, JSON over HTTPS
- Auth
Authorization: Bearer sk-eaon-…- Body limit
- 1 MB
Every path also works under an /api prefix, so
/api/v1/chat/completions reaches the same handler as
/v1/chat/completions. Use whichever your proxy setup prefers.
Calling it from a browser
The gateway allows cross-origin requests from eaon.dev,
labs.eaon.dev, and localhost or 127.0.0.1 on
any port. It returns no permissive origin header to anything else, so a
fetch from another site fails in the browser. Server-side calls,
curl and native apps are unaffected. A public web page is the wrong
place to keep a key in the first place.
What it costs
Nothing. There is no paid tier and no card. The limits further down are about keeping a shared free service usable, not about upselling you.
Before you build against it
Model availability moves. Eaon routes to upstream providers, and which of them are
reachable changes, which means the set of servable model ids changes with it. Two
habits keep an integration alive through that: ask
/v1/models at runtime instead of hardcoding an
id, and treat model_not_found and
502 as ordinary conditions you handle. The endpoint contract holds
still. The catalog behind it moves.
Authentication
Send your key as a bearer token. Keys look like sk-eaon- followed by 32
hex characters.
Authorization: Bearer sk-eaon-0123456789abcdef0123456789abcdef
Getting a key
- Create an Eaon account and sign in.
- Redeem an invite code. Until you do, Eaon refuses to create one and answers
invite_required. - Create a key in the dashboard, or let the CLI's browser flow mint one for you.
When you do not need a key
The Instant tier is designed to need no key at all: no account, rate-limited by
IP, with auto as its model id. The protocol below is what an Instant
request looks like.
Two models serve it, and you can call either right now with no key:
| Model id | What it is |
|---|---|
| kimi-k2-thinking-extended | Kimi K2 Thinking, via Featherless |
| minimax-m2-extended | MiniMax M2, via Featherless |
Both think before they answer, so give them room. Each spends a couple of
hundred tokens on internal reasoning before writing a word, and that comes out of
your max_tokens. Ask for 20 and you will get an empty
content back, with the budget gone on reasoning you never see. A few
hundred is a sensible floor. The reasoning arrives in
reasoning_content when the provider sends it.
One provider serves both, and its plan allows four requests at once, so a busy
moment can answer 502 where a keyed request on another tier would fail
over to a second provider. Retry once before treating it as broken.
# No key, no account. Copy this and run it. curl https://api.eaon.dev/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"minimax-m2-extended", "messages":[{"role":"user","content":"Hello"}], "max_tokens":400}'
Everything above the Instant tier needs a key, and which models the gateway can serve shifts with provider availability. Read the live model list instead of hardcoding an id.
Chat completions
The main endpoint. Takes an OpenAI chat request, routes it to a provider that can serve the model, and fails over to another if the first one errors.
| Field | Type | Notes |
|---|---|---|
| messagesrequired | array | Standard OpenAI messages. Must not be empty. |
| modeloptional | string | Defaults to auto. Append :online to any id to turn on web search. |
| streamoptional | boolean | true streams the reply as Server-Sent Events. |
| web_searchoptional | boolean | Search the web first and add the results to the prompt. |
| anything elseoptional | any | Forwarded to the provider untouched: temperature, max_tokens, top_p, tools, response_format, stop, seed and so on. Whether a given provider honours one is up to that provider. |
Example
curl https://api.eaon.dev/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $EAON_API_KEY" \ -d '{ "model": "auto", "messages": [ {"role": "system", "content": "You are terse."}, {"role": "user", "content": "Why is the sky blue?"} ], "temperature": 0.7, "max_tokens": 300 }'
const res = await fetch("https://api.eaon.dev/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.EAON_API_KEY}`, }, body: JSON.stringify({ model: "auto", messages: [ { role: "system", content: "You are terse." }, { role: "user", content: "Why is the sky blue?" }, ], temperature: 0.7, max_tokens: 300, }), }); if (!res.ok) throw new Error(`Eaon ${res.status}`); const data = await res.json(); console.log(data.choices[0].message.content);
import os, json, urllib.request req = urllib.request.Request( "https://api.eaon.dev/v1/chat/completions", data=json.dumps({ "model": "auto", "messages": [ {"role": "system", "content": "You are terse."}, {"role": "user", "content": "Why is the sky blue?"}, ], "temperature": 0.7, "max_tokens": 300, }).encode(), headers={ "Content-Type": "application/json", "Authorization": f"Bearer {os.environ['EAON_API_KEY']}", }, ) with urllib.request.urlopen(req) as r: data = json.load(r) print(data["choices"][0]["message"]["content"])
Response
A standard OpenAI completion object, passed through from whichever provider answered,
plus one extra eaon object describing how it was routed.
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1770000000,
"model": "deepseek-v3.2",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Rayleigh scattering." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 18, "completion_tokens": 4, "total_tokens": 22 },
"eaon": {
"model": "deepseek-v3.2", // the id actually sent upstream
"attempted": 1, // how many providers were tried
"plan": "instant" // the plan this request was billed against
}
}
Eaon does not tell you which upstream served a request. With
model: "auto", eaon.model is how you find out which model
answered.
Streaming
Set "stream": true. The response is
text/event-stream: data: lines carrying OpenAI-shaped delta
chunks, ending with data: [DONE]. Eaon forwards the provider's stream
byte for byte, so an OpenAI client reads it without changes.
Token usage arrives as a final data: chunk when the provider supports
it. Eaon asks for it on your behalf, so you do not have to. Disconnect and the
upstream request is dropped too.
curl -N https://api.eaon.dev/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $EAON_API_KEY" \ -d '{"model":"auto","stream":true, "messages":[{"role":"user","content":"Count to five."}]}' # data: {"choices":[{"delta":{"content":"One"}}]} # data: {"choices":[{"delta":{"content":", two"}}]} # data: [DONE]
const res = await fetch("https://api.eaon.dev/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.EAON_API_KEY}`, }, body: JSON.stringify({ model: "auto", stream: true, messages: [{ role: "user", content: "Count to five." }], }), }); const reader = res.body.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // SSE events are separated by a blank line. let cut; while ((cut = buffer.indexOf("\n\n")) >= 0) { const event = buffer.slice(0, cut); buffer = buffer.slice(cut + 2); for (const line of event.split("\n")) { if (!line.startsWith("data:")) continue; const payload = line.slice(5).trim(); if (payload === "[DONE]") continue; try { const delta = JSON.parse(payload).choices?.[0]?.delta?.content; if (delta) process.stdout.write(delta); } catch { // a partial chunk; wait for more bytes } } } }
# pip install requests import json, os, requests with requests.post( "https://api.eaon.dev/v1/chat/completions", headers={"Authorization": f"Bearer {os.environ['EAON_API_KEY']}"}, json={ "model": "auto", "stream": True, "messages": [{"role": "user", "content": "Count to five."}], }, stream=True, ) as r: r.raise_for_status() for line in r.iter_lines(decode_unicode=True): if not line or not line.startswith("data:"): continue payload = line[5:].strip() if payload == "[DONE]": break try: delta = json.loads(payload)["choices"][0]["delta"] except (json.JSONDecodeError, KeyError, IndexError): continue if "content" in delta: print(delta["content"], end="", flush=True)
Using the OpenAI SDKs
Point the official client at Eaon's base URL. Nothing else changes: streaming, tool calls and async behave as they do against OpenAI.
// npm install openai import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.eaon.dev/v1", apiKey: process.env.EAON_API_KEY, }); const stream = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: "Explain a mutex in two sentences." }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); }
# pip install openai import os from openai import OpenAI client = OpenAI( base_url="https://api.eaon.dev/v1", api_key=os.environ["EAON_API_KEY"], ) stream = client.chat.completions.create( model="auto", messages=[{"role": "user", "content": "Explain a mutex in two sentences."}], stream=True, ) for chunk in stream: print(chunk.choices[0].delta.content or "", end="", flush=True)
One rough edge: /v1/models returns only id per entry, not
the object, created and owned_by fields
OpenAI sends. A strict client that validates client.models.list()
against the full schema may complain. Chat completions are unaffected.
Listing models
The OpenAI-compatible list, filtered to what your key can run, with
auto first. Without a key you see the open models. With one you also see
whatever your plan reaches.
{ "data": [ { "id": "auto" }, { "id": "glm-5.2" } ] }
The richer catalog behind the models page: display names, vendors, descriptions and
tiers, plus the plan table. It does not include auto.
| Key | Holds |
|---|---|
| data | Every entry: id, name, vendor, vendorName, description, tag, tier, and contextLength or weeklyTokens where they apply |
| groups | Entries bucketed by tier, as { tier, tierLabel, tierNote, models }. Empty tiers are dropped |
| plans | The same payload as /v1/plans |
Do not hardcode model ids. Which models are servable depends on which
upstream providers are configured and healthy at that moment, and it changes.
Ask /v1/models at runtime and fall back to auto. At the
time of writing the open catalog is small, so a named id that worked last month may
answer model_not_found today.
Plans and limits
Every plan is free. They differ in what they can reach and how fast.
| Plan | Requests / min | Models | Needs |
|---|---|---|---|
| instant | 10, per IP | Instant tier | Nothing |
| plus | 10 | Instant and Plus | Account, invite code, API key |
| pro | 20 | Instant, Plus and Ultra, with unlimited flagship use | Account, invite code, API key |
| beta | Not capped | Preview models, on a weekly token budget | Invite only |
How the rate limit is counted
A sixty-second sliding window. Eaon counts signed-in callers per account, so
every key you own draws on one bucket, and anonymous callers per IP. Go over and you
get 429 with a retry_after in seconds.
Enforcement is approximate. The counter lives in the memory of whichever edge
instance served you, so a short burst can slip through. Code to the numbers above
and back off on 429, and the difference will not reach you.
Response length on frontier models
For the largest models, Eaon caps max_tokens at 30,000 per request, even
if you ask for more or leave it out. Everything else passes through as you sent it.
One-shot text
The prompt goes in the URL and the reply comes back as text/plain, which
suits a shell or a keyboard shortcut with no JSON on either side. Errors come back as
plain text too.
| Name | Default | Notes |
|---|---|---|
| model | deepseek-v4-flash | Pass auto if you have no key, since the default is not an open model |
| max_tokens | 512 | Anything unparseable falls back to 512 |
curl "https://api.eaon.dev/v1/text/Name%20three%20primary%20colours?model=auto" \ -H "Authorization: Bearer $EAON_API_KEY" # Red, blue, and yellow.
Web search
Two ways to reach it: fold it into a completion, or call it on its own.
Inside a completion
Add "web_search": true, or append :online to the model id.
Eaon searches first and puts the results in front of the model. On a non-streaming
reply the eaon.web_search object tells you what it searched for and how
many results it used.
curl https://api.eaon.dev/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $EAON_API_KEY" \ -d '{"model":"auto:online", "messages":[{"role":"user","content":"What shipped in Eaon this week?"}]}'
On its own
Returns { data, formatted, provider }. data is the raw
result list; formatted is the same thing already laid out as text, ready
to drop into a prompt. POST takes a search array for up to
three queries at once.
MIKLIUM serves the search, as an independent search API. It is not Eaon, and it is not tied to your account.
Your account
These describe your own usage and keys. Reads accept either a dashboard session or an API key; anything that changes state requires a session, so a leaked key cannot mint more keys.
Returns windows (today, week, month, year, all time), a daily
series, a per-models breakdown, your resolved
plan, and betaBudget if you are on the beta tier. Each
window carries requests, successes, errors, token totals, success rate and average
latency.
POST takes an optional name and returns the new key once.
Listing returns your keys with their usage counts and last-used timestamps.
Service status
A liveness probe: { "ok": true, "name": "eaon", "version": "…" }. Cheap
enough to poll.
Service discovery: the version, whether it is OpenAI-compatible, and a map of the main endpoints. A good first call if you are exploring.
One row: ready, degraded or offline, with a
model count and whether failover is available. Eaon does not name the individual
upstreams.
Errors
Errors are { "error": { "message": …, "code": … } }. Switch on
code. The messages are written for people to read and get reworded.
{
"error": {
"message": "Rate limit exceeded. Try again in 12s.",
"code": "rate_limit_exceeded",
"retry_after": 12,
"limit": 10
}
}
| Code | Status | Means |
|---|---|---|
| model_not_found | 404 | No provider serves that id. Retry with auto |
| invalid_api_key | 401 | Key missing or unrecognised on a model that needs one |
| api_key_required | 401 | The plan requires a key and none was sent |
| invite_required | 403 | The account has not redeemed an invite code yet |
| plan_upgrade_required | 403 | The model is above your plan. required_plan says which one it needs |
| flagship_not_included | 403 | Flagship models are not on your plan |
| rate_limit_exceeded | 429 | Too many requests. Back off for retry_after seconds |
| daily_quota_exceeded | 429 | Daily flagship allowance used up |
| beta_weekly_budget_exhausted | 429 | Beta weekly budget spent. percent_used is included |
| appwrite_not_configured | 503 | Accounts are unavailable server-side right now |
Two shapes that are not like the others
-
A
400for a missingmessagesarray, and a502when every provider failed, carry amessagebut nocode. Read a missingcodeas "unknown" and carry on. -
/v1/text/*answers in plain text throughout, including its errors.
Retrying well
A 502 means Eaon already worked through its failover candidates, so an
instant retry lands in the same place. Wait a second or two. For 429,
honour retry_after. Anything else in the 4xx range is a request you need
to change before sending it again.
Going further
Plugins and MCP servers
Open Plugins (⌘⇧P). There are
67 verified plugins with browser sign-in, seven of them one click with no account,
and skills read from SKILL.md folders. You can also add your own MCP
server: a command to run over stdio, or a URL over HTTP.
A plugin action that can’t be undone always waits for you. Connect only servers you trust, since what their tools do is up to them.
Local API server
Settings → Local API Server starts an OpenAI-compatible server on your own
machine, so any tool that speaks that protocol can use the models you set up in Eaon,
on your keys or running locally. It serves /v1/models and
/v1/chat/completions on 127.0.0.1, port 1337 by default.
Turn on Auto start to have it come up with the app.
export OPENAI_BASE_URL=http://127.0.0.1:1337/v1 export OPENAI_API_KEY="local" # any value; the server does not check it
The server does not ask for a key, and it answers requests from web pages open in your browser as well as from programs. Anything that reaches it uses your providers and your keys, so run it only while you need it.
Claude Code
Settings → Claude Code integration points Claude Code at this server, so it runs on whichever provider and key you configured here. Eaon backs up your Claude Code settings before it changes them, and Reset puts them back.
Troubleshooting
macOS says the app is damaged or cannot be opened
You have a build from before 2026.4.0, which was unsigned. Download the current version and it opens on a double-click.
"No model selected"
Eaon has no model of its own. Add a key or sign in under Settings → Model providers, or download one from Models, then pick it from the chip in the message box.
A model keeps failing
Check the key under Settings → Model providers, add a fallback key so Eaon has another to try, or pick a different model.
The API returns model_not_found for an id that used to work
Model availability follows provider availability, and that moves. Ask
/v1/models for the current list and fall back to auto. More
integrations break on this than on anything else, which is why the advice above is to
skip hardcoded ids.
A local model will not load
Check its badge in Models: a model marked Too big for this machine’s memory will struggle. Settings → System Monitor shows what is free. If it runs in Ollama or another server of your own, check that server is up.
Something else
Email support@eaon.dev or ask in
the Discord. If it is an API problem, include the
code from the error and the rough time it happened.
Something here wrong or missing? Tell us and it gets fixed.