Develop / Reference
Audio
Transcribes audio files and generates speech output in the OpenAI format; neither endpoint is inspected by Noirdoc Shield.
Both endpoints accept only POST. The gateway answers other methods with 405 method_not_allowed. Neither endpoint supports streaming.
Transcription
/v1/audio/transcriptions - Family
- stt
- Shield
- no
- Streaming
- no
- Format
- multipart/form-data
- Billing
- audio duration
The body is a multipart form, as with the OpenAI Audio API. model is required and must be a model of the stt family from GET /v1/models. The fields model, stream and response_format may each appear only once.
Audio counts as file content. The endpoint works only if admins of your organisation have turned on Allow file and image attachments in requests under Models → Shield (Advanced Settings). Otherwise, the gateway answers with 403 file_content_not_allowed.
The body may be at most 26 MB. The gateway rejects larger requests with 413 request_too_large before forwarding them.
curl https://api.noirdoc.de/v1/audio/transcriptions \
-H "Authorization: Bearer $NOIRDOC_API_KEY" \
-F model=whisper \
-F file=@dictation.mp3import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.noirdoc.de/v1",
api_key=os.environ["NOIRDOC_API_KEY"],
)
with open("dictation.mp3", "rb") as audio:
transcript = client.audio.transcriptions.create(
model="whisper", file=audio
)
print(transcript.text)import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.noirdoc.de/v1",
apiKey: process.env.NOIRDOC_API_KEY,
});
const transcript = await client.audio.transcriptions.create({
model: "whisper",
file: fs.createReadStream("dictation.mp3"),
});
console.log(transcript.text);What Noirdoc changes
| Area | Behaviour |
|---|---|
| Model | The gateway resolves the model ID through the catalogue and uses it to select the provider. It forwards the audio file unchanged. |
response_format | With json, text or no value, the gateway requests verbose_json from the provider so it can bill the audio duration. You receive the format you requested: an object with only text for json, plain text for text. If the provider reports no duration, you receive its verbose_json response unchanged. The gateway passes verbose_json, srt, vtt and other formats through unchanged. |
stream | If you set stream to true, 1, yes or on, the gateway answers with 400 streaming_not_supported_for_endpoint. |
| Billing | The gateway bills by the audio duration that the provider reports in verbose_json. |
| Provider | The X-Noirdoc-Provider header in the response names the provider. |
Speech output
/v1/audio/speech - Family
- tts
- Shield
- no
- Streaming
- no
- Format
- JSON, response as audio
- Billing
- characters in input
The body is JSON, as with the OpenAI Audio API. model is required and must be a model of the tts family from GET /v1/models.
input must be a non-empty text of at most 4,096 characters. voice must be a non-empty string. The provider determines which voices a model offers. The gateway answers violations with 400 invalid_tts_request.
The response contains the audio data as the provider delivers it, with the provider’s Content-Type.
curl https://api.noirdoc.de/v1/audio/speech \
-H "Authorization: Bearer $NOIRDOC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "tts",
"voice": "<voice>",
"input": "Your appointment is on Tuesday at 10:00."
}' \
--output appointment.mp3import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.noirdoc.de/v1",
api_key=os.environ["NOIRDOC_API_KEY"],
)
response = client.audio.speech.create(
model="tts",
voice="<voice>",
input="Your appointment is on Tuesday at 10:00.",
)
response.write_to_file("appointment.mp3")import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.noirdoc.de/v1",
apiKey: process.env.NOIRDOC_API_KEY,
});
const response = await client.audio.speech.create({
model: "tts",
voice: "<voice>",
input: "Your appointment is on Tuesday at 10:00.",
});
const audio = Buffer.from(await response.arrayBuffer());
fs.writeFileSync("appointment.mp3", audio);What Noirdoc changes
| Area | Behaviour |
|---|---|
| Model | The gateway resolves the model ID through the catalogue and uses it to select the provider. |
| Streaming | None. If stream is true or stream_format is set, the gateway answers with 400 streaming_not_supported_for_endpoint. |
| Billing | The gateway counts the characters in input and bills them. |
| Provider | The X-Noirdoc-Provider header in the response names the provider. |
Sub-routes
None. The gateway answers other paths under /v1/audio/ with 404 unsupported_endpoint.
Errors on these endpoints
| Status | Code | Meaning |
|---|---|---|
| 400 | model_required | The request names no model (model in the body, or in the form field for /v1/audio/transcriptions). |
| 400 | wrong_endpoint_for_model | The model belongs to a different endpoint family or API format than the endpoint called. |
| 400 | streaming_not_supported_for_endpoint | The request asks for streaming, but the endpoint does not support it. |
| 400 | invalid_request_body | The body is not a JSON object or not encoded in UTF-8. |
| 400 | invalid_tts_request | input or voice is missing or empty, or input is too long. |
| 400 | invalid_multipart_body | The body is not valid multipart/form-data, or model, stream or response_format appears more than once. |
| 402 | insufficient_credit | The organisation's credit is used up or lower than the estimated maximum cost of the request (including Noirdoc Shield, also on BYOK routes when Shield is active). |
| 402 | key_budget_exhausted | This key's budget is used up for the current period or does not cover the estimated cost of the request. |
| 403 | model_not_allowed_for_key | The model is not one of this key's allowed models. |
| 403 | provider_not_allowed_for_tenant | The organisation counts as bound by professional secrecy (§ 203 StGB); this excludes every provider that offers this model. |
| 403 | provider_not_allowed_for_key | The key's restrictions (providers, data residency, CLOUD Act, § 203) exclude every provider of this model. |
| 403 | starter_model_not_allowed | The organisation is still using only its starter credit, and the model or its managed provider is not included in starter credit. |
| 403 | masking_not_supported_for_endpoint | The endpoint supports neither Noirdoc Shield nor legacy masking, yet the policy is enforced or the request sends X-Noirdoc-Mask: on (and on /v1/images/generations also X-Noirdoc-Shield: on). During the legacy masking transition until 30.11.2026, this also affects /v1/embeddings and /v1/rerank on pre-existing organisations that have not switched to Shield yet. |
| 403 | file_content_not_allowed | The request contains files, images or audio, and the organisation does not allow file content. |
| 404 | model_not_available | The model ID is not available to the organisation. |
| 405 | method_not_allowed | The endpoint accepts only POST. |
| 413 | request_too_large | The body exceeds the endpoint's size limit. |
| 502 | provider_unreachable | The gateway could not connect to the provider, or the connection dropped. |
| 504 | provider_timeout | The provider did not answer within the gateway's timeout. The response contains Retry-After with 10 seconds. |
OpenAI reference
OpenAI’s API reference describes all other request and response fields: Audio (checked on 30 September 2026).