> For the full page index, fetch https://echonova.ai/llms.txt

# Speech to Text formats and output

What the upload endpoints accept, which checks run before a job starts, every field the responses carry, speaker identification, the TXT, SRT and JSON downloads, and what durationSeconds means.

This page is the lookup table for the two Speech to Text upload endpoints,
`POST /v1/speech-to-text/sync` and `POST /v1/speech-to-text`: what a request
must satisfy, which audio decodes, every field the responses carry, and
what the transcript does and does not include. It also covers speaker
identification and `GET /v1/speech-to-text/jobs/{jobId}/export`, which
downloads a finished job as TXT, SRT subtitles or JSON.

## Accepted input

Both endpoints take one `multipart/form-data` part named `file`. The API runs
these checks, in this order, before any credits are charged or any
transcription starts:

| Check        | Rule                                                                        | Rejection                                                     | Status |
| ------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------- | ------ |
| Size         | 150 MB or less                                                              | `File too large`                                              | `413`  |
| Part present | A part named `file` exists                                                  | `File is required`                                            | `400`  |
| Content type | The part's `Content-Type` starts with `audio/`, compared case-insensitively | `Unsupported file type: <type>. Please upload an audio file.` | `400`  |
| Duration     | 10 minutes or less, on both endpoints                                       | `Audio exceeds the 10-minute limit.`                          | `400`  |

The size check runs while the body is being received, so an oversized
upload is cut off before the other checks run. The duration check reads the
file's metadata; when the duration cannot be read, the check passes and the
file is accepted.

The `<type>` in the content-type rejection is whatever your client put on the
part. `application/octet-stream` means the client guessed a generic type;
`text/plain` means the part carried no `Content-Type` header at all. Set the
type on the part yourself:

:::tabs

```bash title="cURL"
curl -X POST "https://api.echonova.ai/v1/speech-to-text/sync" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@audio.wav;type=audio/wav"
```

```python
import requests

with open("audio.wav", "rb") as audio:
    response = requests.post(
        "https://api.echonova.ai/v1/speech-to-text/sync",
        headers={"Authorization": "Bearer YOUR_API_KEY"},
        files={"file": ("audio.wav", audio, "audio/wav")},
    )
print(response.json())
```

```javascript
import { readFile } from 'node:fs/promises';

const formData = new FormData();
formData.append('file', new Blob([await readFile('audio.wav')], { type: 'audio/wav' }), 'audio.wav');

const response = await fetch('https://api.echonova.ai/v1/speech-to-text/sync', {
  method: 'POST',
  headers: { Authorization: 'Bearer YOUR_API_KEY' },
  body: formData,
});
console.log(await response.json());
```

:::

| Container | `Content-Type` to send |
| --------- | ---------------------- |
| WAV       | `audio/wav`            |
| MP3       | `audio/mpeg`           |
| OGG       | `audio/ogg`            |

## Which audio decodes

The checks above read the type label, not the bytes. Decoding happens after
them: the audio is resampled to 16 kHz mono, and the channels of a stereo
file are averaged rather than one being kept.

WAV, FLAC, OGG and MP3 decode. Before you rely on M4A, MP4, 3GP, WebM or any
other container, send a short test file. A file that cannot be decoded passes
every check above and fails afterwards, in one of two ways:

| Endpoint                       | What you see                                                                                                                                                                           | Credits  |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `POST /v1/speech-to-text/sync` | `503` with `Transcription is temporarily unavailable. Please try again.` The reason is not included.                                                                                   | Refunded |
| `POST /v1/speech-to-text`      | The job ends as `failed` and its `error` field holds the reason, which starts with `Audio could not be decoded. Supported: WAV, FLAC, OGG/Opus, MP3…`. A `job.failed` webhook is sent. | Refunded |

Audio shorter than 50 ms is rejected with a message that starts with
`Audio is too short` followed by the measured duration. This surfaces the
same two ways.

To learn why a file fails, send it to `POST /v1/speech-to-text`: the job's
`error` field carries the reason, and the synchronous endpoint does not.

## Language

