openrouter-tts
Generate speech audio from text using OpenRouter's text-to-speech API. Use when the user asks to synthesize speech, narrate text, create a voiceover, generate an audiobook clip, read text aloud, convert text to an audio file, or mentions TTS, text-to-speech, or voice synthesis.
What this skill does
# OpenRouter Text-to-Speech
Synthesize speech via `POST /api/v1/audio/speech` using `curl`. The endpoint is OpenAI-compatible, so the OpenAI SDKs work by pointing them at `https://openrouter.ai/api/v1`. Requires `OPENROUTER_API_KEY` (get one at https://openrouter.ai/keys). If unset, stop and ask.
## One call, raw bytes back
The response body is the audio bytes — write them to a file with the extension matching `response_format`. It is **not JSON**; error responses are, so only try to parse JSON when the status is non-200.
Two response headers are worth keeping:
- `Content-Type` — `audio/mpeg` for mp3; for pcm it includes the sample rate and channel count, e.g. `audio/pcm;rate=24000;channels=1`. Parse these parameters if you need to wrap the raw bytes into a WAV container.
- `X-Generation-Id` — the generation ID (format `gen-tts-<timestamp>-<suffix>`), useful for tracking, debugging, and cost lookups.
## Drop-in workflow
```bash
#!/usr/bin/env bash
set -euo pipefail
MODEL="openai/gpt-4o-mini-tts-2025-12-15"
VOICE="alloy"
FORMAT="mp3" # mp3 or pcm
INPUT="Hello! This is a text-to-speech test."
OUTPUT="speech-$(date +%Y%m%d-%H%M%S).${FORMAT}"
HEADERS=$(mktemp)
payload=$(jq -n --arg model "$MODEL" --arg input "$INPUT" \
--arg voice "$VOICE" --arg fmt "$FORMAT" \
'{model: $model, input: $input, voice: $voice, response_format: $fmt}')
http_code=$(curl -sS -X POST https://openrouter.ai/api/v1/audio/speech \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-D "$HEADERS" \
--output "$OUTPUT" \
-w '%{http_code}' \
-d "$payload")
if [[ "$http_code" != "200" ]]; then
echo "TTS failed (HTTP $http_code):" >&2
cat "$OUTPUT" >&2 # error body is JSON, not audio
rm -f "$OUTPUT" "$HEADERS"
exit 1
fi
gen_id=$(grep -i '^x-generation-id:' "$HEADERS" | awk '{print $2}' | tr -d '\r')
rm -f "$HEADERS"
echo "Saved $(realpath "$OUTPUT") (generation_id=${gen_id:-unknown})"
```
## Discovering TTS models and voices
Filter the models endpoint by output modality to list speech models. Each model carries a `supported_voices` array with the exact voice IDs that provider accepts.
```bash
# Models + voices in one shot
curl -sS "https://openrouter.ai/api/v1/models?output_modalities=speech" \
| jq '.data[] | {id, name, supported_voices, pricing}'
# Just the voices for a specific model
curl -sS "https://openrouter.ai/api/v1/models?output_modalities=speech" \
| jq -r '.data[] | select(.id=="openai/gpt-4o-mini-tts-2025-12-15") | .supported_voices[]'
```
Voices are provider-namespaced: OpenAI uses short names (`alloy`, `nova`), Voxtral encodes language + persona + emotion (`en_paul_happy`), Kokoro prefixes with language/gender (`af_bella` = American female Bella).
## Parameters
| Field | Required | Notes |
| ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `model` | yes | TTS model slug (e.g. `openai/gpt-4o-mini-tts-2025-12-15`, `mistralai/voxtral-mini-tts-2603`). |
| `input` | yes | The text to synthesize. |
| `voice` | yes | Voice identifier. Look up the exact set for your model in `supported_voices` on the models endpoint (see the discovery section above). Voices are provider-namespaced — e.g. `alloy` is an OpenAI voice and will not work on Voxtral or Kokoro. |
| `response_format` | no | `mp3` or `pcm`. Default is `pcm`. **Set this explicitly** — the default is usually not what a user wants to save. |
| `speed` | no | Playback multiplier (e.g. `1.25`). Honored by OpenAI TTS. Other providers may accept and ignore it, or reject unknown fields — check the provider's behavior if it matters. |
| `provider` | no | Provider passthrough — see below. |
### Picking a format
- **`mp3`** (`audio/mpeg`) — compressed, ready to play in any audio app. Default choice for files the user will listen to or share.
- **`pcm`** (`audio/pcm;rate=<rate>;channels=<n>`) — uncompressed raw samples. The response `Content-Type` carries the sample rate and channel count (e.g. `rate=24000;channels=1` for OpenAI TTS), which you'll need if you wrap the bytes into a WAV container. Lower latency for real-time streaming pipelines, but not directly playable on its own. Pick this only when the user explicitly wants raw audio or is piping into a streaming system.
Match the file extension to the format — saving pcm bytes as `.mp3` produces a file no player will open, and this is the most common cause of "empty/corrupted audio" reports.
## Provider-specific options
Provider passthrough goes under `provider.options.<slug>` and is only forwarded when that provider handles the request. The most useful one is OpenAI's `instructions`, which steers tone, accent, pacing, or emotion without retraining:
```json
{
"model": "openai/gpt-4o-mini-tts-2025-12-15",
"input": "Welcome to the show.",
"voice": "alloy",
"response_format": "mp3",
"provider": {
"options": {
"openai": {
"instructions": "Speak in a warm, friendly tone with a slow pace."
}
}
}
}
```
For other providers, check each provider's upstream docs for available passthrough keys — naming conventions vary (camelCase for OpenAI/Google, snake_case for most others).
## OpenAI SDK compatibility
Because the endpoint mirrors OpenAI's `/audio/speech`, both OpenAI SDKs work by swapping the base URL. Prefer this when the user is already in a Python/TypeScript project and doesn't want to shell out.
```python
# Python — streaming write to file
import os
from openai import OpenAI
client = OpenAI(base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"])
with client.audio.speech.with_streaming_response.create(
model="openai/gpt-4o-mini-tts-2025-12-15",
input="The quick brown fox jumps over the lazy dog.",
voice="nova",
response_format="mp3",
) as response:
response.stream_to_file("output.mp3")
```
```typescript
// TypeScript — collect bytes, write once
import OpenAI from "openai";
import fs from "fs";
const client = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY!,
});
const response = await client.audio.speech.create({
model: "openai/gpt-4o-mini-tts-2025-12-15",
input: "The quick brown fox jumps over the lazy dog.",
voice: "nova",
response_format: "mp3",
});
await fs.promises.writeFile(
"output.mp3",
Buffer.from(await response.arrayBuffer()),
);
```
## Long inputs
TTS models have per-request character limits (usually a few thousand characters) and are priced **per character of input**, so there's no penalty to splitting. For anything long (chapters, articles, scripts):
1. Split the text at sentence or paragraph boundaries — never mid-word.
2. Synthesize each chunk with the same `model` + `voice` so prosody stays consistent.
3. Concatenate the resulting audio files. For mp3, `ffmpeg -i "concat:part1.mp3|part2.mp3" -c copy out.mp3` works for simple cases; for mixed bitrates or tight seams, re-encode via `ffmpeg -f concat -safe 0 -i list.txt output.mp3`.
Splitting also improves time-to-first-audio if you stream chunks to a user as they're generated.
## Troubleshooting
**"Empty" or unplayable audio file** — almost always a format/extension mismatch. Check `Content-Type` in the response headers: `audio/pcm` saved as `.mp3` will not play. Either re-request with `response_format: "mp3"` or save with the matching extension.
**400 with `"Model X does not exist"`** — the slug is wrong. Use the full dateRelated in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.