127.0.0.1 only. It never
listens on the LAN. The server writes the bound port and a bearer token to a
discovery file on disk. MCP wrappers, benchmark scripts, and shell automation
read this file to find the server, and they need no configuration.
Discovery file
chmod 600 permissions on macOS. It has this JSON shape:
Authentication
Every endpoint except/health needs this header:
%LOCALAPPDATA%\HyperWhisper\local-api-token.bin. You can make a new token
from Settings → API Server (macOS) or Settings → Local API (Windows).
The previous token then becomes invalid immediately.
Response envelope
The server returns both shapes with HTTP 200:- 400 for malformed JSON
- 401 for a missing or invalid bearer token
- 403 for a rejected Host/Origin header. This is a DNS-rebinding guard on every
route, including
/health.
ok:false with a machine-readable code, and with
HTTP 200. Examples are a transcription engine error, a missing API key, and a
file that the server cannot find. Read the code, not the status.
Request limits
The server refuses a request that is too large:
A refusal is a business failure, not a protocol failure. The server returns
HTTP 200 with this envelope:
Audio exceeds the configured upload limit. when the audio
limit is the one that the request passed.
audio_base64 stops at 37.5 MiB, not at 48 MiB. Base64 turns 3 bytes of
audio into 4 characters, and the request limit applies to those characters. So
the request limit is always the first one that a large audio_base64 payload
passes: it stops the audio at 39,321,600 bytes (37.5 MiB), and a little below
that, because the JSON around the string counts too. Design against 37.5 MiB
when you put audio in the JSON body. A payload above it gets Request exceeds the configured limit., never the audio message.
To send more audio than that, keep it out of the JSON body: use a file path,
or a multipart audio part on Linux. Neither travels as base64, so the audio
limit of 48 MiB is the one that can apply to them. The list below gives the
limit that each platform applies to each one.
Each platform applies the limits differently:
-
macOS applies the request limit to every route that reads a body, and
always answers with the HTTP 200 envelope. It applies the audio limit to
audio_base64and to afilepath. Foraudio_base64the limit is out of reach, because the request limit refuses the payload first; for afilepath it is reachable, and a file above 48 MiB getsAudio exceeds the configured upload limit.macOS accepts no multipart request. So afilepath is how you transcribe audio between 37.5 MiB and 48 MiB on macOS. -
Linux applies both limits, and both are reachable. It applies the audio
limit to a multipart
audiopart and to afilepath as well as toaudio_base64. Afilepath above 48 MiB gets the same refusal here as on macOS. Only/transcribeanswers with the HTTP 200 envelope. On the other routes, the web server stops the body itself and returns a bare 413 with no envelope. -
Windows applies both limits, and both are reachable. The request limit
applies to every route that reads a body —
POST /transcribe,POST /post-process,POST /modesandPATCH /modes/{id}— and each one answers with the HTTP 200 envelope. The audio limit applies to the decoded bytes ofaudio_base64and to afilepath, so afilepath above 48 MiB is refused here and on Linux but accepted on macOS. Windows accepts no multipart request. Older Windows builds used neither value. Their web server kept its own default of 30,000,000 bytes (approximately 28.6 MiB), the refusal arrived as HTTP 400 with the messageInvalid JSON body— which a client could not tell from malformed JSON — and there was no audio limit at all. Read thecodeand themessage, and keep your client tolerant of both answers if it must support older builds.
PAYLOAD_TOO_LARGE is not one
of the codes in the closed set, and a client that shares the macOS decoder
cannot read an envelope that contains it. Read the code, not the status.
Endpoints
The full schema is in
openapi.yaml.
