Errors use one JSON shape:

{
  "error": {
    "code": "audio_not_ready",
    "message": "Audio is still processing.",
    "param": "audio"
  }
}

param may be null when the error applies to the whole request. Use code for application logic; message is readable detail.

Some validation errors also include fields, with the messages for each field:

{
  "error": {
    "code": "invalid",
    "message": "Name can't be blank",
    "param": "name",
    "fields": { "name": ["can't be blank"] }
  }
}
HTTP statusCodeWhat to do
400bad_requestSupply a required parameter or body.
401unauthorizedSend Authorization: Bearer with a valid token; create or replace it if needed.
403forbiddenUse an admin account for this podcast.
404not_foundCheck the hashid or ID and account access.
422invalidCorrect the field named by param, if present.
422upload_invalidCheck the upload ID, audio type, file size, artwork format, or remote URL.
422audio_not_readyPoll the episode until audio.status is ready.
422plan_limit_reachedA plan limit (episodes a month, or episode length) stopped the publish; the message says which. The episode stays a draft.
422channels_unsupportedOmit channels for a legacy podcast.
422immutable_live_releaseA channel release is already live; unpublish before making a new release.
422time_zone_unconfirmedConfirm the podcast time zone before using local-time channel scheduling.

A remote audio import or an audio conversion can fail after a draft was created. In that case, GET /episodes/:id returns audio.status: "failed" and an audio.error message. Correct the URL and PATCH the episode with a new audio_url, or attach a new upload.

For network failures, retry reads. For a retried POST /episodes, reuse the same Idempotency-Key so the retry returns the original draft. Do not retry a validation error unchanged.