audivo-dev · Schema
JobStatus
Failure/cancellation objects report `reservation_released: true` and never imply a cash refund occurred — a cash refund is a separate, explicit Stripe refund via `POST /v1/billing/refund`.
CompanyPodcastTranscriptsAPIAudio
Properties
| Name | Type | Description |
|---|---|---|
| job_id | object | |
| status | object | |
| episode_id | object | |
| show_title | object | |
| episode_title | object | |
| progress | object | |
| estimated_credits | object | |
| reserved_credits | object | |
| settled_credits | object | Present on terminal `completed` jobs; measured usage capped at the reservation. |
| released_credits | object | Present on terminal states; the unused portion of the reservation released back to the account. |
| reservation_released | boolean | Present on terminal `failed`/`cancelled` jobs. Never implies a cash refund. |
| ledger_event_ids | array | Immutable ledger event IDs, present on terminal states. |
| error | object | Present when `status` is `failed`; code `processing_failed` with retryability metadata. |
| artifact | object | The completed transcript, in JSON delivery. Present only when `status` is `completed` **and** the caller explicitly asked for it with `?format=json` on `GET /v1/transcripts/{job_id}` — `PollFormatQuer |
| created_at | string | |
| started_at | string | |
| completed_at | string |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://artifacts.apis.io/audivo-dev/json-schema/audivo-dev-job-status-schema.json",
"title": "JobStatus",
"description": "Failure/cancellation objects report `reservation_released: true` and never imply a cash refund occurred — a cash refund is a separate, explicit Stripe refund via `POST /v1/billing/refund`.",
"x-generated": "2026-10-30",
"x-method": "derived",
"x-generator": "derive-json-schema.py",
"x-source": "openapi/audivo-dev-openapi.yaml#/components/schemas/JobStatus",
"type": "object",
"additionalProperties": false,
"required": [
"job_id",
"status",
"episode_id",
"show_title",
"episode_title",
"estimated_credits",
"reserved_credits",
"created_at"
],
"properties": {
"job_id": {
"$ref": "#/$defs/JobId"
},
"status": {
"$ref": "#/$defs/JobState"
},
"episode_id": {
"$ref": "#/$defs/EpisodeId"
},
"show_title": {
"$ref": "#/$defs/JobShowTitle"
},
"episode_title": {
"$ref": "#/$defs/JobEpisodeTitle"
},
"progress": {
"$ref": "#/$defs/JobProgress"
},
"estimated_credits": {
"$ref": "#/$defs/CreditAmount"
},
"reserved_credits": {
"$ref": "#/$defs/CreditAmount"
},
"settled_credits": {
"$ref": "#/$defs/CreditAmount",
"description": "Present on terminal `completed` jobs; measured usage capped at the reservation."
},
"released_credits": {
"$ref": "#/$defs/CreditAmount",
"description": "Present on terminal states; the unused portion of the reservation released back to the account."
},
"reservation_released": {
"type": "boolean",
"description": "Present on terminal `failed`/`cancelled` jobs. Never implies a cash refund."
},
"ledger_event_ids": {
"type": "array",
"maxItems": 20,
"items": {
"type": "string",
"minLength": 1,
"maxLength": 80
},
"description": "Immutable ledger event IDs, present on terminal states."
},
"error": {
"$ref": "#/$defs/ErrorDetail",
"description": "Present when `status` is `failed`; code `processing_failed` with retryability metadata."
},
"artifact": {
"$ref": "#/$defs/TranscriptArtifact",
"description": "The completed transcript, in JSON delivery. Present only when `status` is `completed` **and** the caller explicitly asked for it with `?format=json` on `GET /v1/transcripts/{job_id}` — `PollFormatQueryParam` has no default, so omitting `format` there is a real, distinct request that never carries `artifact`, whatever `status` is. It is deliberately *not* required by `completed`: a client polling a finished job would otherwise be forced to re-download the whole transcript on every call, with no status-only option."
},
"created_at": {
"type": "string",
"format": "date-time"
},
"started_at": {
"type": "string",
"format": "date-time"
},
"completed_at": {
"type": "string",
"format": "date-time"
}
},
"allOf": [
{
"if": {
"required": [
"status"
],
"properties": {
"status": {
"const": "failed"
}
}
},
"then": {
"required": [
"reservation_released",
"released_credits",
"error"
]
},
"else": {
"not": {
"required": [
"error"
]
}
}
},
{
"if": {
"required": [
"status"
],
"properties": {
"status": {
"const": "cancelled"
}
}
},
"then": {
"required": [
"reservation_released",
"released_credits"
]
}
},
{
"if": {
"required": [
"status"
],
"properties": {
"status": {
"enum": [
"failed",
"cancelled"
]
}
}
},
"else": {
"not": {
"required": [
"reservation_released"
]
}
}
},
{
"if": {
"required": [
"status"
],
"properties": {
"status": {
"const": "completed"
}
}
},
"then": {
"required": [
"settled_credits",
"released_credits"
]
},
"else": {
"allOf": [
{
"not": {
"required": [
"settled_credits"
]
}
},
{
"not": {
"required": [
"artifact"
]
}
}
]
}
},
{
"if": {
"required": [
"status"
],
"properties": {
"status": {
"enum": [
"completed",
"failed",
"cancelled"
]
}
}
},
"else": {
"allOf": [
{
"not": {
"required": [
"released_credits"
]
}
},
{
"not": {
"required": [
"ledger_event_ids"
]
}
}
]
}
}
],
"$defs": {
"CanonicalTranscript": {
"type": "object",
"additionalProperties": false,
"required": [
"episode_id",
"show_id",
"language",
"duration_sec",
"source",
"source_revision",
"model_version",
"pipeline_version",
"timing_precision",
"diarized",
"warnings",
"segments",
"created_at"
],
"properties": {
"episode_id": {
"$ref": "#/$defs/EpisodeId"
},
"show_id": {
"$ref": "#/$defs/ShowId"
},
"language": {
"$ref": "#/$defs/Language",
"description": "Resolved BCP-47 language code."
},
"duration_sec": {
"type": "number",
"minimum": 0,
"maximum": 129600
},
"source": {
"$ref": "#/$defs/TranscriptSource"
},
"source_revision": {
"type": "string",
"minLength": 1,
"maxLength": 150,
"description": "e.g. `sha256:...` — the asset revision this transcript was produced from."
},
"model_version": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "Provider-pinned model version, or `publisher` for passthrough."
},
"pipeline_version": {
"type": "string",
"minLength": 1,
"maxLength": 60
},
"timing_precision": {
"$ref": "#/$defs/TimingPrecision"
},
"diarized": {
"const": false,
"description": "Always `false` in the MVP; diarization is post-MVP."
},
"warnings": {
"type": "array",
"maxItems": 1000,
"items": {
"$ref": "#/$defs/TranscriptWarning"
}
},
"segments": {
"type": "array",
"maxItems": 200000,
"items": {
"$ref": "#/$defs/TranscriptSegment"
}
},
"created_at": {
"type": "string",
"format": "date-time"
}
}
},
"CreditAmount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "A non-negative JavaScript-safe integer credit amount. The upper bound matches `Number.MAX_SAFE_INTEGER`, which is enforced by the domain ledger so arithmetic and JSON round-trips cannot silently lose cents worth of credit precision."
},
"EpisodeId": {
"type": "string",
"pattern": "^ep_[a-z2-7]{16}$",
"description": "Canonical episode identifier."
},
"ErrorCode": {
"type": "string",
"enum": [
"invalid_request",
"invalid_url",
"unauthenticated",
"idempotency_conflict",
"job_not_completed",
"source_not_supported",
"feed_dead",
"episode_not_found",
"show_not_found",
"unsafe_source",
"unsupported_codec",
"unsupported_language",
"duration_exceeded",
"size_exceeded",
"content_blocked",
"payment_required",
"rate_limited",
"concurrency_limited",
"job_not_found",
"api_key_not_found",
"api_key_limit_reached",
"credit_lot_not_found",
"credits_not_refundable",
"engine_unavailable",
"processing_failed",
"discovery_unavailable",
"nothing_to_quote",
"internal_error",
"quote_not_found",
"group_not_found",
"quote_expired",
"quote_mismatch",
"quote_unverified",
"expected_total_mismatch",
"request_in_progress",
"account_suspended",
"account_closed",
"account_not_found",
"tier_unchanged",
"upload_quota_exceeded",
"upload_not_found",
"upload_not_received",
"upload_mismatch",
"max_credits_exceeded",
"oauth_request_not_found",
"connected_app_not_found"
],
"description": "Every code the API returns, and the HTTP status it comes with. `invalid_request`, `invalid_url` → 400. `unauthenticated` → 401. `payment_required` → 402. `job_not_found`, `quote_not_found`, `group_not_found`, `api_key_not_found`, `credit_lot_not_found`, `upload_not_found`, `oauth_request_not_found`, `connected_app_not_found` → 404. `idempotency_conflict`, `request_in_progress`, `job_not_completed`, `quote_expired`, `quote_mismatch`, `quote_unverified`, `expected_total_mismatch`, `account_suspended`, `account_closed`, `account_not_found`, `api_key_limit_reached`, `tier_unchanged`, `upload_not_received` → 409; `upload_not_received` is retryable once the file's PUT has landed. `source_not_supported`, `feed_dead`, `episode_not_found`, `show_not_found`, `unsafe_source`, `unsupported_codec`, `unsupported_language`, `duration_exceeded`, `size_exceeded`, `nothing_to_quote`, `credits_not_refundable`, `upload_mismatch`, `max_credits_exceeded` → 422. `content_blocked` → 451. `rate_limited`, `concurrency_limited`, `upload_quota_exceeded` → 429; `upload_quota_exceeded` is retryable, at the earliest capacity return time its message names. `internal_error`, `processing_failed` → 500. `discovery_unavailable`, `engine_unavailable` → 503. `processing_failed` also appears inside a failed job's `error` field rather than as a live status. Each code has a heading on the documentation site's errors page, which is what `doc_url` links to."
},
"ErrorDetail": {
"type": "object",
"additionalProperties": false,
"required": [
"type",
"code",
"message",
"doc_url",
"request_id",
"retryable"
],
"properties": {
"type": {
"$ref": "#/$defs/ErrorType"
},
"code": {
"$ref": "#/$defs/ErrorCode"
},
"message": {
"type": "string",
"minLength": 1,
"maxLength": 1000
},
"doc_url": {
"type": "string",
"format": "uri",
"maxLength": 300
},
"request_id": {
"$ref": "#/$defs/RequestId"
},
"retryable": {
"type": "boolean"
}
},
"oneOf": [
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"enum": [
"invalid_request",
"invalid_url",
"source_not_supported",
"unsafe_source"
]
},
"type": {
"const": "invalid_request"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"enum": [
"duration_exceeded",
"size_exceeded",
"unsupported_codec",
"unsupported_language",
"credits_not_refundable",
"upload_mismatch",
"max_credits_exceeded"
]
},
"type": {
"const": "unprocessable_input"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"const": "unauthenticated"
},
"type": {
"const": "unauthenticated"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"enum": [
"idempotency_conflict",
"job_not_completed",
"quote_expired",
"quote_mismatch",
"quote_unverified",
"expected_total_mismatch",
"request_in_progress",
"account_suspended",
"account_closed",
"account_not_found",
"api_key_limit_reached",
"tier_unchanged",
"upload_not_received"
]
},
"type": {
"const": "conflict"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"enum": [
"feed_dead",
"episode_not_found",
"show_not_found",
"job_not_found",
"quote_not_found",
"group_not_found",
"api_key_not_found",
"credit_lot_not_found",
"upload_not_found",
"oauth_request_not_found",
"connected_app_not_found"
]
},
"type": {
"const": "not_found"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"const": "content_blocked"
},
"type": {
"const": "content_blocked"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"const": "payment_required"
},
"type": {
"const": "payment_required"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"enum": [
"rate_limited",
"concurrency_limited",
"upload_quota_exceeded"
]
},
"type": {
"const": "rate_limited"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"enum": [
"engine_unavailable",
"processing_failed",
"discovery_unavailable",
"internal_error"
]
},
"type": {
"const": "unavailable"
}
}
},
{
"required": [
"code",
"type"
],
"properties": {
"code": {
"const": "nothing_to_quote"
},
"type": {
"const": "unprocessable_input"
}
}
}
]
},
"ErrorType": {
"type": "string",
"enum": [
"invalid_request",
"unprocessable_input",
"unauthenticated",
"conflict",
"not_found",
"content_blocked",
"payment_required",
"rate_limited",
"unavailable"
],
"description": "The family an error belongs to, for handling; `code` is the precise reason. `invalid_request`: something in the request is wrong — fix the integration. `unprocessable_input`: the request is well-formed but the episode or resource is outside published support. `unauthenticated`: missing, malformed, revoked or expired credential. `payment_required`: the account's credits cannot cover the ceiling. `conflict`: the resource's or the account's state refuses this request as sent — a lapsed quote, a reused idempotency key, a held account. `not_found`: the job, quote, group or key the request named does not exist for this account. `rate_limited`: over a plan limit; wait and retry. `unavailable`: a failure on our side or at a provider; retry, and quote `request_id` if it persists."
},
"EstimatedProcessingSeconds": {
"type": "integer",
"minimum": 0,
"maximum": 72000,
"description": "Estimated **wall-clock** seconds until the job reaches a terminal state — intended for choosing a poll interval. This is *not* audio duration: audio duration has its own field (`duration_sec` on the transcript, `duration_sec` on `EpisodeSummary`), and conflating the two silently misprices everything a caller derives from it. The committed examples only make sense under this reading: `estimated_seconds: 420` alongside `estimated_credits: 268` cannot be audio seconds, because 7 audio minutes bills 7 credits at `CREDITS_PER_AUDIO_MINUTE`, while 268 credits is about 4.5 hours of audio.\n\nThe bound is derived from that meaning rather than copied from the audio cap: twice the published 10-hour `max_audio_duration_seconds` ceiling, an end-to-end envelope that still holds at a pathological 1× realtime factor. The real hard per-job timeout is duration-scaled and much shorter."
},
"JobEpisodeTitle": {
"description": "The episode's title as it was when this job or read was created, or `null` when it was created without one. For an upload it is the title given when the file was announced. Where a feed gave no title, a catalog read (`GET /v1/episodes/{episode_id}/transcript`) answers the identifier the catalog stores in its place.",
"anyOf": [
{
"type": "string",
"minLength": 1,
"maxLength": 300
},
{
"type": "null"
}
]
},
"JobId": {
"type": "string",
"pattern": "^job_[A-Za-z0-9]{16,32}$",
"maxLength": 40,
"description": "Opaque, server-generated job identifier."
},
"JobProgress": {
"type": "object",
"additionalProperties": false,
"required": [
"chunks_done",
"chunks_total",
"percent",
"realtime_factor",
"eta_seconds"
],
"properties": {
"chunks_done": {
"type": "integer",
"minimum": 0,
"maximum": 100000
},
"chunks_total": {
"type": "integer",
"minimum": 0,
"maximum": 100000
},
"percent": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"realtime_factor": {
"type": "number",
"minimum": 0,
"maximum": 10000
},
"eta_seconds": {
"description": "Estimated wall-clock seconds remaining until this job reaches a terminal state, or `null` if not yet computable. Same quantity and same bound as `estimated_seconds`, measured later in the job's life — so it shares `EstimatedProcessingSeconds` rather than carrying its own copy of the audio-duration ceiling.",
"anyOf": [
{
"$ref": "#/$defs/EstimatedProcessingSeconds"
},
{
"enum": [
null
]
}
]
}
}
},
"JobShowTitle": {
"description": "The show's title as it was when this job or read was created (ADR-0036), or `null` when it was created without one: before 0.14.0, or from a feed that names no show. For an upload it is `Uploads`. Copied once, never updated.",
"anyOf": [
{
"type": "string",
"minLength": 1,
"maxLength": 300
},
{
"type": "null"
}
]
},
"JobState": {
"type": "string",
"enum": [
"validating",
"queued",
"downloading",
"transcribing",
"merging",
"completed",
"failed",
"cancelled"
],
"description": "Legal transitions: validating → queued|failed|cancelled; queued → downloading|failed|cancelled; downloading → transcribing|failed; transcribing → merging|failed; merging → completed|failed. Terminal states (completed, failed, cancelled) have no further transitions. Cancellation is legal only from validating/queued; every non-terminal state may fail."
},
"Language": {
"type": "string",
"pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$",
"maxLength": 35,
"description": "A BCP-47 language tag (general syntax check, not full IANA subtag validation)."
},
"RequestId": {
"type": "string",
"pattern": "^req_[A-Za-z0-9-]+$",
"maxLength": 100
},
"ShowId": {
"type": "string",
"pattern": "^sh_[a-z2-7]{16}$",
"description": "Canonical show identifier."
},
"TimingPrecision": {
"type": "string",
"enum": [
"word",
"segment",
"none"
],
"description": "How precisely segments/words are timed. ASR output targets `word`. Publisher-sourced transcripts report the precision the source actually provides and never claim word timing they do not have: plain text is `none`, segment-timed sources are `segment`, and only genuine word-timed sources are `word`."
},
"TranscriptArtifact": {
"type": "object",
"additionalProperties": false,
"required": [
"format"
],
"description": "The JSON delivery of a completed job's transcript, embedded in `JobStatus` when `status` is `completed` and JSON delivery was requested. `format` is always `json` here: the other formats are delivered as raw bodies (`RawTranscriptArtifact`), never wrapped in this envelope. Payloads above the ~5 MB inline limit carry `transcript_url` (presigned, 24 h) plus `expires_at` instead of an inline `transcript`.",
"properties": {
"format": {
"const": "json"
},
"transcript": {
"$ref": "#/$defs/CanonicalTranscript"
},
"transcript_url": {
"type": "string",
"format": "uri",
"maxLength": 2048
},
"expires_at": {
"type": "string",
"format": "date-time"
}
},
"oneOf": [
{
"required": [
"transcript"
]
},
{
"required": [
"transcript_url",
"expires_at"
]
}
]
},
"TranscriptSegment": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"start",
"end",
"speaker",
"text"
],
"properties": {
"id": {
"type": "integer",
"minimum": 0,
"maximum": 1000000
},
"start": {
"type": "number",
"minimum": 0,
"maximum": 129600,
"description": "Start time in seconds."
},
"end": {
"type": "number",
"minimum": 0,
"maximum": 129600,
"description": "End time in seconds."
},
"speaker": {
"enum": [
null
],
"description": "Always `null` in the MVP — diarization is post-MVP. A future diarized-speaker field is a deliberate, reviewed type change, not a silent widening."
},
"text": {
"type": "string",
"maxLength": 20000
},
"words": {
"type": "array",
"maxItems": 20000,
"items": {
"$ref": "#/$defs/TranscriptWord"
},
"description": "Present when `timing_precision` is `word`; absent otherwise."
}
}
},
"TranscriptSource": {
"type": "string",
"enum": [
"qwen3-asr",
"whisper-large-v3-turbo",
"whisper-large-v3",
"publisher"
],
"description": "The ASR engine that produced this transcript, or `publisher` for passthrough."
},
"TranscriptWarning": {
"type": "object",
"additionalProperties": false,
"required": [
"segment",
"type",
"detail"
],
"description": "A quality warning from the pipeline's own checks, attached to the segment it concerns. Presented as a heuristic, not an accuracy guarantee; the transcript is delivered and charged as usual. `type` is an open string rather than a closed enum; the documentation's output-formats page lists the values in use.",
"properties": {
"segment": {
"type": "integer",
"minimum": 0,
"maximum": 1000000
},
"type": {
"type": "string",
"minLength": 1,
"maxLength": 60
},
"detail": {
"type": "string",
"minLength": 1,
"maxLength": 500
}
}
},
"TranscriptWord": {
"type": "object",
"additionalProperties": false,
"required": [
"w",
"s",
"e"
],
"properties": {
"w": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "The word text."
},
"s": {
"type": "number",
"minimum": 0,
"maximum": 129600,
"description": "Start time in seconds."
},
"e": {
"type": "number",
"minimum": 0,
"maximum": 129600,
"description": "End time in seconds."
}
}
}
}
}
Work with this as data
Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for schemas
4 MCP tools reach this
find_json_schemasBrowse and filter every JSON Schema in the catalog.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/audivo-dev-job-status"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.