This site does not allow itself to be embedded. You can still read it on the original site — the toolbar below keeps your place in the directory.
The agent finishes, the summary scrolls past, and you will read it later. Build the fix: a coding agent that ends every run with a plain-language summary, piped into ElevenLabs text-to-speech and out as an MP3 you can listen to on the way to work. The complete one-hour build.
Most of what an agent finishes with is text that expects you to be sitting in front of a terminal: a diff, a test run, a ten-paragraph summary of what changed. Reading that is cheap when it is one interactive session. It is not cheap when the agent runs on a schedule and you have seven summaries by Friday - the scrolling is the bottleneck, not the agent.
The fix is to change the consumption mode. Routine agent output does not need a screen; it needs ears. A two-minute MP3 on the way to work beats a wall of text you were going to skim anyway. This guide builds exactly that: a coding agent that ends every run with a plain-language summary, piped into [ElevenLabs](https://dub.sh/dd-elevenlabs) text-to-speech, and delivered as an audio file on your machine. [OpenCode](https://opencode.ai/go?ref=M6HEHM4JM5) is the agent CLI doing the work - open source, scriptable, and the same `opencode run` pattern our [cron automation guide](/blog/opencode-cron-automation-guide) uses - and ElevenLabs is the TTS layer, the same API our [text-to-speech comparison](/blog/best-tts-apis-for-developers-2026) ranks as the quality leader.
This is the output-side sibling of the [Wispr Flow post](/blog/wispr-flow-voice-prompts-coding-agents): that one puts your voice into the agent, this one puts the agent's voice into your ears.
## Official Sources
| Resource | Description |
|----------|-------------|
| [ElevenLabs TTS API reference](https://elevenlabs.io/docs/api-reference/text-to-speech/convert) | The `POST /v1/text-to-speech/{voice_id}` endpoint, request body, and defaults |
| [ElevenLabs voices API](https://elevenlabs.io/docs/api-reference/voices/search) | Listing available voices and their IDs |
| [ElevenLabs API pricing](https://elevenlabs.io/pricing/api) | Per-character rates for every TTS model |
| [OpenCode Docs](https://opencode.ai/docs/) | Install, models, and `opencode run` non-interactive mode |
Seven steps, under an hour, every step ending in something you can run.
## Step 1: Install OpenCode and prove headless mode works
Prerequisites: a machine with curl and a shell, an [ElevenLabs](https://dub.sh/dd-elevenlabs) account (the free tier includes 10,000 characters a month, per the [API pricing page](https://elevenlabs.io/pricing/api) - enough to try this build several times), and an LLM provider key.
Install OpenCode with the official one-liner from the [OpenCode docs](https://opencode.ai/docs/):
```bash
curl -fsSL https://opencode.ai/install | bash
```
Authenticate a provider (`opencode auth login`), then confirm the capability the whole pipeline depends on - one task, one answer, no interactive session:
```bash
opencode run --model opencode/deepseek-v4-flash "print the current directory tree, two levels deep"
```
If that prints a tree and exits cleanly, the worker side is proven. On model choice: briefs are a narrow, bounded task, which is exactly where a budget model earns its keep. The [DeepSeek V4 Flash 0731 release](/blog/deepseek-v4-flash-0731-opencode-guide) at $0.14/$0.28 per million tokens is the current sweet spot; the summary it writes is short, so the token cost of a brief is fractions of a cent before TTS.
**What you have now:** a proven headless agent command that produces text you can capture.
## Step 2: Get an API key and pick a voice
In the ElevenLabs dashboard, generate an API key from your profile settings, then export it. The key is sent as the `xi-api-key` header on every request, per the [API reference](https://elevenlabs.io/docs/api-reference/text-to-speech/convert):
```bash
export ELEVEN_API_KEY="your-key-here"
```
List the voices available to your account with the [voices endpoint](https://elevenlabs.io/docs/api-reference/voices/search) - no request body, the key in the header is enough:
```bash
curl -H "xi-api-key: $ELEVEN_API_KEY" "https://api.elevenlabs.io/v2/voices" \
| jq -r '.voices[] | "\(.voice_id) \(.name)"' | head -20
```
Every row is a voice ID plus a name. Pick one you want to hear twice a day, then export its ID:
```bash
export VOICE_ID="the-id-of-the-voice-you-picked"
```
Keep these two environment variables around for the rest of the build - they are the entire API surface you need.
**What you have now:** an authenticated, working API key and a chosen voice ID.
## Step 3: The one-command bridge: agent text to spoken MP3
The [TTS endpoint](https://elevenlabs.io/docs/api-reference/text-to-speech/convert) is a single POST: `https://api.elevenlabs.io/v1/text-to-speech/{voice_id}` with a JSON body containing the `text`, and the audio comes back as a file download. The default output is MP3 at 44.1kHz, and the default model is `eleven_multilingual_v2` - both fine for this build.
Save this as `~/bin/speak.sh`:
```bash
#!/bin/bash
# speak.sh - read text from stdin, say it as an MP3
set -eu
: "${ELEVEN_API_KEY:?}" "${VOICE_ID:?}"
TEXT="$(cat)"
mkdir -p ~/briefs
OUT="$HOME/briefs/brief-$(date +%Y%m%d-%H%M%S).mp3"
curl -sS -X POST "https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}" \
-H "xi-api-key: $ELEVEN_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "$TEXT" '{text: $t}')" \
-o "$OUT"
echo "$OUT"
```
Make it executable, then prove the whole audio path with one sentence:
```bash
chmod +x ~/bin/speak.sh
echo "First agent brief, ready when you are." | ~/bin/speak.sh
# → ~/briefs/brief-20260808-*.mp3
afplay ~/briefs/brief-*.mp3 # macOS
# mpv ~/briefs/brief-*.mp3 # Linux
```
Two details matter: `jq -n` builds the JSON body so the text is escaped properly (a summary full of quotes and backticks stays data, not shell), and `-o` writes the returned audio file straight to disk. If you hear the sentence, the entire pipeline - key, voice, endpoint, playback - is proven in isolation.
**What you have now:** a two-command voice: text in, MP3 out.
## Step 4: Make the agent write for audio, not for the terminal
This is the step that decides whether the MP3 is listenable. TTS reads what you give it, and what you give it is the agent's output - so the prompt must ask for a summary shaped for speaking. Tables, code blocks, and diff hunks are garbage in audio. Short sentences, plain language, and numbers spelled out are gold.
A prompt that ends like this produces a brief you can actually listen to:
```bash
cd ~/work/some-repo
opencode run --model opencode/deepseek-v4-flash \
"Run the test suite and summarize the state of this repo.
Finish with a summary for audio, 200 to 400 words, written in short
sentences for text-to-speech. Use plain language and no bullet lists,
no code, no table syntax. Spell out numbers. End the summary with the
single most important thing I should know. Save the summary to summary.txt."
```
The file is the contract, not the terminal: the agent writes `summary.txt`, and your voice script reads it. Piping raw agent stdout to TTS is tempting but brittle - a progress line or a log line ruins the audio, and you cannot hear the difference until it is too late. A file the agent was told to fill is deterministic.
Now the full pipeline, end to end:
```bash
opencode run --model opencode/deepseek-v4-flash "$PROMPT" >/dev/null
cat summary.txt | ~/bin/speak.sh | xargs afplay
```
**What you have now:** one command that runs a real agent task and speaks its summary aloud.
## Step 5: Put the voice on the schedule
A voice you have to remember to trigger is a novelty. The payoff is the schedule: the [cron automation guide](/blog/opencode-cron-automation-guide) has the full runner pattern - fresh clone, one bounded job, a gate, a quiet no-op exit. Adding a voice is three lines at the end of that runner script, after the quiet-exit check:
```bash
# Nothing changed? Stay quiet - most runs should.
git diff --quiet && git diff --cached --quiet && exit 0
# Speak the summary of what this run actually did
cat summary.txt | ~/bin/speak.sh >/dev/null
```
Then schedule it like any chore (`crontab -e`; sanity-check expressions on [crontab.guru](https://crontab.guru)):
```bash
# Every weekday at 07:10: a spoken morning brief of yesterday's work
10 7 * * 1-5 ~/bin/agent-chore.sh morning-brief "Summarize yesterday's commits and open PRs. Write a 200 to 400 word audio summary to summary.txt."
```
Keep two behaviors from the cron guide intact: the fresh clone per run, and the quiet no-op exit. A run that found nothing to do should not talk; silence is the feature. If the MP3 lands in `~/briefs/` and you want it on your phone, sync the folder or drop it into a podcast player's watch directory - the file is a plain MP3, every audio app accepts it.
**What you have now:** a morning brief that reads itself, with zero interaction.
## Step 6: Speak only when it matters
Summaries are pleasant; alerts are useful. The same bridge becomes a failure notifier by gating on the run's exit code instead of its output. A CI status, a nightly dependency check, or a scheduled agent run - the shape is identical: run the thing, and only if it failed, speak why.
```bash
#!/bin/bash
# ~/bin/fail-brief.sh - speak only when the job fails
set -u
: "${ELEVEN_API_KEY:?}" "${VOICE_ID:?}"
if "$@"; then
echo "quiet success - no audio" >&2
exit 0
fi
echo "The job failed. Last error: $(tail -c 400 "$LOGFILE")" \
| ~/bin/speak.sh | xargs afplay
exit 1
```
Wrap any command: `~/bin/fail-brief.sh ./nightly-check.sh`. Success is silent; failure is a spoken sentence with the last 400 characters of the log. That is the same discipline as the [$400 overnight bill post](/blog/400-dollar-overnight-bill-agent-finops) applied to audio: the signal should be rare, specific, and impossible to ignore. The same gate can hang off the [webhook pattern](/blog/deploy-agent-webhook-railway) - an agent run triggered by an issue speaks only when the test gate fails.
**What you have now:** a notifier that earns attention by spending it rarely.
## Step 7: What you have now, and where it goes next
The build is complete: an agent CLI, a voice API, and one shell script connecting them. Run a task, get an MP3. Schedule the task, get a daily brief. Gate the task, get a failure alert. The whole stack costs less than a cup of coffee a month: TTS is billed per character - $0.10 per 1,000 characters for the default multilingual model, $0.05 for Flash/Turbo, per the [API pricing page](https://elevenlabs.io/pricing/api). A 400-word brief is roughly 2,400 characters: about $0.24 on the default model, $0.12 on Flash, and the free tier's 10,000 characters covers three or four briefs a month. The agent's own token cost is fractions of a cent on a budget model.
Refinements worth the next half hour: pass `voice_settings` in the request body to tune stability and speed (`speed` above 1.0 shortens the brief without touching the text); list models with `GET /v1/models` and switch `model_id` to a Flash model to halve the cost; and if you want the agent to talk while it works instead of after, the [WebSockets streaming endpoint](https://elevenlabs.io/docs/api-reference/text-to-speech/v-1-text-to-speech-voice-id-stream-input) streams audio from partial text - overkill for briefs, right for live demos.
The end state: your agent produces a short audio digest every morning and a spoken alarm when something breaks, and the only screen time involved is the ten minutes you spent building it.
## FAQ
### Can my coding agent talk in real time while it works?
Not with this build - it is file-first: the agent finishes, writes a summary, and the summary is spoken. For real-time streaming audio from partial text, ElevenLabs offers a [WebSockets endpoint](https://elevenlabs.io/docs/api-reference/text-to-speech/v-1-text-to-speech-voice-id-stream-input) that streams generated audio as text arrives. For briefs and alerts, the simpler POST pipeline is the right tool.
### How much does an audio agent brief cost?
TTS is billed per character: $0.10 per 1,000 characters for the default `eleven_multilingual_v2` model and $0.05 for Flash/Turbo models, per the [API pricing page](https://elevenlabs.io/pricing/api). A 400-word brief is about 2,400 characters, so roughly $0.24 on the default model and $0.12 on Flash. The free tier includes 10,000 characters per month.
### Do I need to clone my own voice?
No. The [voices endpoint](https://elevenlabs.io/docs/api-reference/voices/search) lists the premade voice library, and any of them works with the same API key. Voice cloning exists but sits on paid tiers - for a brief you will listen to for two minutes, a premade voice is the honest choice.
### Does this work with Claude Code or Codex instead of OpenCode?
Yes. The only OpenCode-specific step is Step 1. Any agent CLI that can write a summary to a file works - run it, then feed the file to `speak.sh`. The shell script does not know or care which harness produced the text.
### What makes an agent summary actually listenable?
Ask for the shape in the prompt: short sentences, plain language, no bullet lists, no code or table syntax, numbers spelled out. TTS reads exactly what you give it - a summary that reads well on screen frequently reads poorly aloud. The prompt in Step 4 encodes all of it.
## Sources
| Source | URL |
|--------|-----|
| ElevenLabs TTS API reference | https://elevenlabs.io/docs/api-reference/text-to-speech/convert |
| ElevenLabs voices API | https://elevenlabs.io/docs/api-reference/voices/search |
| ElevenLabs TTS WebSockets streaming | https://elevenlabs.io/docs/api-reference/text-to-speech/v-1-text-to-speech-voice-id-stream-input |
| ElevenLabs API pricing | https://elevenlabs.io/pricing/api |
| OpenCode Docs | https://opencode.ai/docs/ |
Some links to tools above are referral links - see our [affiliate disclosure](/affiliate-disclosure).
**Last updated:** August 8, 2026
## Continue Reading
- [Put an AI Agent on a Cron Job](/blog/opencode-cron-automation-guide) - the schedule this voice hangs off: the full runner, gate, and quiet-exit pattern
- [Give Your Coding Agent a Voice](/blog/wispr-flow-voice-prompts-coding-agents) - the input side: dictate prompts into the agent with Wispr Flow
- [Text-to-Speech APIs for Developers in 2026](/blog/best-tts-apis-for-developers-2026) - where ElevenLabs sits on quality, latency, and price
- [OpenCode Developer Guide 2026](/blog/opencode-developer-guide-2026) - the full tour of the CLI doing the work in this build
- [DeepSeek V4 Flash 0731 in OpenCode](/blog/deepseek-v4-flash-0731-opencode-guide) - the budget model that keeps agent runs cheapRead on developersdigest.tech ↗
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.