{"openapi":"3.1.1","info":{"title":"transcrevo API","description":"Audio transcription API.\n\nAuthenticate every request with `Authorization: Bearer tk_...` (your key is created with your account).\n\n**Quickstart**\n\n1. `POST /v1/transcripts` with `{\"url\": \"https://example.com/audio.mp3\"}`. It returns `201` with an `id` and `status: \"processing\"`.\n2. `GET /v1/transcripts/{id}` until `status` is `done` (text in `text`) or `failed` (reason in `error`). Pass `webhookUrl` on creation to be called instead of polling.\n\nFor a local file, `POST /v1/uploads` first and send the returned id as `uploadId` instead of `url`.\n\nEvery response is `{\"data\": ...}` on success or `{\"error\": {\"code\", \"message\"}}` on failure; `message` always explains the `code`. You are charged on completion, by audio duration; failed transcripts are free.","version":"1.0.0"},"servers":[{"url":"https://api.transcrevo.com"}],"tags":[{"name":"Transcripts","description":"Create and poll transcriptions"},{"name":"Uploads","description":"Send a file, in one request or in chunks"},{"name":"Webhooks","description":"Delivery history of completion notices, and sending one again"},{"name":"Account","description":"Credits, limits and current rate"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key (tk_...)"}},"schemas":{"Transcript":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","status","text","words","utterances","diarizationSegments","error","language","durationSeconds","speakerCount","cost","currency","model","url","retentionDays","externalId","metadata","createdAt"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["processing","done","failed","canceled"]},"text":{"description":"The transcribed text. null until status is done","anyOf":[{"type":"string"},{"type":"null"}]},"words":{"description":"Every word with its timing and speaker. null until status is done","anyOf":[{"type":"array","items":{"type":"object","required":["start","end","text","speaker"],"properties":{"start":{"description":"Start offset from the beginning of the audio, in SECONDS as a decimal number (1.5 is one and a half seconds, never 1500 ms)","type":"number"},"end":{"description":"End offset, in seconds, on the same scale as `start`","type":"number"},"text":{"type":"string"},"speaker":{"description":"Speaker label (\"A\", \"B\", …) when diarization was requested; null otherwise","anyOf":[{"type":"string"},{"type":"null"}]}}}},{"type":"null"}]},"utterances":{"description":"The same transcript grouped into blocks of continuous speech by one speaker: the unit to READ, to subtitle and to show. `words` stays the unit to search and to seek. null until status is done","anyOf":[{"type":"array","items":{"type":"object","required":["start","end","text","speaker","overlap"],"properties":{"start":{"description":"Start of the block, in seconds","type":"number"},"end":{"description":"End of the block, in seconds","type":"number"},"text":{"type":"string"},"speaker":{"description":"Speaker label (\"A\", \"B\", …), or null when speakers were not requested","anyOf":[{"type":"string"},{"type":"null"}]},"overlap":{"description":"True when another speaker was talking during part of this block. Those stretches are where speaker attribution is least reliable","type":"boolean"}}}},{"type":"null"}]},"diarizationSegments":{"description":"The speaker timeline as the diarizer produced it, before words were labeled. Overlapping speech survives here (a segment with two names) and is the one thing `words[].speaker` cannot express. null when speakers were not requested, and on transcripts made before this field existed","anyOf":[{"type":"array","items":{"type":"object","required":["start","end","speakers"],"properties":{"start":{"type":"number"},"end":{"type":"number"},"speakers":{"description":"Everyone speaking in this stretch. More than one name is overlapping speech, which `words[].speaker` cannot represent: there, each word gets a single label","type":"array","items":{"type":"string"}}}}},{"type":"null"}]},"error":{"description":"Set only when status is failed; null otherwise","anyOf":[{"description":"Why it failed. Nothing is ever charged for a failed transcript. One of:\n\n`audio_too_long`: The audio is longer than the 10 hour limit.\n\n`insufficient_balance`: The audio is longer than the remaining balance covers. Top up and submit again; nothing was charged.\n\n`audio_unreachable`: The `url` could not be downloaded.\n\n`audio_invalid`: The audio could not be decoded.\n\n`audio_too_large`: The audio downloaded from `url` exceeds the 2 GiB limit.\n\n`audio_mismatch`: The stored audio does not hash to the `sha256` declared on the upload, so it is not the file you meant to send. Nothing was charged; upload it again.\n\n`engine_failed`: Transcription failed after every retry. Nothing was charged; submit again.","type":"object","required":["code","message"],"properties":{"code":{"anyOf":[{"const":"audio_too_long","type":"string"},{"const":"insufficient_balance","type":"string"},{"const":"audio_unreachable","type":"string"},{"const":"audio_invalid","type":"string"},{"const":"audio_too_large","type":"string"},{"const":"audio_mismatch","type":"string"},{"const":"engine_failed","type":"string"}]},"message":{"type":"string"}}},{"type":"null"}]},"language":{"description":"Detected (or requested) language. null while processing","anyOf":[{"type":"string"},{"type":"null"}]},"durationSeconds":{"description":"Real audio duration, measured on completion. 0 while processing","type":"integer"},"speakerCount":{"description":"How many distinct speakers were found. null when `speakers` was not requested, which is different from 0 (asked, and none were found)","anyOf":[{"type":"integer"},{"type":"null"}]},"cost":{"description":"Cost charged, in the minor unit of `currency`. Fractional: billing is per second at the hourly rate, so a short audio costs less than one cent. 0 while processing, and always 0 on a failed transcript","type":"number"},"currency":{"description":"ISO 4217 currency of `cost`","const":"USD","type":"string"},"model":{"description":"Model that produced the transcript","type":"string"},"url":{"description":"The audio URL you sent. null when the audio came from an upload","anyOf":[{"type":"string"},{"type":"null"}]},"retentionDays":{"description":"How many days the CONTENT of this transcript is kept, counted from creation. After that, `text`, `words` and the stored audio are erased and the transcript reads like a deleted one; the usage record stays, because it is what the invoice is built from. null means it follows the account default","anyOf":[{"type":"integer"},{"type":"null"}]},"externalId":{"description":"Your own id for this audio, exactly as you sent it. Echoed here, in the list and in the webhook, so a delivery is recognizable without a lookup table","anyOf":[{"type":"string"},{"type":"null"}]},"metadata":{"description":"The key/value pairs you sent with the request, returned unchanged. Never interpreted by the API","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},{"type":"null"}]},"createdAt":{"description":"Creation date (ISO 8601)","type":"string"}}}}},"TranscriptList":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["transcripts","nextCursor","total","page","perPage"],"properties":{"transcripts":{"type":"array","items":{"type":"object","required":["id","status","error","language","durationSeconds","speakerCount","cost","currency","model","url","retentionDays","externalId","metadata","createdAt"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["processing","done","failed","canceled"]},"error":{"description":"Set only when status is failed; null otherwise","anyOf":[{"description":"Why it failed. Nothing is ever charged for a failed transcript. One of:\n\n`audio_too_long`: The audio is longer than the 10 hour limit.\n\n`insufficient_balance`: The audio is longer than the remaining balance covers. Top up and submit again; nothing was charged.\n\n`audio_unreachable`: The `url` could not be downloaded.\n\n`audio_invalid`: The audio could not be decoded.\n\n`audio_too_large`: The audio downloaded from `url` exceeds the 2 GiB limit.\n\n`audio_mismatch`: The stored audio does not hash to the `sha256` declared on the upload, so it is not the file you meant to send. Nothing was charged; upload it again.\n\n`engine_failed`: Transcription failed after every retry. Nothing was charged; submit again.","type":"object","required":["code","message"],"properties":{"code":{"anyOf":[{"const":"audio_too_long","type":"string"},{"const":"insufficient_balance","type":"string"},{"const":"audio_unreachable","type":"string"},{"const":"audio_invalid","type":"string"},{"const":"audio_too_large","type":"string"},{"const":"audio_mismatch","type":"string"},{"const":"engine_failed","type":"string"}]},"message":{"type":"string"}}},{"type":"null"}]},"language":{"description":"Detected (or requested) language. null while processing","anyOf":[{"type":"string"},{"type":"null"}]},"durationSeconds":{"description":"Real audio duration, measured on completion. 0 while processing","type":"integer"},"speakerCount":{"description":"How many distinct speakers were found. null when `speakers` was not requested, which is different from 0 (asked, and none were found)","anyOf":[{"type":"integer"},{"type":"null"}]},"cost":{"description":"Cost charged, in the minor unit of `currency`. Fractional: billing is per second at the hourly rate, so a short audio costs less than one cent. 0 while processing, and always 0 on a failed transcript","type":"number"},"currency":{"description":"ISO 4217 currency of `cost`","const":"USD","type":"string"},"model":{"description":"Model that produced the transcript","type":"string"},"url":{"description":"The audio URL you sent. null when the audio came from an upload","anyOf":[{"type":"string"},{"type":"null"}]},"retentionDays":{"description":"How many days the CONTENT of this transcript is kept, counted from creation. After that, `text`, `words` and the stored audio are erased and the transcript reads like a deleted one; the usage record stays, because it is what the invoice is built from. null means it follows the account default","anyOf":[{"type":"integer"},{"type":"null"}]},"externalId":{"description":"Your own id for this audio, exactly as you sent it. Echoed here, in the list and in the webhook, so a delivery is recognizable without a lookup table","anyOf":[{"type":"string"},{"type":"null"}]},"metadata":{"description":"The key/value pairs you sent with the request, returned unchanged. Never interpreted by the API","anyOf":[{"type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},{"type":"null"}]},"createdAt":{"description":"Creation date (ISO 8601)","type":"string"}}}},"nextCursor":{"description":"Pass it back as `cursor` to get the next page. `null` means this was the last one. Paging by cursor never skips or repeats a transcript, which offsets do when new ones arrive while you page","anyOf":[{"type":"string"},{"type":"null"}]},"total":{"description":"Total transcripts matching the filter. `null` when paging by `cursor`: counting the whole history is the cost the cursor exists to avoid","anyOf":[{"type":"integer"},{"type":"null"}]},"page":{"description":"1-based page number, or `null` when paging by `cursor`","anyOf":[{"type":"integer"},{"type":"null"}]},"perPage":{"type":"integer"}}}}},"TranscriptDeleted":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","deleted"],"properties":{"id":{"type":"string"},"deleted":{"const":true,"type":"boolean"}}}}},"Balance":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["currency","total","reserved","available"],"properties":{"currency":{"description":"ISO 4217 currency of the amounts below","const":"USD","type":"string"},"total":{"description":"Credits on the account, in the minor unit of `currency`. Fractional, like every amount here","type":"number"},"reserved":{"description":"Held by transcriptions already running. Released when each one completes and is charged","type":"number"},"available":{"description":"What the next transcription can spend: `total` minus `reserved`. At 0, new transcriptions are refused with `402 insufficient_balance`","type":"number"}}}}},"Upload":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["id","receivedBytes","expectedBytes","sha256","createdAt"],"properties":{"id":{"type":"string"},"receivedBytes":{"description":"Total bytes received so far","type":"integer"},"expectedBytes":{"description":"The size you declared for this file, or null if you did not. While it differs from `receivedBytes`, the upload cannot be transcribed","anyOf":[{"type":"integer"},{"type":"null"}]},"sha256":{"description":"The digest you declared for this file, or null if you did not. Checked against the stored audio before it reaches the GPU","anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"description":"Creation date (ISO 8601)","type":"string"}}}}},"Limits":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["audio","upload","glossary","concurrency","rateLimits","idempotencyKeyMaxChars"],"properties":{"audio":{"type":"object","required":["maxDurationSeconds","formats"],"properties":{"maxDurationSeconds":{"description":"Longest audio accepted in one transcript. Longer fails with `audio_too_long`, never charged","type":"integer"},"formats":{"description":"Container formats the decoder accepts. Video files are accepted for their audio track","type":"array","items":{"type":"string"}}}},"upload":{"type":"object","required":["maxBytes","maxChunkBytes","maxRequestBytes"],"properties":{"maxBytes":{"description":"Largest file, across every chunk of one upload","type":"integer"},"maxChunkBytes":{"description":"Largest single `PUT /v1/uploads/{id}` body","type":"integer"},"maxRequestBytes":{"description":"Largest body in ONE request, enforced at the edge before it reaches the API. A bigger file has to go through the resumable chunk path","type":"integer"}}},"glossary":{"type":"object","required":["maxEntries","maxTermChars"],"properties":{"maxEntries":{"type":"integer"},"maxTermChars":{"type":"integer"}}},"concurrency":{"type":"object","required":["maxRunning"],"properties":{"maxRunning":{"description":"How many transcripts this account can run at once, or null for no limit. The trial limit is lifted by the first top-up","anyOf":[{"type":"integer"},{"type":"null"}]}}},"rateLimits":{"description":"Per-account request limits. Every response also carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`","type":"array","items":{"type":"object","required":["endpoint","max","windowSeconds"],"properties":{"endpoint":{"type":"string"},"max":{"type":"integer"},"windowSeconds":{"type":"integer"}}}},"idempotencyKeyMaxChars":{"type":"integer"}}}}},"WebhookDeliveries":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"type":"object","required":["id","transcriptId","url","attempt","delivered","statusCode","error","responsePreview","durationMs","manual","createdAt"],"properties":{"id":{"type":"string"},"transcriptId":{"description":"The transcript this delivery is about","type":"string"},"url":{"description":"Where it was sent, as given in `webhookUrl`","type":"string"},"attempt":{"description":"1 on the first try. A replay continues the count: it is the same delivery, tried again","type":"integer"},"delivered":{"description":"True only for a 2xx. A redirect, a timeout or any other status is a delivery that did not land","type":"boolean"},"statusCode":{"description":"What your endpoint answered, or null when there was no answer at all (timeout, DNS, refused)","anyOf":[{"type":"integer"},{"type":"null"}]},"error":{"description":"What stopped the request on our side. null when your endpoint answered","anyOf":[{"type":"string"},{"type":"null"}]},"responsePreview":{"description":"The start of your endpoint's response body, truncated, with anything that looks like a credential removed","anyOf":[{"type":"string"},{"type":"null"}]},"durationMs":{"description":"How long the request took, end to end","anyOf":[{"type":"integer"},{"type":"null"}]},"manual":{"description":"True when you asked for this attempt, false for an automatic one","type":"boolean"},"createdAt":{"description":"When the attempt was made (ISO 8601)","type":"string"}}}}}}}},"security":[{"bearerAuth":[]}],"paths":{"/v1/transcripts":{"post":{"operationId":"create-transcript","tags":["Transcripts"],"summary":"Create transcript","description":"Starts a transcription and returns it right away with `status: \"processing\"`.\n\n**Audio**: `url` (any public http(s) address) **or** `uploadId` (from `POST /v1/uploads`), exactly one of the two.\n\n**Result**: poll `GET /v1/transcripts/{id}` until `status` is `done` or `failed`, or pass `webhookUrl` to be called when it finishes.\n\n**Billing**: charged on completion, from the real audio duration (max 10 hours). Failed transcripts are never charged. `reuseIfIdentical` (on an upload created with `sha256`) returns the transcript the account already has for that exact audio instead of charging twice.\n\n**Retries**: send an `Idempotency-Key` header (up to 64 chars); the same key returns the same transcript instead of creating and charging a second one. The same key with a different body is a `409`. An `uploadId` changes on every retry, so key the request by your first `uploadId`, never by your own media id.\n\n**Trial limit**: until the first top-up, at most 2 transcriptions run at once; a third gets `429 concurrency_limit`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"minLength":1,"maxLength":2048,"description":"Public http(s) URL of the audio. Send this or `uploadId`, never both","type":"string"},"uploadId":{"maxLength":36,"description":"Id returned by `POST /v1/uploads`. Send this or `url`, never both","type":"string"},"language":{"maxLength":16,"description":"Language code (\"pt\", \"en\", …). Omit to detect it automatically","type":"string"},"speakers":{"description":"Separate speakers: `true` (count unknown), `3` (exactly 3) or `\"2-5\"` (between 2 and 5). Omit for a single block of text. Changes the hourly rate","anyOf":[{"type":"boolean"},{"minimum":1,"maximum":20,"type":"integer"},{"pattern":"^([1-9]|1[0-9]|20)-([1-9]|1[0-9]|20)$","type":"string"}]},"webhookUrl":{"minLength":1,"maxLength":2048,"description":"Public http(s) URL that receives a POST when this transcript reaches `done` or `failed`. The body is the transcript without `text` and `words`; fetch those with `GET /v1/transcripts/{id}`. Use it instead of polling.\n\nDeliveries are signed: `Transcrevo-Signature: t=<unix seconds>,v1=<hex>`, where `v1` is the HMAC-SHA256 of `<t>.<raw body>`, keyed with the lowercase hex SHA-256 of the API key that created the transcript. Recompute over the raw body, compare in constant time, reject old `t`. Respond 2xx to acknowledge; anything else is retried with backoff for about 9 hours","type":"string"},"glossary":{"description":"Corrections that apply to this audio only, as `{\"what the engine hears\": \"what to write\"}` (for example `{\"gatu\": \"Gato\"}`). Proper names and jargon are what this is for. Applied after transcription, on top of the built-in corrections","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"externalId":{"minLength":1,"maxLength":128,"description":"Your own id for this audio (a media id, a row id, a ticket number). Returned unchanged on every read and inside the webhook, and accepted as a filter on `GET /v1/transcripts`, so a delivery is recognizable without a lookup table on your side. Not unique: the same audio can be transcribed twice with different options","type":"string"},"metadata":{"description":"Key/value pairs carried with the transcript and returned unchanged on every read and in the webhook. Never interpreted by the API. Up to 20 pairs, keys up to 40 chars, values up to 500","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"retentionDays":{"minimum":1,"maximum":365,"description":"Erase the CONTENT of this transcript this many days after it is created: `text`, `words` and the stored audio go, and the transcript then reads like a deleted one. The usage record stays, because the invoice is built from it. Omit to follow the account default. There is no zero here: content has to survive long enough for you to fetch it","type":"integer"},"reuseIfIdentical":{"description":"Return the transcript this account already has for this exact audio, instead of transcribing (and charging) again. Requires an upload created with `sha256`, which is what identifies the content. Responds `200` with the existing transcript instead of `201`","type":"boolean"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"url":{"minLength":1,"maxLength":2048,"description":"Public http(s) URL of the audio. Send this or `uploadId`, never both","type":"string"},"uploadId":{"maxLength":36,"description":"Id returned by `POST /v1/uploads`. Send this or `url`, never both","type":"string"},"language":{"maxLength":16,"description":"Language code (\"pt\", \"en\", …). Omit to detect it automatically","type":"string"},"speakers":{"description":"Separate speakers: `true` (count unknown), `3` (exactly 3) or `\"2-5\"` (between 2 and 5). Omit for a single block of text. Changes the hourly rate","anyOf":[{"type":"boolean"},{"minimum":1,"maximum":20,"type":"integer"},{"pattern":"^([1-9]|1[0-9]|20)-([1-9]|1[0-9]|20)$","type":"string"}]},"webhookUrl":{"minLength":1,"maxLength":2048,"description":"Public http(s) URL that receives a POST when this transcript reaches `done` or `failed`. The body is the transcript without `text` and `words`; fetch those with `GET /v1/transcripts/{id}`. Use it instead of polling.\n\nDeliveries are signed: `Transcrevo-Signature: t=<unix seconds>,v1=<hex>`, where `v1` is the HMAC-SHA256 of `<t>.<raw body>`, keyed with the lowercase hex SHA-256 of the API key that created the transcript. Recompute over the raw body, compare in constant time, reject old `t`. Respond 2xx to acknowledge; anything else is retried with backoff for about 9 hours","type":"string"},"glossary":{"description":"Corrections that apply to this audio only, as `{\"what the engine hears\": \"what to write\"}` (for example `{\"gatu\": \"Gato\"}`). Proper names and jargon are what this is for. Applied after transcription, on top of the built-in corrections","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"externalId":{"minLength":1,"maxLength":128,"description":"Your own id for this audio (a media id, a row id, a ticket number). Returned unchanged on every read and inside the webhook, and accepted as a filter on `GET /v1/transcripts`, so a delivery is recognizable without a lookup table on your side. Not unique: the same audio can be transcribed twice with different options","type":"string"},"metadata":{"description":"Key/value pairs carried with the transcript and returned unchanged on every read and in the webhook. Never interpreted by the API. Up to 20 pairs, keys up to 40 chars, values up to 500","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"retentionDays":{"minimum":1,"maximum":365,"description":"Erase the CONTENT of this transcript this many days after it is created: `text`, `words` and the stored audio go, and the transcript then reads like a deleted one. The usage record stays, because the invoice is built from it. Omit to follow the account default. There is no zero here: content has to survive long enough for you to fetch it","type":"integer"},"reuseIfIdentical":{"description":"Return the transcript this account already has for this exact audio, instead of transcribing (and charging) again. Requires an upload created with `sha256`, which is what identifies the content. Responds `200` with the existing transcript instead of `201`","type":"boolean"}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"url":{"minLength":1,"maxLength":2048,"description":"Public http(s) URL of the audio. Send this or `uploadId`, never both","type":"string"},"uploadId":{"maxLength":36,"description":"Id returned by `POST /v1/uploads`. Send this or `url`, never both","type":"string"},"language":{"maxLength":16,"description":"Language code (\"pt\", \"en\", …). Omit to detect it automatically","type":"string"},"speakers":{"description":"Separate speakers: `true` (count unknown), `3` (exactly 3) or `\"2-5\"` (between 2 and 5). Omit for a single block of text. Changes the hourly rate","anyOf":[{"type":"boolean"},{"minimum":1,"maximum":20,"type":"integer"},{"pattern":"^([1-9]|1[0-9]|20)-([1-9]|1[0-9]|20)$","type":"string"}]},"webhookUrl":{"minLength":1,"maxLength":2048,"description":"Public http(s) URL that receives a POST when this transcript reaches `done` or `failed`. The body is the transcript without `text` and `words`; fetch those with `GET /v1/transcripts/{id}`. Use it instead of polling.\n\nDeliveries are signed: `Transcrevo-Signature: t=<unix seconds>,v1=<hex>`, where `v1` is the HMAC-SHA256 of `<t>.<raw body>`, keyed with the lowercase hex SHA-256 of the API key that created the transcript. Recompute over the raw body, compare in constant time, reject old `t`. Respond 2xx to acknowledge; anything else is retried with backoff for about 9 hours","type":"string"},"glossary":{"description":"Corrections that apply to this audio only, as `{\"what the engine hears\": \"what to write\"}` (for example `{\"gatu\": \"Gato\"}`). Proper names and jargon are what this is for. Applied after transcription, on top of the built-in corrections","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"externalId":{"minLength":1,"maxLength":128,"description":"Your own id for this audio (a media id, a row id, a ticket number). Returned unchanged on every read and inside the webhook, and accepted as a filter on `GET /v1/transcripts`, so a delivery is recognizable without a lookup table on your side. Not unique: the same audio can be transcribed twice with different options","type":"string"},"metadata":{"description":"Key/value pairs carried with the transcript and returned unchanged on every read and in the webhook. Never interpreted by the API. Up to 20 pairs, keys up to 40 chars, values up to 500","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}},"retentionDays":{"minimum":1,"maximum":365,"description":"Erase the CONTENT of this transcript this many days after it is created: `text`, `words` and the stored audio go, and the transcript then reads like a deleted one. The usage record stays, because the invoice is built from it. Omit to follow the account default. There is no zero here: content has to survive long enough for you to fetch it","type":"integer"},"reuseIfIdentical":{"description":"Return the transcript this account already has for this exact audio, instead of transcribing (and charging) again. Requires an upload created with `sha256`, which is what identifies the content. Responds `200` with the existing transcript instead of `201`","type":"boolean"}}}}}},"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcript"}}}},"201":{"description":"Response for status 201","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcript"}}}},"400":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.\n\n`upload_mismatch`: The bytes stored for this upload do not match the `expectedBytes` declared when it was created. Send the missing bytes, or start a new upload; transcribing it would return a silently truncated transcript.","content":{"application/json":{"schema":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.\n\n`upload_mismatch`: The bytes stored for this upload do not match the `expectedBytes` declared when it was created. Send the missing bytes, or start a new upload; transcribing it would return a silently truncated transcript.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"type":"string","enum":["validation","upload_mismatch"]},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"402":{"description":"`insufficient_balance`: The account has no credits left. Top up to continue; nothing is deleted.","content":{"application/json":{"schema":{"description":"`insufficient_balance`: The account has no credits left. Top up to continue; nothing is deleted.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"insufficient_balance","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"404":{"description":"`upload_not_found`: No upload with this id exists on this account.","content":{"application/json":{"schema":{"description":"`upload_not_found`: No upload with this id exists on this account.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"upload_not_found","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"409":{"description":"`idempotency_conflict`: This `Idempotency-Key` was already used with a different request body. Use a new key.","content":{"application/json":{"schema":{"description":"`idempotency_conflict`: This `Idempotency-Key` was already used with a different request body. Use a new key.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"idempotency_conflict","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"429":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.\n\n`concurrency_limit`: The account is already running the maximum number of transcriptions at once. Until the first top-up, an account runs 2 concurrently; wait for one to finish, or top up to lift the limit. Retryable.","content":{"application/json":{"schema":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.\n\n`concurrency_limit`: The account is already running the maximum number of transcriptions at once. Until the first top-up, an account runs 2 concurrently; wait for one to finish, or top up to lift the limit. Retryable.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"type":"string","enum":["rate_limited","concurrency_limit"]},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}},"get":{"operationId":"list-transcripts","tags":["Transcripts"],"summary":"List transcripts","description":"Transcripts on the account, newest first. Items omit `text` and `words`; fetch a single transcript to get those.\n\n`since` and `until` cut by creation date in the database, so closing out one day of cost does not mean paging through the whole account: `?since=2026-08-27T00:00:00Z&until=2026-08-28T00:00:00Z`.","parameters":[{"name":"page","in":"query","required":false,"schema":{"minimum":1,"maximum":100000,"description":"1-based page number (default 1)","type":"integer"}},{"name":"perPage","in":"query","required":false,"schema":{"minimum":1,"maximum":100,"description":"Items per page, up to 100 (default 20)","type":"integer"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["processing","done","failed","canceled"]}},{"name":"externalId","in":"query","required":false,"schema":{"maxLength":128,"description":"Only transcripts created with this `externalId`","type":"string"}},{"name":"cursor","in":"query","required":false,"schema":{"maxLength":200,"description":"The `nextCursor` of the previous page. Use it instead of `page` to walk a long history: it never skips or repeats a transcript, and it does not count the whole account","type":"string"}},{"name":"since","in":"query","required":false,"schema":{"description":"Only transcripts created at or after this ISO 8601 date-time","type":"string"}},{"name":"until","in":"query","required":false,"schema":{"description":"Only transcripts created before this ISO 8601 date-time","type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptList"}}}},"400":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.","content":{"application/json":{"schema":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"validation","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}},"/v1/transcripts/{id}":{"get":{"operationId":"get-transcript","tags":["Transcripts"],"summary":"Get transcript","description":"Read this until `status` becomes `done` (text in `text`, word timings in `words`, cost in `cost`), `failed` (reason in `error`) or `canceled`.\n\n**Prefer `?wait=30` over a polling loop**: the response is held until the transcript finishes, and comes back immediately when it does. While it is still `processing`, the response carries `Retry-After` with the interval worth waiting before asking again.\n\nFor many transcripts at once, `webhookUrl` on creation is still the cheaper path: one delivery per job instead of one connection per job.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"wait","in":"query","required":false,"schema":{"minimum":1,"maximum":60,"description":"Hold the response for up to this many seconds (max 60) while the transcript is still processing. It returns THE MOMENT the transcript finishes, so one request replaces a polling loop. It is not an error for the wait to expire: you get the transcript still in `processing` and call again","type":"integer"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcript"}}}},"400":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.","content":{"application/json":{"schema":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"validation","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"404":{"description":"`not_found`: No transcript with this id exists on this account.","content":{"application/json":{"schema":{"description":"`not_found`: No transcript with this id exists on this account.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"not_found","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}},"delete":{"operationId":"delete-transcript","tags":["Transcripts"],"summary":"Delete transcript","description":"Erases the transcribed text, the words and the stored audio. The usage record itself stays, since it is what the invoice for the period is built from, but the content is gone and cannot be recovered.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptDeleted"}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"404":{"description":"`not_found`: No transcript with this id exists on this account.","content":{"application/json":{"schema":{"description":"`not_found`: No transcript with this id exists on this account.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"not_found","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"409":{"description":"`transcript_processing`: The transcript is still processing and cannot be deleted yet. Wait for it to finish. Retryable.","content":{"application/json":{"schema":{"description":"`transcript_processing`: The transcript is still processing and cannot be deleted yet. Wait for it to finish. Retryable.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"transcript_processing","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}},"/v1/transcripts/{id}/cancel":{"post":{"operationId":"cancel-transcript","tags":["Transcripts"],"summary":"Cancel transcript","description":"Stops a transcript that is still `processing` and returns it with `status: \"canceled\"`.\n\n**Billing**: a canceled transcript is never charged, and the credit it was holding (`reserved` in `GET /v1/balance`) is released immediately.\n\n**Timing**: cancelling is about the charge, not about the machine. Audio already on a GPU runs to the end of its current work and the result is discarded; nothing about it reaches you or your invoice.\n\nA transcript that already reached `done`, `failed` or `canceled` returns `409`: those states are terminal, and the one that completed was already charged. If you passed a `webhookUrl`, the cancellation is delivered to it like any other ending.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcript"}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"404":{"description":"`not_found`: No transcript with this id exists on this account.","content":{"application/json":{"schema":{"description":"`not_found`: No transcript with this id exists on this account.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"not_found","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"409":{"description":"`transcript_finished`: The transcript already reached a terminal state (`done`, `failed` or `canceled`) and cannot be canceled. A `done` one was already charged.","content":{"application/json":{"schema":{"description":"`transcript_finished`: The transcript already reached a terminal state (`done`, `failed` or `canceled`) and cannot be canceled. A `done` one was already charged.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"transcript_finished","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}},"/v1/balance":{"get":{"operationId":"get-balance","tags":["Account"],"summary":"Get balance","description":"Credits on the account, in the minor unit of `currency` (cents). Values carry a fraction of a cent: a short audio costs less than one cent, so do not store them in an integer column.\n\n`available` is what the next transcription can spend: `total` minus what transcriptions already running hold in `reserved`. Credit is only spent when a transcription completes, never when it is created, and a failed transcription is never charged.","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}},"/v1/webhook-deliveries":{"get":{"operationId":"list-webhook-deliveries","tags":["Webhooks"],"summary":"List webhook deliveries","description":"Every attempt made to deliver a completion notice, newest first: the status your endpoint answered, how long it took, and the beginning of its response body.\n\nThis is what separates \"the webhook never arrived\" from \"it arrived and your server answered 500\", which is the same sentence from the outside.","parameters":[{"name":"transcriptId","in":"query","required":false,"schema":{"maxLength":36,"description":"Only the attempts made for this transcript","type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"minimum":1,"maximum":200,"description":"How many attempts to return, newest first (default 50)","type":"integer"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveries"}}}},"400":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.","content":{"application/json":{"schema":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"validation","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}},"post":{"operationId":"replay-webhook","tags":["Webhooks"],"summary":"Send a webhook again","description":"Delivers the completion notice for a transcript again, right now, and returns the attempt it produced.\n\nThe attempt counter continues instead of restarting: it is the same delivery being retried, and restarting it would hand a broken endpoint a fresh set of automatic retries on every click. A replay does not cancel the automatic retry that may still be scheduled.\n\nThe body is identical to the original notice, signed with your current webhook secret. Your endpoint must be idempotent: the same transcript can arrive more than once, which is also true of the automatic retries.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["transcriptId"],"properties":{"transcriptId":{"maxLength":36,"description":"The transcript whose completion notice should be sent again","type":"string"}}}},"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["transcriptId"],"properties":{"transcriptId":{"maxLength":36,"description":"The transcript whose completion notice should be sent again","type":"string"}}}},"multipart/form-data":{"schema":{"type":"object","required":["transcriptId"],"properties":{"transcriptId":{"maxLength":36,"description":"The transcript whose completion notice should be sent again","type":"string"}}}}}},"responses":{"201":{"description":"Response for status 201","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveries"}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"404":{"description":"`not_found`: No transcript with this id exists on this account.","content":{"application/json":{"schema":{"description":"`not_found`: No transcript with this id exists on this account.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"not_found","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"409":{"description":"`no_webhook`: This transcript has no `webhookUrl` to deliver to, or has not finished yet. There is nothing to send again.","content":{"application/json":{"schema":{"description":"`no_webhook`: This transcript has no `webhookUrl` to deliver to, or has not finished yet. There is nothing to send again.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"no_webhook","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"429":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.","content":{"application/json":{"schema":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"rate_limited","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}},"/v1/limits":{"get":{"operationId":"get-limits","tags":["Account"],"summary":"Get limits","description":"Every platform limit, plus the ones that depend on this account. Read it at startup instead of hardcoding the numbers: a file above `upload.maxRequestBytes` has to go through the chunked path, and one above `audio.maxDurationSeconds` fails after the upload is already spent.\n\n`concurrency.maxRunning` is `null` once the account has topped up at least once.","responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Limits"}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}},"/v1/uploads":{"post":{"operationId":"create-upload","tags":["Uploads"],"summary":"Upload a file","description":"Send the audio file as the raw request body and use the returned `id` as `uploadId` when creating the transcript. No `Content-Type` is required. One request carries a file of up to 100 MB, which covers most audio; the edge rejects a bigger request before it reaches the API.\n\nSend no body to get an empty upload instead, and fill it with `PUT /v1/uploads/{id}`, 16 MiB at a time, up to 2 GiB: that is the path for a file above 100 MB, and for a long file over a link that may drop.","parameters":[{"name":"expectedBytes","in":"query","required":false,"schema":{"description":"Size of the file in bytes. Declare it and a truncated upload becomes an explicit error instead of a transcript of whatever arrived","type":"string"}},{"name":"sha256","in":"query","required":false,"schema":{"description":"SHA-256 of the file, in lowercase hex. Checked against the stored audio before it reaches the GPU, and required by `reuseIfIdentical`","type":"string"}}],"responses":{"201":{"description":"Response for status 201","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"400":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.\n\n`upload_mismatch`: The bytes stored for this upload do not match the `expectedBytes` declared when it was created. Send the missing bytes, or start a new upload; transcribing it would return a silently truncated transcript.","content":{"application/json":{"schema":{"description":"`validation`: The request body failed validation; `fields` maps each invalid field to a stable message key.\n\n`upload_mismatch`: The bytes stored for this upload do not match the `expectedBytes` declared when it was created. Send the missing bytes, or start a new upload; transcribing it would return a silently truncated transcript.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"type":"string","enum":["validation","upload_mismatch"]},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"402":{"description":"`insufficient_balance`: The account has no credits left. Top up to continue; nothing is deleted.","content":{"application/json":{"schema":{"description":"`insufficient_balance`: The account has no credits left. Top up to continue; nothing is deleted.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"insufficient_balance","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"413":{"description":"`upload_too_large`: The upload would exceed the 2 GiB limit.","content":{"application/json":{"schema":{"description":"`upload_too_large`: The upload would exceed the 2 GiB limit.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"upload_too_large","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"429":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.","content":{"application/json":{"schema":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"rate_limited","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}},"/v1/uploads/{id}":{"put":{"operationId":"append-upload-chunk","tags":["Uploads"],"summary":"Append chunk","description":"Appends a chunk (raw body, up to 16 MiB) to the end of the file. Send chunks **in order**, one at a time; the response returns the accumulated `receivedBytes`, which is where to resume from.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"400":{"description":"`chunk_empty`: The request body is empty.\n\n`upload_mismatch`: The bytes stored for this upload do not match the `expectedBytes` declared when it was created. Send the missing bytes, or start a new upload; transcribing it would return a silently truncated transcript.","content":{"application/json":{"schema":{"description":"`chunk_empty`: The request body is empty.\n\n`upload_mismatch`: The bytes stored for this upload do not match the `expectedBytes` declared when it was created. Send the missing bytes, or start a new upload; transcribing it would return a silently truncated transcript.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"type":"string","enum":["chunk_empty","upload_mismatch"]},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"404":{"description":"`upload_not_found`: No upload with this id exists on this account.","content":{"application/json":{"schema":{"description":"`upload_not_found`: No upload with this id exists on this account.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"upload_not_found","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"409":{"description":"`upload_busy`: Another chunk of this upload is still being written. Send chunks sequentially, one at a time. Retryable.","content":{"application/json":{"schema":{"description":"`upload_busy`: Another chunk of this upload is still being written. Send chunks sequentially, one at a time. Retryable.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"upload_busy","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"413":{"description":"`chunk_too_large`: The chunk exceeds the 16 MiB limit.\n\n`upload_too_large`: The upload would exceed the 2 GiB limit.","content":{"application/json":{"schema":{"description":"`chunk_too_large`: The chunk exceeds the 16 MiB limit.\n\n`upload_too_large`: The upload would exceed the 2 GiB limit.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"type":"string","enum":["chunk_too_large","upload_too_large"]},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"429":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.","content":{"application/json":{"schema":{"description":"`rate_limited`: Too many requests. Wait a moment and retry. Retryable.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"rate_limited","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}},"get":{"operationId":"get-upload","tags":["Uploads"],"summary":"Get upload","description":"Returns `receivedBytes`: use it to resume an interrupted upload from the right byte.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Response for status 200","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"401":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","content":{"application/json":{"schema":{"description":"`invalid_api_key`: The Authorization header is missing or the API key is invalid.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"invalid_api_key","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}},"404":{"description":"`upload_not_found`: No upload with this id exists on this account.","content":{"application/json":{"schema":{"description":"`upload_not_found`: No upload with this id exists on this account.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","requestId"],"properties":{"code":{"const":"upload_not_found","type":"string"},"message":{"description":"Plain-English explanation of `code`. Read this one; branch on `code`.","type":"string"},"retryable":{"description":"Whether repeating the SAME request can succeed. `false` means the request must change; repeating it returns this error forever.","type":"boolean"},"requestId":{"description":"Identifier of this response, also in the `X-Request-Id` header. Quote it in a support request.","type":"string"},"fields":{"description":"One stable message key per invalid field (only for `validation`).","type":"object","patternProperties":{"^(.*)$":{"type":"string"}}}}}}}}}}}}}}}