Both upload endpoints take an optional `language` form field. Send `auto`
(default), `ar` or `en`. The value is trimmed and read without regard to case,
so `AR` and `en` are accepted. Any other value is a `400` with the message
`language must be one of: auto, ar, en`. Other extra form fields are ignored.

| Value  | Use for                                                                                                             |
| ------ | ------------------------------------------------------------------------------------------------------------------- |
| `auto` | Letting the service decide. That is Arabic today, which also handles mixed Arabic/English and clean English speech. |
| `ar`   | Arabic and mixed Arabic/English speech.                                                                             |
| `en`   | Speech that is all English. Arabic sent as `en` comes back garbled.                                                 |

## Speaker identification

Both upload endpoints take an optional `identify_speakers` form field. Send
`true` to learn who speaks when: the finished job then also carries the
transcript as timed `segments`, each tagged with a `speaker`, and a
`speakers` list with each speaker's talk time. The default is `false`. The
value is trimmed and read without regard to case; `true`, `false`, `1` and
`0` are accepted, and an empty value counts as `false`. Any other value is a
`400` with the message `identify_speakers must be true or false`. Asking for
speakers does not change the charge.

:::tabs

```bash title="cURL"
curl -X POST "https://api.echonova.ai/v1/speech-to-text" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@call.mp3;type=audio/mpeg" \
  -F "language=ar" \
  -F "identify_speakers=true"
```

```python
import requests

with open("call.mp3", "rb") as audio:
    response = requests.post(
        "https://api.echonova.ai/v1/speech-to-text",
        headers={"Authorization": "Bearer YOUR_API_KEY"},
        files={"file": ("call.mp3", audio, "audio/mpeg")},
        data={"language": "ar", "identify_speakers": "true"},
    )
print(response.json())
```

```javascript
import { readFile } from 'node:fs/promises';

const formData = new FormData();
formData.append('file', new Blob([await readFile('call.mp3')], { type: 'audio/mpeg' }), 'call.mp3');
formData.append('language', 'ar');
formData.append('identify_speakers', 'true');

const response = await fetch('https://api.echonova.ai/v1/speech-to-text', {
  method: 'POST',
  headers: { Authorization: 'Bearer YOUR_API_KEY' },
  body: formData,
});
console.log(await response.json());
```

:::

