{"openapi":"3.0.3","info":{"title":"WhipScribe API","version":"1.0.0","description":"Transcribe audio and video into accurate, timestamped text. Submit a file or a link, poll the job, then fetch the transcript as JSON (segments and word timings), TXT, SRT, VTT or DOCX, plus an AI summary with key quotes and topics.\n\nAuthenticate every request with your API key in the `X-API-Key` header. Create a key at https://whipscribe.com/apis/keys. API access comes with the 5,000-minute pack ($24) or the Workspace plan ($19/month); smaller packs work on the website but do not include API keys. Usage is metered in audio time and drawn from the same balance as the web app; see https://whipscribe.com/pricing. Errors are JSON: `{\"error\": \"...\", \"code\": \"...\"}`.\n\nFull guide: https://whipscribe.com/docs","termsOfService":"https://whipscribe.com/terms","contact":{"name":"WhipScribe Team","email":"contact@whipscribe.com","url":"https://whipscribe.com/docs"},"x-logo":{"url":"https://whipscribe.com/icons/icon128.png"}},"externalDocs":{"description":"API guide","url":"https://whipscribe.com/docs"},"servers":[{"url":"https://whipscribe.com/api","description":"Production"}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Transcription","description":"Submit audio or video for transcription."},{"name":"Jobs","description":"Track jobs and fetch transcripts."},{"name":"Account","description":"Identity and remaining balance."}],"paths":{"/v1/transcribe":{"post":{"tags":["Transcription"],"operationId":"submitFile","summary":"Transcribe a file","description":"Upload an audio or video file as `multipart/form-data`. The job is queued; poll `GET /v1/jobs/{job_id}` until it is `done`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. Retries that carry the same key (per API key) return the original job (HTTP 200, `X-Idempotent-Replay: true`) instead of creating a duplicate. Up to 255 characters from `[A-Za-z0-9_.:/-]`.","schema":{"type":"string","maxLength":255,"pattern":"^[A-Za-z0-9_.:/-]{1,255}$"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/FileSubmit"}}}},"responses":{"202":{"description":"Job accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAccepted"}}}},"200":{"description":"Idempotent replay of an earlier submit with the same `Idempotency-Key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAccepted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/PaymentRequired"},"413":{"description":"File too large (`FILE_TOO_LARGE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Unsupported file type (`BAD_MIME`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/Unprocessable"},"502":{"$ref":"#/components/responses/Upstream"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/SubmitRateLimited"}}}},"/v1/transcribe/url":{"post":{"tags":["Transcription"],"operationId":"submitUrl","summary":"Transcribe from a URL","description":"We fetch the media from the link and transcribe it. Same options as the file submit. A link to one podcast episode (Apple Podcasts ?i= link, Podbean, Transistor, Captivate or any episode page) is transcribed as that episode; if we cannot tell which episode it is, the request is refused with 400 EPISODE_UNKNOWN and nothing is charged. A show page or an RSS feed URL means the show's latest episode. A profile, playlist or set is refused with 400 LINK_IS_A_LIST.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. Retries that carry the same key (per API key) return the original job (HTTP 200, `X-Idempotent-Replay: true`) instead of creating a duplicate. Up to 255 characters from `[A-Za-z0-9_.:/-]`.","schema":{"type":"string","maxLength":255,"pattern":"^[A-Za-z0-9_.:/-]{1,255}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UrlSubmit"}}}},"responses":{"202":{"description":"Job accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAccepted"}}}},"200":{"description":"Idempotent replay of an earlier submit with the same `Idempotency-Key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAccepted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/PaymentRequired"},"422":{"$ref":"#/components/responses/Unprocessable"},"502":{"$ref":"#/components/responses/Upstream"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/SubmitRateLimited"}}}},"/v1/uploads/init":{"post":{"tags":["Transcription"],"operationId":"createUpload","summary":"Start a direct upload (large files)","description":"For large files: get a short-lived upload URL, send the file bytes to it with the returned `method` and `headers`, then call `POST /v1/uploads/{job_id}/commit`. Requires an API key bound to an account.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. Retries that carry the same key (per API key) return the original job (HTTP 200, `X-Idempotent-Replay: true`) instead of creating a duplicate. Up to 255 characters from `[A-Za-z0-9_.:/-]`.","schema":{"type":"string","maxLength":255,"pattern":"^[A-Za-z0-9_.:/-]{1,255}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadInit"}}}},"responses":{"200":{"description":"Upload ticket.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadTicket"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/PaymentRequired"},"415":{"description":"File extension not accepted (`BAD_EXT`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/Unprocessable"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/uploads/{job_id}/commit":{"post":{"tags":["Transcription"],"operationId":"commitUpload","summary":"Finish a direct upload and start transcription","description":"Call after the file bytes have been uploaded to the ticket's `upload_url`. Echo back `object_key` and `options` from the ticket.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The job id returned on submit.","schema":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadCommit"}}}},"responses":{"202":{"description":"Job accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAccepted"}}}},"200":{"description":"Already committed; returns the existing job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitAccepted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"description":"The `object_key` does not belong to this job and caller (`KEY_MISMATCH`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/SubmitRateLimited"}}}},"/v1/jobs":{"get":{"tags":["Jobs"],"operationId":"listJobs","summary":"List your jobs","description":"Most recent first, scoped to the caller's identity.","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"status","in":"query","schema":{"type":"string","enum":["queued","processing","done","failed"]}}],"responses":{"200":{"description":"A page of jobs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/jobs/{job_id}":{"get":{"tags":["Jobs"],"operationId":"getJob","summary":"Get job status","description":"Lightweight poll; a 3-second interval while the job is `queued` or `processing` is fine. Supports `If-None-Match` (304). Check `locked` before fetching the transcript.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The job id returned on submit.","schema":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"}},{"name":"X-Claim-Token","in":"header","required":false,"description":"Proves ownership of a job submitted without a user identity. The submit response returns it as `claim_token`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"304":{"description":"Not modified since the ETag you sent."},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Jobs"],"operationId":"deleteJob","summary":"Cancel or delete a job","description":"Cancels a queued job, or removes a finished job and its files. Deleting a finished job does not refund the time it used.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The job id returned on submit.","schema":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"}},{"name":"X-Claim-Token","in":"header","required":false,"description":"Proves ownership of a job submitted without a user identity. The submit response returns it as `claim_token`.","schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted."},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/jobs/{job_id}/result":{"get":{"tags":["Jobs"],"operationId":"getTranscript","summary":"Get the transcript","description":"`format=json` returns text, segments and word timings. `txt`, `srt` and `vtt` return plain text; `docx` returns a Word document. A locked job returns a preview for `json` and 402 for other formats.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The job id returned on submit.","schema":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"}},{"name":"format","in":"query","schema":{"type":"string","enum":["json","txt","srt","vtt","docx"],"default":"json"}},{"name":"X-Claim-Token","in":"header","required":false,"description":"Proves ownership of a job submitted without a user identity. The submit response returns it as `claim_token`.","schema":{"type":"string"}}],"responses":{"200":{"description":"The transcript.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transcript"}},"text/plain":{"schema":{"type":"string"}},"application/x-subrip":{"schema":{"type":"string"}},"text/vtt":{"schema":{"type":"string"}},"application/vnd.openxmlformats-officedocument.wordprocessingml.document":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/NotReady"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/jobs/{job_id}/insights":{"get":{"tags":["Jobs"],"operationId":"getInsights","summary":"Get summary and insights","description":"Summary, key quotes, topics and named speakers for a finished job. Generated on first request, then returned from cache.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The job id returned on submit.","schema":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"}},{"name":"X-Claim-Token","in":"header","required":false,"description":"Proves ownership of a job submitted without a user identity. The submit response returns it as `claim_token`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Insights.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Insights"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Job not finished (`NOT_READY`), or not enough speech to summarize (`NO_SPEECH`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"$ref":"#/components/responses/Upstream"},"503":{"description":"Insights temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/jobs/{job_id}/audio/url":{"get":{"tags":["Jobs"],"operationId":"getPlaybackUrl","summary":"Get a playback URL","description":"A short-lived URL for streaming the original audio, so your API key stays off the playback path.","parameters":[{"name":"job_id","in":"path","required":true,"description":"The job id returned on submit.","schema":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"}},{"name":"X-Claim-Token","in":"header","required":false,"description":"Proves ownership of a job submitted without a user identity. The submit response returns it as `claim_token`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Playback URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackUrl"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"description":"Audio retention window has passed (`AUDIO_EXPIRED`) or the audio is gone (`AUDIO_MISSING`). The transcript is still readable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/me":{"get":{"tags":["Account"],"operationId":"getMe","summary":"Who am I","description":"What the server sees for this caller, including the audio retention window that applies.","responses":{"200":{"description":"Caller identity.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/credits":{"get":{"tags":["Account"],"operationId":"getBalance","summary":"Get remaining transcription time","description":"Transcription time left on the account behind the API key. Credit is shared between the web app and the API.","responses":{"200":{"description":"Balance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"502":{"$ref":"#/components/responses/Upstream"},"503":{"description":"Balance lookup is not available right now; retry later (`CREDITS_UNAVAILABLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Your WhipScribe API key."}},"schemas":{"Error":{"type":"object","required":["error","code"],"properties":{"error":{"type":"string","description":"Human-readable sentence."},"code":{"type":"string","description":"Stable machine-readable code, e.g. `BAD_ID`, `NOT_FOUND`, `NO_CREDITS`, `transcript_locked`."}},"additionalProperties":true,"example":{"error":"invalid job_id","code":"BAD_ID"}},"SubmitAccepted":{"type":"object","required":["job_id","status"],"properties":{"job_id":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"},"status":{"type":"string","enum":["queued","processing","done","failed"]},"claim_token":{"type":"string","description":"Returned only when no user identity was supplied. Send it as `X-Claim-Token` on later reads of this job."}},"additionalProperties":true,"example":{"job_id":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3","status":"queued"}},"UrlSubmit":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","minLength":8,"maxLength":2000,"description":"Public link to the audio or video (a page link or a direct media file)."},"language":{"type":"string","default":"auto","example":"en","description":"ISO 639-1 language code (`en`, `es`, `fr`, ...), or `auto` to detect it."},"diarize":{"type":"boolean","default":false,"description":"Label who is speaking."},"word_timestamps":{"type":"boolean","default":false,"description":"Include per-word start and end times."},"source":{"type":"string","enum":["upload","url","recording","api"],"description":"How the transcript was created. Defaults to `upload` for file submits and `url` for link submits."}},"example":{"url":"https://example.com/episode-412.mp3","language":"en","source":"api"}},"FileSubmit":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"Audio or video file (mp3, m4a, wav, mp4, mov, ogg, webm, flac and more). Up to 10 hours."},"language":{"type":"string","default":"auto","example":"en","description":"ISO 639-1 language code (`en`, `es`, `fr`, ...), or `auto` to detect it."},"diarize":{"type":"boolean","default":false,"description":"Label who is speaking."},"word_timestamps":{"type":"boolean","default":false,"description":"Include per-word start and end times."},"source":{"type":"string","enum":["upload","url","recording","api"],"description":"How the transcript was created. Defaults to `upload` for file submits and `url` for link submits."}}},"UploadInit":{"type":"object","required":["filename","size_bytes"],"properties":{"filename":{"type":"string","minLength":1,"maxLength":512,"example":"episode-412.mp3"},"content_type":{"type":"string","maxLength":128,"default":"application/octet-stream","example":"audio/mpeg"},"size_bytes":{"type":"integer","minimum":1,"example":48213344},"language":{"type":"string","default":"auto","example":"en","description":"ISO 639-1 language code (`en`, `es`, `fr`, ...), or `auto` to detect it."},"diarize":{"type":"boolean","default":false,"description":"Label who is speaking."},"word_timestamps":{"type":"boolean","default":false,"description":"Include per-word start and end times."},"source":{"type":"string","enum":["upload","url","recording","api"],"description":"How the transcript was created. Defaults to `upload` for file submits and `url` for link submits."}}},"UploadTicket":{"type":"object","required":["job_id","object_key","upload_url","method","expires_in","options"],"properties":{"job_id":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"},"object_key":{"type":"string","description":"Echo this back to the commit call."},"upload_url":{"type":"string","format":"uri","description":"Short-lived URL to send the file bytes to."},"method":{"type":"string","example":"PUT"},"headers":{"type":"object","additionalProperties":{"type":"string"},"description":"Headers to send with the upload request."},"expires_in":{"type":"integer","description":"Seconds until `upload_url` expires."},"options":{"type":"object","additionalProperties":true,"description":"Echo this back to the commit call unchanged."}}},"UploadCommit":{"type":"object","required":["object_key","options"],"properties":{"object_key":{"type":"string","description":"`object_key` from the upload ticket."},"options":{"type":"object","additionalProperties":true,"description":"`options` from the upload ticket, unchanged."},"original_filename":{"type":"string","description":"Optional display name for the job."}}},"Job":{"type":"object","required":["job_id","status"],"properties":{"job_id":{"type":"string","format":"uuid","example":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3"},"status":{"type":"string","enum":["queued","processing","done","failed"]},"progress":{"type":"number","minimum":0,"maximum":100,"description":"Best-effort progress as a percentage, 0 to 100. A hint for a progress bar; branch on `status`."},"queue_position":{"type":"integer","minimum":0,"description":"Jobs ahead of this one while `queued`; 0 otherwise."},"filename":{"type":"string","nullable":true},"audio_duration_seconds":{"type":"number","nullable":true},"language":{"type":"string","nullable":true},"source":{"type":"string","enum":["upload","url","recording","api"],"description":"How the transcript was created. Defaults to `upload` for file submits and `url` for link submits."},"speech_detected":{"type":"boolean","description":"`false` when the audio holds no transcribable speech (music, noise, silence). The job still finishes as `done`, and no time is charged."},"speech_ratio":{"type":"number","minimum":0,"maximum":1,"description":"Fraction of the audio classified as speech."},"locked":{"type":"boolean","description":"`true` when the finished transcript needs credit on the account before it can be read in full."},"locked_code":{"type":"string","example":"transcript_locked"},"unlock_url":{"type":"string","format":"uri"},"created_at":{"type":"number","description":"Unix epoch seconds."},"completed_at":{"type":"number","nullable":true,"description":"Unix epoch seconds."},"error":{"type":"string","nullable":true,"description":"On a failed job: one sentence a person can read. `null` otherwise."},"error_code":{"type":"string","enum":["SERVICE_UNAVAILABLE","PROCESSING_STALLED","PROCESSING_TIMEOUT","NO_AUDIO","SOURCE_SIGN_IN_REQUIRED","LIVE_STREAM_UNSUPPORTED","SOURCE_PROTECTED","SOURCE_TOO_LARGE","SOURCE_RATE_LIMITED","SOURCE_UNAVAILABLE","SOURCE_UNSUPPORTED","SOURCE_ACCESS_DENIED","SOURCE_UNREACHABLE","SERVICE_ERROR"],"description":"Present on a failed job. Stable code for why it failed. `SOURCE_*` codes are about the link you submitted; `NO_AUDIO` is about the file."},"retryable":{"type":"boolean","description":"Present on a failed job. `true` when submitting the same request again can succeed. A failed job is never billed."}},"additionalProperties":true,"example":{"job_id":"35f4be54-aa3e-4adc-85b7-b44f284d1fc3","status":"processing","progress":42,"audio_duration_seconds":967,"language":"en","source":"api","locked":false,"error":null}},"JobList":{"type":"object","required":["jobs"],"properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"limit":{"type":"integer"},"offset":{"type":"integer"},"count":{"type":"integer"},"total":{"type":"integer","nullable":true}},"additionalProperties":true},"Word":{"type":"object","properties":{"start":{"type":"number"},"end":{"type":"number"},"text":{"type":"string"}}},"Segment":{"type":"object","properties":{"start":{"type":"number"},"end":{"type":"number"},"speaker":{"type":"string","nullable":true,"example":"SPEAKER_00"},"text":{"type":"string"},"words":{"type":"array","items":{"$ref":"#/components/schemas/Word"}}},"additionalProperties":true},"Transcript":{"type":"object","properties":{"text":{"type":"string"},"language":{"type":"string","nullable":true},"segments":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"speech_detected":{"type":"boolean"}},"additionalProperties":true,"example":{"text":"Welcome back to the show.","language":"en","segments":[{"start":0.0,"end":2.1,"speaker":"SPEAKER_00","text":"Welcome back to the show.","words":[{"start":0.0,"end":0.4,"text":"Welcome"}]}]}},"Insights":{"type":"object","properties":{"insights":{"type":"object","additionalProperties":true,"description":"Summary, key quotes, topics and named speakers for the recording."}},"additionalProperties":true},"PlaybackUrl":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"Short-lived URL an `<audio>` element (or any client that supports Range) can stream from."},"expires_in":{"type":"integer","description":"Seconds until the URL expires; fetch a new one after that."},"storage":{"type":"string","enum":["direct","signed"],"description":"`direct`: `url` is absolute; use it as is. `signed`: `url` is a path on this API (`/v1/jobs/{job_id}/audio?token=…`); prefix it with the API base you called (for example `https://whipscribe.com/api`)."}},"additionalProperties":true},"Me":{"type":"object","required":["tier","retention_days","signed_in"],"properties":{"email":{"type":"string","nullable":true,"description":"The account this caller is identified as: the signed-in user, or the owner of the API key. `null` for an anonymous caller."},"tier":{"type":"string","enum":["guest","free","paid"],"description":"Stable public field."},"retention_days":{"type":"integer","description":"Days uploaded audio is kept after a job finishes. Today every account answers 36500: recordings are kept until you delete them (`DELETE /v1/jobs/{job_id}`)."},"signed_in":{"type":"boolean","description":"`true` when the request carries a user sign-in. API-key callers are identified by `email` and see `false`."}},"additionalProperties":true,"example":{"email":"you@example.com","tier":"paid","retention_days":36500,"signed_in":false}},"Balance":{"type":"object","properties":{"email":{"type":"string"},"payg_hours_remaining":{"type":"number","description":"Pay-as-you-go transcription hours left."},"pro_active":{"type":"boolean","description":"Whether a subscription plan is active."},"pro_hours_remaining_in_period":{"type":"number","description":"Plan hours left in the current billing period."},"pro_plan":{"type":"string","nullable":true},"pro_expires_at":{"type":"string","nullable":true},"hours_used_lifetime":{"type":"number"}},"additionalProperties":true},"ConcurrencyLimit":{"type":"object","required":["error","code"],"properties":{"error":{"type":"string","description":"Human-readable sentence."},"code":{"type":"string","description":"`CONCURRENCY_LIMIT`, `RATE_LIMITED` or `QUOTA_EXCEEDED`."},"limit":{"type":"integer","description":"How many recordings the account's plan runs at once."},"running":{"type":"integer","description":"How many of the account's recordings are queued or transcribing now."},"retry_after":{"type":"integer","description":"Seconds to wait before submitting again (also in the `Retry-After` header)."}},"additionalProperties":true,"example":{"error":"This account already has 10 recordings transcribing; the Workspace plan runs up to 10 at once. Retry this one after one finishes (about 60 s).","code":"CONCURRENCY_LIMIT","limit":10,"running":10,"retry_after":60},"description":"A 429 from a submit. With `code: CONCURRENCY_LIMIT` it also carries `limit`, `running` and `retry_after`."}},"responses":{"BadRequest":{"description":"Malformed request (e.g. `BAD_ID`, `BAD_URL`, `BAD_IDEMPOTENCY_KEY`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing or invalid API key, or the job needs an ownership proof (`X-Claim-Token`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PaymentRequired":{"description":"Not enough credit (`NO_CREDITS`), or the finished transcript is locked (`transcript_locked`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Unknown job id, or a job that is not yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"not found","code":"NOT_FOUND"}}}},"NotReady":{"description":"The job has not finished yet (`NOT_READY`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"A field failed validation (e.g. `BAD_SOURCE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; retry with backoff (`RATE_LIMITED`, `QUOTA_EXCEEDED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Upstream":{"description":"Temporary service error; safe to retry (`BACKEND_ERROR`, `BACKEND_UNREACHABLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubmitRateLimited":{"description":"Too many requests (`RATE_LIMITED`, `QUOTA_EXCEEDED`), or the account already has as many recordings running as its plan allows at once (`CONCURRENCY_LIMIT`: Workspace subscribers, counted across the website and all of the account's keys). Wait `Retry-After` seconds and submit again; a refused commit can be retried with the same upload.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConcurrencyLimit"}}}}}}}