Transcription server
Dictate through a speech-to-text server you run: speaches, whisper.cpp, or any OpenAI-compatible service.
The transcription server is another dictation engine, next to the system recognizer, offline Whisper, offline Whistle, offline Asr4All, offline Parakeet and offline Moonshine. The keyboard records what you say and sends the recording to a server you choose. The server sends back the text. The heavy work runs on a computer on your desk or in your rack, not on your phone’s battery, and you choose the model.
It works in every edition, Lite included. There is nothing to download on the phone.
Setting it up
Section titled “Setting it up”- Under Engine, set Recognition engine to Transcription server.
- In the Transcription server group that appears, fill in Server address. For a server on your own network it looks like
http://192.168.1.10:8000/v1. - Fill in Model if your server needs one (see the recipes below). Fill in API key if it asks for one.
- Press Test on Test the server. The keyboard sends one second of silence and says whether the server answered. If it fails, it shows the same message a failed dictation would show.
Dictation then works the way it does with offline Whisper. Speak, then press the microphone when you are done (or let the 30-second clip fill, or set Stop after a pause under Voice typing and let a pause end it). The phrase lands a moment later, with spoken punctuation, spacing and undo applied as usual. Keep listening chains the next phrase the same way.
Send the keyboard language is on by default. It tells the server the language of your active layout, which is faster and more accurate than letting it guess from a short clip. Turn it off if you dictate in two languages on one layout and want the server to detect which one you are speaking. With a Voice typing language picked, the server is told that language instead, and with several picked it’s told none and detects it.
The address
Section titled “The address”Type the server’s API root, up to and including the version segment, the same way the AI tool’s Other service asks for it. The keyboard adds /audio/transcriptions itself.
| You type | The keyboard sends to |
|---|---|
http://192.168.1.10:8000/v1 | http://192.168.1.10:8000/v1/audio/transcriptions |
http://192.168.1.10:8000 | http://192.168.1.10:8000/v1/audio/transcriptions (a bare address gets /v1) |
https://api.groq.com/openai/v1 | https://api.groq.com/openai/v1/audio/transcriptions |
http://192.168.1.10:8080/inference | used as it is (whisper.cpp’s own endpoint) |
An address that already ends in /transcriptions or /inference is used as typed.
A server with its own route
Section titled “A server with its own route”Some servers take the same request on a route of their own, such as POST /v1/transcribe. Put that route in Endpoint path, under Server address. When it is filled in, the keyboard adds it to the address exactly as typed, with no guessing and no /v1:
| Server address | Endpoint path | The keyboard sends to |
|---|---|---|
http://192.168.1.10:8000 | /v1/transcribe | http://192.168.1.10:8000/v1/transcribe |
http://192.168.1.10:8000/v1 | transcribe | http://192.168.1.10:8000/v1/transcribe |
http://192.168.1.10:8000 | https://asr.example.com/api/stt | https://asr.example.com/api/stt (a whole address replaces the server address) |
Leave Endpoint path empty for the usual /audio/transcriptions. The request itself is the same either way; see The protocol.
A model or server per language
Section titled “A model or server per language”Some models are much better at one language than another. With two or more languages enabled, a Per language group appears under Transcription server, with one row per language. Each row says what that language uses. Tap it to change it:
- Same server, another model. Leave Server address empty and fill in Model. The language uses the main server, its route and its key, with this model instead of the main one. Leave Model empty too and the language goes back to the main server.
- A server of its own. Fill in Server address. Endpoint path and API key appear for that server. Nothing is borrowed from the main server: the main key is never sent to a different address, and an empty Model lets this server pick its own.
Test in the dialog sends one second of silence to what the dialog holds, before you save. Use main server clears the language’s entry.
Dictation picks the server from the language of the layout you are typing on, the same way offline Whisper picks a model. Send the keyboard language, Prompt and the words to listen for apply to every server. A language’s own API key is left out of backups unless you choose to include API keys, like the main one.
Running a server
Section titled “Running a server”The keyboard does not use a protocol of its own. It speaks OpenAI’s audio transcription API, which most speech-to-text servers already support. Any of these works.
speaches (faster-whisper)
Section titled “speaches (faster-whisper)”speaches runs faster-whisper behind an OpenAI-compatible API. On a machine with Docker:
docker run --detach --publish 8000:8000 --name speaches \ --volume hf-hub-cache:/home/ubuntu/.cache/huggingface/hub \ ghcr.io/speaches-ai/speaches:latest-cpuUse the latest-cuda image with --gpus=all on a machine with an NVIDIA GPU. speaches only runs models you have downloaded, so fetch one once:
curl -X POST http://localhost:8000/v1/models/Systran/faster-whisper-smallThen set:
- Server address:
http://<computer's address>:8000/v1 - Model: the id you downloaded, here
Systran/faster-whisper-small. For English only,Systran/faster-distil-whisper-small.enis faster.
whisper.cpp
Section titled “whisper.cpp”whisper.cpp ships whisper-server, a single binary with no Python. Download a model and start it on all interfaces:
whisper-server -m models/ggml-base.bin -l auto --host 0.0.0.0 --port 8080 \ --inference-path /v1/audio/transcriptionsWith --inference-path as above, set Server address to http://<computer's address>:8080/v1. Without it, point the keyboard at http://<computer's address>:8080/inference. Leave Model empty: whisper-server uses the model it was started with. -l auto matters when Send the keyboard language is off. Without it whisper-server assumes English.
Cloud services
Section titled “Cloud services”The same engine reaches the hosted APIs, if you would rather pay per minute than run a machine:
| Service | Server address | Model |
|---|---|---|
| Groq | https://api.groq.com/openai/v1 | whisper-large-v3-turbo |
| OpenAI | https://api.openai.com/v1 | whisper-1 or gpt-4o-mini-transcribe |
Both need an API key. LocalAI and other OpenAI-compatible gateways work too.
The protocol
Section titled “The protocol”This is everything the keyboard sends and expects, so you can build or proxy a server of your own.
Request. POST <endpoint> with Content-Type: multipart/form-data, and these parts:
| Part | Sent | Contents |
|---|---|---|
file | always | the clip, speech.wav, audio/wav: 16-bit PCM, 16 kHz, mono, up to 30 seconds (under 1 MB) |
model | when Model is filled in | the text of the field |
language | when Send the keyboard language is on and Whisper knows the language | an ISO-639-1 code such as en, bn or de |
prompt | when there are words to listen for, or Prompt is filled in | your words to listen for, then the text of Prompt. See Words and the prompt |
response_format | always | json |
An Authorization: Bearer <key> header is added when API key is filled in. Clips shorter than 0.3 seconds, and clips with nothing louder than a quiet room in them, are never sent. See A silent clip types nothing.
Reply. HTTP 200 with {"text": "the words"}. Other fields are ignored. A plain-text body is accepted too, and is used as the transcript.
Errors. Any status outside 200–299 fails the phrase. When the body is {"error": {"message": "…"}}, that message is what the voice panel shows. Otherwise the panel shows the keyboard’s own wording for the status (401 and 403 read as a rejected key, 429 as a rate limit).
Timeouts are 10 seconds to connect and 60 seconds for the reply.
Words and the prompt
Section titled “Words and the prompt”A server gets the words to listen for inside OpenAI’s prompt field, followed by whatever you type into Prompt. The model reads the prompt as the text that came before your clip, so a name in it is a name the model has just “seen” spelled your way. The Prompt field is also where to set a style, such as “Casual chat between friends.” or a sentence in the spelling you want.
Whisper models, which is what most servers run, read only the last 224 tokens of the prompt, and drop the rest without saying so. That’s a limit of the model: Whisper’s decoder has room for 448 tokens and gives half of them to the prompt. 224 tokens is roughly 150 short English words, fewer for long names or for scripts that take several tokens a character, so it can’t hold a whole dictionary. Newer models such as OpenAI’s gpt-4o-transcribe read far more.
So the keyboard builds the prompt for that limit. It sends at most 150 words, and puts them in order of importance with the most important at the end, where a Whisper model keeps them: your Prompt text last, then your own list, then the words you added by hand and the personal dictionary, and the learned words at the front. When a Whisper server has to cut, it cuts the learned words first. The whole prompt is capped at 4,000 characters.
Privacy
Section titled “Privacy”With this engine, the recording of each phrase leaves your phone. It goes to the address you typed, or the one you set for the language you are typing in, and nowhere else, and only after you start dictating. Nothing is sent in the background. The API keys are stored with your other settings and are left out of backups unless you choose to include API keys. See Network policy.
An http:// address sends the audio unencrypted. That is fine on your own network. Use https:// for anything reached over the internet.
With Listen for my words on, the prompt carries up to 150 of your own words, as described in Words and the prompt. Turn it off, and leave More words to listen for and Prompt empty, and no prompt is sent at all.
Voice typing stays off in password fields, whichever engine is chosen.
Details & edge cases
Section titled “Details & edge cases”- Mobile data. Data saver has a Transcription server row. It is allowed by default. Set it to Ask each time and the first phrase on mobile data is held with a note. Pressing the microphone again sends it and allows the rest of that session. Turn off stops the engine on mobile data.
- No address yet. Choosing the engine without filling in the address makes the voice panel say so, with a shortcut to these settings and a button that switches back to the system recognizer.
- Server unreachable. The panel shows why: no connection, a timeout, or a server that refused the connection. Nothing is typed and the rest of your text is untouched. Press the microphone to try again. With Use on-device models offline on and a Whisper model downloaded, the phrase is transcribed on the phone instead, and with no connection at all the next phrase goes to the Whisper model, or to the voice typing of your phone, from the start.
- No live words. Like offline Whisper, the server gets the whole phrase at once, so nothing appears in the field while you speak.
