Endpoints
POST/v1/summarizescope: summarize
Summarize
Send text and say how you want it summarized — verbosity, focus, tone, even a language to translate into. The prompt is built server-side, so you get the same summarization the Mac app runs, without writing a system prompt yourself.
| Field | Type | Required | Notes |
|---|
text | string | yes | What to summarize. |
verbosity | string | — | auto · brief · balanced · detailed · talkback. Default balanced. auto adapts to input length — a short selection is read back almost verbatim, a long one gets a tight TL;DR. talkback reads the whole text aloud faithfully — every point, nothing condensed — instead of summarizing it. |
focus | string | — | outcome · keyChanges · walkthrough. Default outcome. |
tone | string | — | neutral · warm · professional · energetic · calm. Default neutral. |
language | string | — | A language to translate into (english, spanish, french, german, italian, portuguese, hindi, japanese, korean, chinese, arabic, russian, dutch — see GET /v1/prompt-catalog for the live list). Default auto keeps the source language. |
expressive | string | — | none · gemini · cartesia. Default none. Writes performance cues into the summary text itself ([laughs], <emotion value="happy"/>) for voices that can act them out. |
sourceApp | string | — | Where the text came from, e.g. "Xcode" — used for attribution ("Xcode says…"). Truncated to 40 characters. |
stream | boolean | — | Server-sent events when true. Default false — a non-streaming call returns one buffered JSON response. |
Returns. The buffered chat-completions response — `choices[0].message.content` is the summary — when `stream` is false, the default. An SSE stream of `delta` chunks in the same shape when `stream` is true.
curl -X POST https://ideal-newt-976.convex.site/v1/summarize \
-H "Authorization: Bearer wb_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "<the agent output you want summarized>",
"verbosity": "brief",
"focus": "outcome",
"sourceApp": "IMAI"
}'
Response
{
"choices": [
{ "message": { "role": "assistant", "content": "Fixed the retry so uploads no longer stall." } }
],
"usage": { "cost": 0.000114 }
}
Already sending `model` and `messages`? That form still works unchanged — it's the original chat-completions proxy this endpoint was; a body containing `messages` always takes that path, even if it also has `text`. Add "stream": true to either shape for a server-sent-events response instead of one buffered call: data: {"choices":[{"delta":{"content":"..."}}]} lines, ending in data: [DONE]. One thing worth knowing before you rely on these: a misspelled `verbosity`, `focus`, `tone`, or `expressive` value does NOT error — it's silently replaced with that field's default, and an unrecognized `language` name just produces no translation (same as `auto`). Nothing here fails loudly the way `/v1/speak`'s `voice` does (a bad voice key 400s by name) — a typo in any of these fields degrades quietly instead.
POST/v1/speakscope: speak
Speak
Turn text into audio using one of the curated voices. You send text and (optionally) a voice key — the server resolves the model, so you can't be billed for one you didn't ask for.
| Field | Type | Required | Notes |
|---|
text | string | yes | The text to speak. 4000 characters max — over that, 413 text_too_long. |
voice | string | — | Any enabled key returned by GET /v1/presets. The service key is billed under its own API pricing and cap; app everyday/premium labels do not restrict API callers. Defaults to the first enabled preset. |
speed | number | — | 0.5 to 3.0. Default 1. Honored on the Grok voices (eve, leo, ara). Silently ignored on the Gemini voices (aoede, kore, puck, zephyr), including the default — you won't get an error, the audio just comes back at normal speed. |
response_format | string | — | "wav" or "pcm". Only takes effect on the Grok voices, defaulting to wav there. The Gemini voices always return pcm regardless of what you send. The gpt-audio voices ignore this field — the format is whatever OpenRouter returns for that call. |
Returns. Raw audio bytes. `X-Audio-Format` tells you what you actually got — wav, pcm, or provider-chosen for the gpt-audio voices — since Content-Type alone isn't reliable enough to build a player around. When the format is pcm, `X-Audio-Sample-Rate` gives the sample rate. The default voice (aoede) always returns pcm: headerless 24 kHz mono 16-bit audio that no player will open until you wrap it.
curl -X POST https://ideal-newt-976.convex.site/v1/speak \
-H "Authorization: Bearer wb_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "text": "Fixed the retry so uploads no longer stall." }' \
--output reply.pcm
# The default voice (aoede) returns headerless PCM — reply.pcm won't open in anything yet.
# The response headers told you the format: X-Audio-Format: pcm, X-Audio-Sample-Rate: 24000.
# Wrap it into a real, playable file with that:
ffmpeg -f s16le -ar 24000 -ac 1 -i reply.pcm reply.wav
Response
(raw audio bytes)
Response headers:
X-Audio-Format: pcm
X-Audio-Sample-Rate: 24000
Want a file you can --output straight to something playable? Pick a Grok voice instead — { "text": "...", "voice": "eve" } returns a real wav, headers and all, no unwrapping needed. Already sending model and input? That form still works unchanged — a body with model always takes that path, even if it also has text.
POST/v1/actionsscope: actions
Extract actions
Pull the structured items out of a conversation: what to do next, what was decided, and what is still open. Useful for turning a long agent thread into a task list.
| Field | Type | Required | Notes |
|---|
content | string | yes | The conversation or document. |
Returns. Three string arrays. Empty categories come back as [].
curl -X POST https://ideal-newt-976.convex.site/v1/actions \
-H "Authorization: Bearer wb_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "content": "<the full chat transcript>" }'
Response
{
"actions": ["Add a retry to the upload step", "Cover it with a unit test"],
"decisions": ["Cap retries at three attempts"],
"openQuestions": ["Is a 30s total timeout enough on slow networks?"]
}
POST/v1/promptscope: prompt
Draft a prompt
Turn a conversation plus an optional goal into one complete, self-contained prompt a coding agent can act on, with a one-line gist of what it asks for.
| Field | Type | Required | Notes |
|---|
content | string | yes | The conversation or document. |
goal | string | — | What you want to happen next. |
Returns. The drafted prompt and a plain-sentence gist.
curl -X POST https://ideal-newt-976.convex.site/v1/prompt \
-H "Authorization: Bearer wb_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "<the chat so far>",
"goal": "Make the upload retry configurable"
}'
Response
{
"prompt": "In the upload pipeline, make the retry count configurable…",
"gist": "Asks the agent to make upload retries configurable with a sane default."
}
GET/v1/presetsno key required
List voices
The curated voice lineup, server-managed so it stays current without a client release. No key required — use it to build a voice picker.
Returns. An array of presets: key, name, tagline, engine, model, and the provider's own voice id.
curl https://ideal-newt-976.convex.site/v1/presets
Response
{ "presets": [ { "key": "aoede", "name": "Aoede", "tagline": "Calm and clear", "engine": "gemini-tts", "model": "google/gemini-3.1-flash-tts-preview", "voice": "Aoede" } ] }
Every enabled preset returned here is accepted by POST /v1/speak. The server routes each voice to its declared engine while keeping provider credentials private.
GET/v1/prompt-catalogno key required
Prompt catalog
The exact instruction text POST /v1/summarize composes its system prompt from — the same catalog the Mac app bundles and refreshes. No key required: it's instruction text, not a secret, and the app needs it before sign-in.
Returns. The full catalog: instruction templates, and instruction text keyed by each verbosity/focus/tone value, plus the language label map and the per-verbosity token ceilings.
curl https://ideal-newt-976.convex.site/v1/prompt-catalog
Response
{
"version": 1,
"templates": {
"cloud": "You are a voice assistant. The user selected some text…",
"cloudTalkback": "You are narrating someone's text to them out loud…",
"local": "{preamble}\n\nWrite for the ear…",
"localTalkback": "{preamble}\n\nKeep EVERY point, detail, number…",
"localPreamble": "You are a text-to-speech rewriting tool…",
"attributionCloud": "The text was selected in {app}…",
"attributionLocal": "The passage was copied from {app}…",
"language": "\n\nLANGUAGE: Write the ENTIRE spoken response in {language}…",
"expressiveGemini": "\n\nEXPRESSIVE DELIVERY — this OVERRIDES the \"no symbols\" rule…",
"expressiveCartesia": "\n\nEXPRESSIVE DELIVERY — this OVERRIDES the \"no symbols\" rule…"
},
"verbosity": { "brief": "Give a very brief summary: 1-3 sentences, only the essential outcome.", "balanced": "…", "detailed": "…", "talkback": "Read the entire text aloud, faithfully, in natural spoken language." },
"autoLength": { "shortUnder": 40, "longOver": 300, "short": "…", "medium": "…", "long": "…" },
"focus": { "outcome": "Focus only on the final outcome/result — what I now have or what changed for me.", "keyChanges": "…", "walkthrough": "…" },
"tone": { "neutral": "Use a plain, neutral tone.", "warm": "…", "professional": "…", "energetic": "…", "calm": "…" },
"languages": { "english": "English", "spanish": "Spanish", "…": "11 more" },
"maxTokens": { "brief": 180, "balanced": 400, "detailed": 800, "talkback": 4000 }
}
version bumps whenever any string in the catalog changes, so a long-lived client can tell a stale cached copy from a current one rather than comparing string contents.