transcribe
Transcribe an audio or video file to VTT, SRT, plain text, or verbose JSON via OpenAI, an OpenAI-compatible endpoint, or Azure OpenAI.
Parameters
- pathstringconditional
- Path to the audio/video file to transcribe. Supports
${ctx.key}interpolation. Mutually exclusive withsource_key; exactly one of the two is required. - source_keystringconditional
- Context key holding a file path, artifact URI, or artifact descriptor. Mutually exclusive with
path. - providerstringdefault "openai"
- Transcription backend:
"openai","openai_compatible", or"azure". - formatstringdefault "vtt"
- Transcript format:
"vtt","srt","text", or"json". See [Formats](#formats) below. - modelstringdefault "whisper-1"
- Model name. Supports
${ctx.key}interpolation; has no environment-variable fallback. Forprovider = "azure"this is the **deployment name**, not a model ID — see [Azure](#azure). - api_keystringconditional
- API key/token. Required via config or the fallback environment variable for every provider — see [Environment Variable Fallbacks](#environment-variable-fallbacks).
- base_urlstringconditionaldefault provider-dependent
- Base URL of the transcription endpoint. Falls back to an environment variable. Defaults to
https://api.openai.com/v1foropenai/openai_compatiblewhen neither is set;azurehas no default and fails without one. - api_versionstringconditional
- Azure
api-versionquery parameter. Required (via config orAZURE_OPENAI_API_VERSION) only whenprovider = "azure"; not used otherwise. - languagestring
- ISO-639-1 language hint forwarded to the provider. Supports
${ctx.key}interpolation; has no environment-variable fallback. - promptstring
- Free-text hint to bias vocabulary or style, forwarded to the provider as-is. Supports
${ctx.key}interpolation; has no environment-variable fallback. - temperaturenumber
- Sampling temperature forwarded to the provider. Accepts a finite number from
0through1inclusive, or numeric${ctx.key}interpolation in that range. - output_keystringdefault "transcript"
- Prefix for context output keys.
- output_filestring
- When set, the raw provider response body is written to this path and
{output_key}_pathis added to the output. Supports${ctx.key}interpolation. - timeoutnumberdefault 120
- Request timeout in seconds. Accepts a finite number or numeric
${ctx.key}interpolation and must be greater than zero.
The audio file is read from disk before it is uploaded, capped at
IRONFLOW_MAX_AUDIO_BYTES (default 25,000,000 bytes / 25 MB). A file over the
cap fails the step before any network request is made. The opened handle must
be a regular file. Unix non-blocking/no-follow open rejects a FIFO, device,
directory, or final symlink without reading it; Windows refuses final reparse
points. The provider response is
independently capped at IRONFLOW_MAX_TRANSCRIBE_RESPONSE_BYTES (default
26,214,400 bytes / 25 MiB). IronFlow checks Content-Length when present and
also enforces the cap chunk by chunk, so a chunked or misreported response
cannot be buffered without bound. Oversized success and error bodies fail
before parsing or output_file handling. See
CLI_REFERENCE.md for both variables.
Artifact inputs retain their identity until the tracked blocking reader opens them. IronFlow hashes the opened handle, compares it with the URI/descriptor, rewinds it, and reads the upload bytes from that same handle; it never flattens an artifact into a trusted store pathname.
Provider redirects are followed only when the target keeps the original scheme, host, and effective port. A cross-origin redirect fails the step before IronFlow can replay the request, preventing the API key or uploaded media from being forwarded to another origin. Same-origin redirect chains are limited to 10 hops.
Formats
format | Provider response_format sent | Output value |
|---|---|---|
"vtt" (default) | vtt | Raw WebVTT text |
"srt" | srt | Raw SubRip text |
"text" | text | Raw plain text |
"json" | verbose_json | Parsed JSON object |
format = "json" does not request the provider's bare json mode — it requests verbose_json. The returned value is therefore a full object that typically includes text, segments (per-segment timing), duration, and the detected language, not just a transcript string. The exact fields present depend on what the provider returns; IronFlow parses the body as JSON and passes it through unchanged. Before materializing that JSON, IronFlow streams over it once and enforces IRONFLOW_MAX_CONVERSION_DEPTH and IRONFLOW_MAX_CONVERSION_NODES. This prevents a compact response with extreme nesting or many tiny values from expanding into an unbounded in-memory tree; tune those shared conversion limits only when a legitimate verbose transcript needs more room.
The requested format is always echoed back on {output_key}_format using its own label ("vtt", "srt", "text", or "json") — {output_key}_format reads "json" even though the wire request used verbose_json.
Environment Variable Fallbacks
| Config Key | Environment Variable | Provider |
|---|---|---|
api_key | OPENAI_API_KEY | openai / openai_compatible |
base_url | OPENAI_BASE_URL | openai / openai_compatible |
api_key | AZURE_OPENAI_API_KEY | azure |
base_url | AZURE_OPENAI_ENDPOINT | azure |
api_version | AZURE_OPENAI_API_VERSION | azure |
api_key and base_url are read through a config-value-then-environment-variable fallback for every provider; azure additionally requires api_version through the same pattern. model, language, and prompt have no environment-variable fallback — they are config-only (with ${ctx.key} interpolation) to keep the environment contract limited to credentials and endpoints.
Azure
For provider = "azure", model is not a model identifier — it is the deployment name, and it is placed directly in the request URL path:
{base_url}/openai/deployments/{model}/audio/transcriptions?api-version={api_version}
Because Azure selects the model through the URL, the model field is also omitted from the uploaded form body for this provider (some deployments reject a redundant model field). Always set model explicitly to your deployment name when using Azure — the "whisper-1" default is very unlikely to match a real deployment name.
Azure authenticates with an api-key header instead of the Authorization: Bearer header used by openai/openai_compatible; this node handles that automatically based on provider.
Context Output
With the default output_key of "transcript", the keys are transcript, transcript_format, transcript_model, transcript_success, and — only when output_file is set — transcript_path.
| Key | Type | Description |
|---|---|---|
{output_key} | string or object | The transcript. For vtt/srt/text, the raw response body string. For json, the parsed verbose-JSON object (see Formats). |
{output_key}_format | string | The requested format label: "vtt", "srt", "text", or "json". |
{output_key}_model | string | The model value used for the request. |
{output_key}_success | boolean | Always true. Any failure (missing credentials, transport error, non-2xx response, empty body, unparseable JSON) returns a node error instead. |
{output_key}_path | string | Only present when output_file is set. The path the raw response body was written to. |
Errors from this node never contain the API key: the provider's raw error text is scanned for the exact key value sent on the request and any occurrence is replaced with [REDACTED], in addition to the shared pattern-based credential redaction applied to transport and parse errors.
Examples
OpenAI, plain text
flow:step("transcribe", nodes.transcribe({
path = "${ctx.audio_path}",
format = "text",
output_key = "transcript"
}))
flow:step("log_result", nodes.log({
message = "Transcript: ${ctx.transcript}"
})):depends_on("transcribe")
Azure OpenAI
flow:step("transcribe", nodes.transcribe({
provider = "azure",
path = "${ctx.audio_path}",
model = "my-whisper-deployment", -- deployment name, not a model ID
api_version = "2024-06-01",
output_key = "transcript"
}))
Verbose JSON (segments, duration, detected language)
flow:step("transcribe", nodes.transcribe({
path = "${ctx.audio_path}",
format = "json",
output_key = "transcript"
}))
flow:step("log_result", nodes.log({
message = "Detected language: ${ctx.transcript.language}, duration: ${ctx.transcript.duration}s"
})):depends_on("transcribe")
Writing the transcript to a file for a downstream extractor
flow:step("transcribe", nodes.transcribe({
path = "${ctx.audio_path}",
format = "vtt",
output_key = "transcript",
output_file = "${ctx.audio_path}.vtt",
timeout = 300
})):retries(2, 2):timeout(300)
-- extract_vtt reads a file, which is why transcribe wrote one.
flow:step("cues", nodes.extract_vtt({
path = "${ctx.transcript_path}",
cues_key = "cues"
})):depends_on("transcribe")