Speakers are named `spk0`, `spk1` and so on, numbered in the order they
first speak. The same id is used on every segment and in `speakers`, so you
can count, colour or label the speakers yourself. The
[download](#download-as-txt-srt-or-json) turns the ids into `Speaker 1`,
`Speaker 2` and so on.

A segment:

| Field     | Type             | Value                                                                                       |
| --------- | ---------------- | ------------------------------------------------------------------------------------------- |
| `start`   | number           | Where the piece starts, in seconds from the start of the audio.                             |
| `end`     | number           | Where the piece ends, in seconds from the start of the audio.                               |
| `text`    | string           | What was said in this piece. Numbers are written as in `transcription`.                     |
| `speaker` | string or `null` | The id of the speaker, such as `spk0`. `null` when this piece could not be given a speaker. |

Segments are sorted by `start`. A speaker:

| Field      | Type   | Value                                    |
| ---------- | ------ | ---------------------------------------- |
| `id`       | string | The speaker id the segments use.         |
| `talkTime` | number | How long this speaker talks, in seconds. |

`identifySpeakers` on the job records what you asked for, not what came
back. Speaker identification may not be available to you yet. Until it is,
a job made with `identify_speakers=true` completes with its full transcript,
and `segments` and `speakers` are empty. A recording whose speakers could
not be told apart comes back the same way. To know whether a job has
speaker data, check that `segments` is not empty, not `identifySpeakers`.
Jobs created before the field existed read `false`, `[]` and `[]`.

The `job.completed` webhook carries the `transcription`, not the segments
or the speakers. Get the job, or download it, to read them.

## Response fields

Every response is wrapped in the standard envelope; the fields below are
inside `data`. A successful `POST` returns HTTP `200`.

### Synchronous transcription

`POST /v1/speech-to-text/sync` returns the finished job:

| Field              | Type             | Value                                                                                                                                                |
| ------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `jobId`            | string           | The id of the job the call created.                                                                                                                  |
| `status`           | string           | `completed`.                                                                                                                                         |
| `transcription`    | string           | The transcript.                                                                                                                                      |
| `durationSeconds`  | number or `null` | Length of the audio in seconds, with decimals.                                                                                                       |
| `creditsConsumed`  | number           | Credits charged for the call.                                                                                                                        |
| `language`         | string           | The language the transcription was requested with.                                                                                                   |
| `numbersAsDigits`  | boolean          | Whether spoken numbers were written as digits.                                                                                                       |
| `identifySpeakers` | boolean          | Whether the call was made with `identify_speakers=true`.                                                                                             |
| `segments`         | array            | The transcript in timed pieces, each with its speaker. Empty unless the job has speaker data. See [Speaker identification](#speaker-identification). |
| `speakers`         | array            | The speakers heard, in order of first appearance. Empty unless the job has speaker data.                                                             |

```json
{
  "jobId": "66e3f2a1c9d4b8a1f0e12345",
  "status": "completed",
  "transcription": "مرحبا بكم في إيكونوڤا",
  "durationSeconds": 15.51,
  "creditsConsumed": 26,
  "language": "ar",
  "numbersAsDigits": true,
  "identifySpeakers": false,
  "segments": [],
  "speakers": []
}
```

### Asynchronous transcription

`POST /v1/speech-to-text` returns only the job id and its state:

| Field    | Type   | Value                                   |
| -------- | ------ | --------------------------------------- |
| `jobId`  | string | The id to poll.                         |
| `status` | string | `processing` once the file is accepted. |

### Job

`GET /v1/speech-to-text/jobs/{jobId}` returns one job, and
`GET /v1/speech-to-text/jobs` returns `items`, a list of these, plus `total`,
the number of API jobs in the workspace. Lists are sorted newest first and
paged with `offset` (0 or more, default 0) and `limit` (1 to 100, default
20).

| Field              | Type             | Value                                                                                                                                                                          |
| ------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`               | string           | The job id.                                                                                                                                                                    |
| `status`           | string           | `pending`, `processing`, `completed` or `failed`.                                                                                                                              |
| `transcription`    | string           | The transcript. Present only once the job is `completed`; absent before that and on a failed job.                                                                              |
| `durationSeconds`  | number or `null` | See below.                                                                                                                                                                     |
| `creditsConsumed`  | number           | Credits charged when the job was created. Unchanged by a refund; a failed job keeps the number it was charged.                                                                 |
| `error`            | string           | Present only on a failed job. It holds the reason the job failed; a job that stayed `processing` for more than 10 minutes carries `Job timed out — AI server did not respond`. |
| `language`         | string           | The language the transcription was requested with; `auto` for jobs created before the field existed.                                                                           |
| `numbersAsDigits`  | boolean          | Whether spoken numbers were written as digits; `true` for jobs created before the field existed.                                                                               |
| `identifySpeakers` | boolean          | Whether the job was made with `identify_speakers=true`; `false` for jobs created before the field existed.                                                                     |
| `segments`         | array            | The transcript in timed pieces, each with its speaker. Empty unless the job has speaker data, and always empty before the job is `completed`.                                  |
| `speakers`         | array            | The speakers heard, in order of first appearance. Empty unless the job has speaker data.                                                                                       |
| `createdAt`        | string           | ISO 8601 timestamp of when the job was created.                                                                                                                                |

```json
{
  "id": "66e3f2a1c9d4b8a1f0e12346",
  "status": "completed",
  "transcription": "السلام عليكم، كيف حالك؟ بخير، الحمد لله.",
  "durationSeconds": 6.4,
  "creditsConsumed": 11,
  "language": "ar",
  "numbersAsDigits": true,
  "identifySpeakers": true,
  "segments": [
    { "start": 0.38, "end": 4.12, "text": "السلام عليكم، كيف حالك؟", "speaker": "spk0" },
    { "start": 4.3, "end": 6.4, "text": "بخير، الحمد لله.", "speaker": "spk1" }
  ],
  "speakers": [
    { "id": "spk0", "talkTime": 3.74 },
    { "id": "spk1", "talkTime": 2.1 }
  ],
  "createdAt": "2026-09-15T10:21:04.512Z"
}
```

Any API key in the workspace with Speech to Text access can list, get and
delete its jobs, even jobs made by a key that was later deleted. The job
endpoint returns `404` with `STT Job not found` for a job of another workspace
or one made in the portal.

## What durationSeconds is

`durationSeconds` is the length of the audio in seconds. Where the number
comes from depends on the endpoint and the job's state:

| Where                                            | Source of the value                                                                                                                                                    |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Synchronous response                             | The length measured while transcribing. If none was measured, the duration read from the file's metadata, or the size-based estimate below, rounded to a whole second. |
| Asynchronous job while `pending` or `processing` | An estimate read from the file's metadata before the job started. When the metadata carries no duration, the file size in bytes divided by 16 000, rounded up.         |
| Asynchronous job once `completed`                | The length measured while transcribing, replacing the estimate.                                                                                                        |

Credits are charged per second of audio, 100 credits a minute. The charge is computed from the
metadata duration, or the size-based estimate, before the job starts, and it
is rounded to a whole credit, at least 1. The measured
`durationSeconds` is usually the same length, but `creditsConsumed` is always
the number actually charged; use it, not `durationSeconds`, when you
reconcile spend. The rate is on the
[Credits](/docs/developers/credits) page.

Write files with a proper header so the duration can be read. A file with
no readable duration is priced from its size, and that charge is not
corrected afterwards.

**Recommended**

- `audio/wav`, `audio/mpeg` or `audio/ogg` files written by an encoder, with
  a complete header and a known duration.

**Less effective**

- A raw stream captured from a socket and uploaded as `audio/wav` with no
  header, priced at one second per 16 000 bytes whatever its real length.

## What the transcript includes

The transcript is one string, and the duration is one number. A job made
with `identify_speakers=true` that has speaker data also carries the
transcript as timed segments with a speaker on each; see
[Speaker identification](#speaker-identification). No response carries word
timestamps or confidence scores. Only the language you requested comes back,
as `language` on the synchronous response and on a job. If you need
timestamps for a job without speaker data, split the audio on your side
before you upload it and keep the offsets.

## Download as TXT, SRT or JSON

`GET /v1/speech-to-text/jobs/{jobId}/export` returns a completed job as a
file. Unlike the other endpoints, a success is the file itself, not the
`{ data, ... }` envelope. An error is still the usual error object, so check
the status before you save the body. The endpoint reference is
[Download a completed STT job](/docs/reference/stt-export-job).

| Query parameter | Required | Values                                                                                                                        |
| --------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `format`        | Yes      | `txt`, `srt` or `json`.                                                                                                       |
| `lang`          | No       | `en` (default) or `ar`, the language of the speaker labels: `Speaker 1` or `المتحدث 1`. It does not translate the transcript. |

:::tabs

```bash title="cURL"
curl "https://api.echonova.ai/v1/speech-to-text/jobs/JOB_ID/export?format=srt&lang=en" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o call.srt
```

```python
import requests

response = requests.get(
    "https://api.echonova.ai/v1/speech-to-text/jobs/JOB_ID/export",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    params={"format": "srt", "lang": "en"},
)
response.raise_for_status()
with open("call.srt", "wb") as out:
    out.write(response.content)
```

```javascript
import { writeFile } from 'node:fs/promises';

const response = await fetch('https://api.echonova.ai/v1/speech-to-text/jobs/JOB_ID/export?format=srt&lang=en', {
  headers: { Authorization: 'Bearer YOUR_API_KEY' },
});
if (!response.ok) throw new Error((await response.json()).errorMessage);
await writeFile('call.srt', await response.text());
```

:::

| `format` | `Content-Type`                        | Body                                                        |
| -------- | ------------------------------------- | ----------------------------------------------------------- |
| `txt`    | `text/plain; charset=utf-8`           | Exactly the job's `transcription`.                          |
| `srt`    | `application/x-subrip; charset=utf-8` | SubRip subtitles, built as described below.                 |
| `json`   | `application/json; charset=utf-8`     | The transcript, the speakers and the segments, shown below. |

Every download carries `Content-Disposition: attachment` and a file name
made from the uploaded file's name, with the audio extension swapped for the
format: `call.mp3` downloads as `call.srt`. A name that is not plain ASCII,
such as an Arabic one, also comes as an RFC 5987 `filename*` in UTF-8, next
to an ASCII `filename` for older clients. Browser code that calls the API
from another origin can read the header.

```http
Content-Disposition: attachment; filename="transcript.srt"; filename*=UTF-8''%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A9.srt
```

Any key in the workspace with Speech to Text access can download its API
jobs, as with `GET /v1/speech-to-text/jobs/{jobId}`. Only a `completed` job
has a file; wait for `job.completed` or poll the job first.

### SRT

The cues are built from the job's segments:

- Each cue lasts at most 7 seconds and holds at most two lines of at most 42
  characters. Characters are counted as a reader counts them, not as bytes,
  so Arabic gets the same room as English.
- A longer segment is split at sentence and clause punctuation first
  (`.` `!` `?` `؟` `،` `,` `;` `؛` `…` `:`), then between words. A word is
  never split, and the text is never changed except for spaces and line
  breaks.
- The cues of one segment share its time in proportion to their length in
  characters. There are no word timings, so cue times inside a segment are
  estimates: close enough to read along, not exact to the frame. The cues
  start and end where the segment does and never overlap.
- When a segment has a speaker, each of its cues starts with the speaker's
  label in brackets: `[Speaker 1]` with `lang=en`, `[المتحدث 1]` with
  `lang=ar`. Speakers are numbered 1, 2, 3 in the order they first speak.
  The label counts toward the line length.
- A job with no segments, because speakers were not asked for or not
  identified, downloads as one cue from `00:00:00,000` to its
  `durationSeconds`, holding the whole transcript.
- The file is UTF-8 with `\n` line endings, `HH:MM:SS,mmm` timestamps and a
  blank line between cues.

Three segments, the last one 12.7 seconds long, download with `lang=en` as:

```text
1
00:00:00,380 --> 00:00:04,120
[Speaker 1] السلام عليكم، كيف حالك؟

2
00:00:04,300 --> 00:00:06,400
[Speaker 2] بخير، الحمد لله.

3
00:00:06,900 --> 00:00:11,425
[Speaker 2] عندي سؤال عن الفاتورة الأخيرة،

4
00:00:11,425 --> 00:00:15,659
[Speaker 2] المبلغ أعلى من الشهر الماضي.

5
00:00:15,659 --> 00:00:19,600
[Speaker 2] هل يمكنك أن تشرح لي السبب؟
```

### JSON

The JSON download keeps the segments as they are on the job, not split into
cues, and adds the labels:

| Field             | Type             | Value                                                                                                                       |
| ----------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `id`              | string           | The job id.                                                                                                                 |
| `fileName`        | string           | The uploaded file's name.                                                                                                   |
| `durationSeconds` | number or `null` | As on the job.                                                                                                              |
| `language`        | string or `null` | The language the transcription was requested with.                                                                          |
| `text`            | string           | The transcript, the same as `transcription`.                                                                                |
| `speakers`        | array            | `id`, `label` and `talkTime` of each speaker, numbered as in the SRT. Empty when the job has no speaker data.               |
| `segments`        | array            | `start`, `end`, `speaker`, `speakerLabel` and `text` of each segment. `speaker` and `speakerLabel` are `null` when unknown. |

```json
{
  "id": "66e3f2a1c9d4b8a1f0e12346",
  "fileName": "call.mp3",
  "durationSeconds": 19.6,
  "language": "ar",
  "text": "السلام عليكم، كيف حالك؟ بخير، الحمد لله. عندي سؤال عن الفاتورة الأخيرة، المبلغ أعلى من الشهر الماضي. هل يمكنك أن تشرح لي السبب؟",
  "speakers": [
    {
      "id": "spk0",
      "label": "Speaker 1",
      "talkTime": 3.74
    },
    {
      "id": "spk1",
      "label": "Speaker 2",
      "talkTime": 14.8
    }
  ],
  "segments": [
    {
      "start": 0.38,
      "end": 4.12,
      "speaker": "spk0",
      "speakerLabel": "Speaker 1",
      "text": "السلام عليكم، كيف حالك؟"
    },
    {
      "start": 4.3,
      "end": 6.4,
      "speaker": "spk1",
      "speakerLabel": "Speaker 2",
      "text": "بخير، الحمد لله."
    },
    {
      "start": 6.9,
      "end": 19.6,
      "speaker": "spk1",
      "speakerLabel": "Speaker 2",
      "text": "عندي سؤال عن الفاتورة الأخيرة، المبلغ أعلى من الشهر الماضي. هل يمكنك أن تشرح لي السبب؟"
    }
  ]
}
```

### Download errors

| Status | `errorMessage`                                               | When                                                                                      |
| ------ | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| `400`  | `format must be one of the following values: txt, srt, json` | `format` is missing or holds another value, such as `vtt`.                                |
| `400`  | `lang must be one of the following values: en, ar`           | `lang` holds something other than `en` or `ar`.                                           |
| `400`  | `Job is not completed`                                       | The job is `pending`, `processing` or `failed`.                                           |
| `400`  | `Invalid identifier`                                         | `jobId` is not 24 hexadecimal characters.                                                 |
| `400`  | `property <name> should not exist`                           | The query carries a parameter other than `format` and `lang`.                             |
| `404`  | `STT Job not found`                                          | The job id is unknown, or the job belongs to another workspace or was made in the portal. |

The authentication and rate-limit errors of every endpoint apply too; they
are on [Errors](/docs/developers/errors#downloading-transcripts). Downloading
is not charged.

## Numbers

Spoken Arabic numbers are written as digits by default: a phone number said
as `صفر واحد واحد خمسة` comes back as `0115`, and `مية وخمسين جنيه` as
`150 جنيه`. Words that are also ordinary Arabic stay words, so `واحد صاحبي`
("a friend of mine") is not turned into `1`.

To keep the numbers exactly as they were said, send `numbers_as_digits=false`
with the upload. English transcripts are never converted. The job reports the
setting it was made with as `numbersAsDigits`.

## FAQ

### Why did a bad file give me 503 on one endpoint and a failed job on the other?

Both endpoints refund the charge. The synchronous endpoint reports every
transcription failure as `503` without a reason; the asynchronous job
carries the reason in `error`. Use `POST /v1/speech-to-text` when you need
to know why.

### Is a rejected upload charged?

No. Every check in the table above runs before any credits are charged.

### Which language is the transcript in?

Send `language`: `auto` (default) lets the service decide. That is Arabic today, which also handles mixed Arabic/English and clean English speech. Use `ar` for Arabic and mixed speech. Use `en` only for speech that is all English. Arabic sent as `en` comes back garbled. Any other value is a 400.

### Can I get timestamps or speaker labels?

Send `identify_speakers=true` with the upload. A job with speaker data
carries `segments`, each with a start, an end and a speaker, and
`GET /v1/speech-to-text/jobs/{jobId}/export?format=srt` turns them into
subtitles with speaker labels. There are no word timestamps.

### Why are segments and speakers empty when I sent identify_speakers=true?

Speaker identification may not be available to you yet, or the speakers in
the recording could not be told apart. The transcript is complete either
way, and the SRT download holds one cue for the whole file.

### Can I download WebVTT subtitles?

No. The download formats are `txt`, `srt` and `json`; `format=vtt` is a
`400`.

### Why is durationSeconds different from what I was charged for?

The charge is computed before the job starts, from the file's metadata or
its size. `durationSeconds` on a completed job is the length measured
during transcription. Reconcile spend on `creditsConsumed`.

## Related

- [Speech to Text quickstart](/docs/developers/stt-quickstart)
- [Transcribe audio synchronously](/docs/reference/stt-transcribe-sync)
- [Transcribe audio file](/docs/reference/stt-transcribe)
- [Get STT job details](/docs/reference/stt-get-job)
- [Download a completed STT job](/docs/reference/stt-export-job)
- [List Speech to Text jobs](/docs/reference/stt-list-jobs)
- [Credits](/docs/developers/credits)
- [Webhook events](/docs/developers/webhooks-events)
