cast
Reference for the cast DVM, which hosts private RSS feeds and podcast episodes, with six capabilities, retention pricing, and error codes.
cast hosts private RSS feeds and podcast episodes. Publish audio into a feed and cast returns a public RSS 2.0 + iTunes XML URL that any podcast client can subscribe to. No object store, no RSS host, no separate accounts. It is dvmkit's first stateful-resource DVM: feeds are owned by a caller pubkey, and every capability is signed with that key.
Canonical endpoint: https://cast.dvmkit.ai. Six capabilities under one descriptor, all owner-signed with secp256k1 + BIP-340 Schnorr requests.
Published reference. This page is the source of truth for cast's documented inputs, outputs, prices, and errors. The running service publishes its current capability schemas and advertised prices in its live descriptor. Full output envelope shapes are in the dvm CLI reference.
Capabilities
The SDK dispatches on the wire capability field. The first add-episode against a feed_slug creates the feed; every later call requires the calling pubkey to match the feed owner.
| Capability | Pricing | Idempotent | What it does |
|---|---|---|---|
add-episode | Paid | No | Publish an episode (creates the feed on first call) |
update-feed | Free | Yes | Edit feed-level metadata + cover art (post-publish) |
update-episode | Free | Yes | Patch an episode in place, preserving its GUID |
delete-episode | Free | Yes | Remove one episode by episode_id |
delete-feed | Free | Yes | Remove a feed and cascade-delete its episodes |
list-feeds | Free | Yes | List feeds owned by the calling pubkey (recovery path) |
add-episode
Submit a signed AddEpisodeInput with one of two mutually-exclusive audio sources:
audio_url: cast HEAD-fetches the URL during the quote, prices on size × TTL, then streams it into storage after payment.audio_upload_handle: UUID from a priorPOST /v1/ingest/audio(bytes-upload); the quote prices from the recorded byte count.
ttl_days is required (no default): it sets the retention window and drives storage cost. At expiry the cleanup cron auto-deletes the episode; restoring means re-publishing (and paying again). Because it's consequential and hard to reverse, an agent acting for a human should confirm the retention window before publishing.
Result: { feed_url, episode_id, ttl_days, expires_at }.
Feed-level title, description, author, category, explicit flag, and cover art are not set here: use update-feed. Only per-episode artwork (episode_image_url / episode_image_upload_handle), chapters, and transcript belong on the episode. Caps: 200 MB per episode, 50 episodes per feed, ttl_days ∈ [1, 3650].
Free capabilities
update-feed: edit any feed metadata field post-publish (title, description, language, author, category, explicit, owner name/email, copyright, iTunes type, cover image). Feed identity/URL is preserved.update-episode: patch an episode's title, description, artwork, transcript, or chapters in place. Preserves GUID,pub_date, feed position, and audio enclosure (podcast clients see an edit, not a re-download). Replace-only: swapping audio isdelete-episode+ re-add.delete-episode/delete-feed: idempotent removes; a missing target returns{ deleted: false }.list-feeds: the recovery path for a restarted agent. Returns every feed owned by the calling pubkey with canonicalfeed_urlvalues, episode counts, and anempty_feedwarning when a feed has no episodes (Apple rejects those).
CLI example
Every capability is invoked the same way: dvm request -d dvmkit--cast/<capability> with a signed identity.
dvm request -d dvmkit--cast/add-episode --as my-identity --data '{
"feed_slug":"my-show","feed_title":"My Show",
"episode":{"title":"Ep 1","audio_url":"https://example.com/ep1.mp3","ttl_days":365}
}'
dvm request -d dvmkit--cast/list-feeds --as my-identity --data '{}'
dvm request -d dvmkit--cast/update-feed --as my-identity --data '{"feed_slug":"my-show","feed":{"author":"Jane Smith"}}'
dvm request -d dvmkit--cast/delete-episode --as my-identity --data '{"feed_slug":"my-show","episode_id":"<uuid>"}'
Pricing
Only add-episode is paid:
| Component | Cost |
|---|---|
| Per-add fee | $0.02 USD |
| Storage | ceil(audio_bytes / 1MB) × ttl_days × $0.000005 USD |
Storage scales with the retention window, so a longer ttl_days costs more. Sats binding is derived at the 402 handshake using the SDK FX fetcher; both the /v1/quote description and the add-episode result name the retention window and expiry instant explicitly.
Public surfaces
GET https://cast.dvmkit.ai/feeds/{owner_short}/{feed_slug}: the public RSS 2.0 + iTunes feed.owner_shortis the first 12 hex chars ofsha256(pubkey).POST /v1/ingest/audio: stage raw audio bytes (signed claim), returns a 15-minupload_handle.POST /v1/ingest/image: stage cover / episode artwork (JPEG/PNG/WebP, square, ≥ 1400×1400).
Auth
Per-request secp256k1 + BIP-340 Schnorr signature over the canonical-JSON body, at the descriptor level so all six capabilities share one auth slot. Drift window ±5 minutes; replay window 10 minutes (Postgres-backed, survives restarts). An unsigned, stale or replayed request rejects with a structured 401 auth_error before payment is taken.
The CLI handles this for you: dvm init creates a default signing identity and dvm quote / dvm request sign with it automatically. Pass --as <identity> only when you want to sign as a different one. For cast that also selects which owner's feeds you are operating on, since the verified pubkey is the ownership key.
Prepaid credit
cast offers a prepaid balance, so an agent publishing a run of episodes funds once instead of paying per job. The funding menu rides on every quote and every 402.
| Field | Value |
|---|---|
| Minimum funding | $0.10 |
| Maximum residual balance | $5.00 |
| Credit lifetime | 30 days from the most recent funding |
dvm credit fund dvmkit--cast --amount 2.00
dvm credit balance dvmkit--cast
dvm credit drain dvmkit--cast # reclaim it, works after expiry too
A job priced above the maximum still clears: the ceiling binds how much balance you may hold, not what you may spend. And a failed job never takes your money: the charge is released back onto your credit rather than settled. See Prepaid credit for the full model. (This is about job payments; deleting a feed or episode does not refund the storage you already used.)
Error codes
Failures throw a typed error rendered as code (+ sub_reason). payment_failed comes from the SDK's 402 handshake before any cast handler runs.
| Code | Representative sub-reasons | Trigger |
|---|---|---|
auth_error | signature_invalid, timestamp_drift, replay_detected | Request signature, clock, or nonce invalid |
invalid_input | ssrf_blocked, wrong_content_type, oversize, probe_failed, cover_invalid, audio_source_required, upload-handle not_found / expired / not_owner / kind_mismatch | Bad source URL, bad asset, or a bad/expired upload handle |
feed_full | — | Feed already at 50 episodes. Delete one or use a new feed_slug |
provider_error | upstream_status, upstream_timeout, upstream_network, upload_failed | The upstream source or storage host failed, not cast itself. Transient, retry |
rate_limited | upstream, ingest_concurrency, ingest_ip_concurrency, ingest_global_concurrency | Upstream 429 or a concurrent-upload cap. Back off per Retry-After |
not_found | feed_not_found, episode_not_found, path_not_found | Operating on a feed/episode the caller doesn't own or that doesn't exist |
internal_error | unexpected | An unexpected DVM-side fault |
See also
- Explore the catalog · dvm CLI reference · Caller quickstart
- Other first-party DVMs: scrape · scribe · narrate · discover