Skip to main content

Unleashed Podcasts CLI

A command-line interface for the UnleashedPodcasts.com private v1 API, shaped like the AWS CLI: predictable noun verb commands, credentials from the environment, JSON in and JSON out.

Status: episodes (with transcripts), short form videos, people, ideas, tasks, and leads, plus login, whoami, and configure. See PRD.md for the roadmap.

Install

Once published to PyPI:

uv tool install unleashed-podcasts-cli

Straight from the repository, which works today:

uv tool install git+https://github.com/ScottKeckWarren/UnleashedPodcastsCli

Run it once without installing anything:

uvx --from git+https://github.com/ScottKeckWarren/UnleashedPodcastsCli unleashed --help

pipx works everywhere uv tool does, if you prefer it. Plain pip install only inside an activated virtualenv — a CLI belongs in its own environment, not alongside your project's dependencies.

To upgrade or remove:

uv tool upgrade unleashed-podcasts-cli
uv tool uninstall unleashed-podcasts-cli

Working on the CLI itself

git clone git@github.com:ScottKeckWarren/UnleashedPodcastsCli.git
cd UnleashedPodcastsCli
uv tool install --editable .    # `unleashed` on PATH, tracking your working tree
uv sync && uv run pytest        # run the suite

Log in

unleashed login

That opens your browser on the site's approval page. Sign in if asked, press Approve, and the CLI saves a personal access token to ~/.unleashed/config. The token never passes through the browser — the browser carries a one-use code that only the waiting login process can redeem, and only for two minutes.

unleashed login --profile client-a                   # write a named profile
unleashed login --local                              # write ./.unleashed/config
unleashed login --api-url http://localhost/apiv1     # log in to another host
unleashed login --ability episodes:read              # ask for less (repeatable)
unleashed login --device-name studio-mac             # name shown in the web app
unleashed login --no-browser                         # print the URL instead

By default the token asks for every ability the site grants. The device name defaults to your hostname; it is what you will see in the web app when you revoke the token.

Check which account and abilities a token carries:

unleashed whoami

A 401 means the token is dead — run unleashed login again. A 403 means the token is alive but was not granted the ability that command needs; it exits 8.

Configure

To paste a token by hand instead — for CI, or the legacy static token:

unleashed configure

That writes ~/.unleashed/config:

[default]
api_url = https://unleashedpodcasts.com/apiv1
api_token = your-token

Several accounts on one machine

A .unleashed/config in a project directory overrides the home one key by key, so a project file can carry nothing but a token and inherit the URL from home.

~/.unleashed/config              api_url + api_token   (your personal account)

~/work/client-a/.unleashed/config    api_token          (client A's token)
~/work/client-b/.unleashed/config    api_token          (client B's token)

Run from anywhere inside ~/work/client-a — including a subdirectory — and you are using client A. The local file is found by walking up from the working directory.

cd ~/work/client-a
unleashed configure --local

Profiles

For several accounts without separate directories, use sections:

[default]
api_url = https://unleashedpodcasts.com/apiv1
api_token = tok-personal

[client-a]
api_url = https://unleashedpodcasts.com/apiv1
api_token = tok-client-a
unleashed --profile client-a episodes create ...
unleashed configure --profile client-a
export UNLEASHED_PROFILE=client-a      # or set it once

A profile does not inherit from [default]. Give each one both keys.

Precedence

Highest first:

  1. --profile selects which section is read
  2. the nearest local .unleashed/config
  3. ~/.unleashed/config
  4. UNLEASHED_API_URL and UNLEASHED_API_TOKEN

Environment variables sit at the bottom on purpose: a stale export in your shell should not override a project directory that has already declared which account it belongs to. They stay the right tool for CI and containers, which set variables and never write files.

Not sure which account you are about to use? --dry-run names the profile and the file it came from.

$ unleashed episodes create --dry-run ...
# profile default (from /Users/you/work/client-a/.unleashed/config)
POST https://unleashedpodcasts.com/apiv1/episodes
...

Token safety

configure writes the file with mode 0600. If a config file holding a token is readable by other users, every command warns and tells you the chmod that fixes it.

There is deliberately no --token flag — a token on the command line lands in your shell history and in the process table of every other user on the machine.

Episodes

Five actions, the same five the API exposes. No ad-hoc verbs.

unleashed episodes list
unleashed episodes create --podcast-uuid ... --name ... --target-published-date ...
unleashed episodes get <uuid>
unleashed episodes update <uuid> --status Scheduled
unleashed episodes delete <uuid>

Create

You need the UUID of the podcast the episode belongs to. Until the podcasts resource ships in v0.3, copy it from the web app.

unleashed episodes create \
    --podcast-uuid 7f3a1c2e-... \
    --name "Episode 101: Something Useful" \
    --target-published-date 2026-09-15 \
    --status Draft \
    --description "Show notes here."

Pass a payload instead of flags:

unleashed episodes create --cli-input-json '{"podcast_uuid": "...", "name": "...", "target_published_date": "2026-09-15"}'

List

Filters are flags, named for the field they match. Every page is fetched by default, so a result is never a silent truncation.

unleashed episodes list --status Edit --transcript-status none --no-has-cover-art
unleashed episodes list --sort -target_published_date --max-items 20
unleashed episodes list --no-paginate            # first page only

--sort takes one allowlisted column; prefix it with - for descending. A column outside the allowlist is caught before the request is sent:

$ unleashed episodes list --sort nonsense
Error: Cannot sort by 'nonsense'. Allowed columns: target_published_date,
actual_published_date, created_at, name, status (prefix - for desc)

Update

An omitted flag leaves the field alone. --clear-<field> sends an explicit null. Passing no flags at all is a successful no-op.

unleashed episodes update <uuid> --status Scheduled
unleashed episodes update <uuid> --clear-description      # sets description to null
unleashed episodes update <uuid> --is-published           # publishes it
unleashed episodes update <uuid> --no-is-published        # hides it

Only nullable fields get a --clear- flag, and --description X --clear-description together is a usage error rather than a coin toss.

--podcast-uuid has no flag here — it is immutable after create, so the manifest keeps it off this command entirely.

Delete

unleashed episodes delete <uuid>

The API soft-deletes. There is no restore endpoint; recovery is a database task.

Short form videos

The same five actions, plus a video-file attachment.

unleashed short-form-videos create --title "Three mistakes new hosts make" \
    --podcast-episode-uuid 7f3a1c2e-... --target-date 2026-10-01
unleashed short-form-videos list --origin generated --standalone --sort -target_date
unleashed short-form-videos get <uuid>
unleashed short-form-videos update <uuid> --clear-podcast-uuid     # detach podcast and episode
unleashed short-form-videos delete <uuid>

A short stands alone, sits under a podcast, or sits under an episode. An episode brings its own podcast, so --podcast-uuid is only needed without one. Moving a short to another podcast drops its episode.

Video file

unleashed short-form-videos video-file attach <uuid> --file-uuid <file-uuid>
unleashed short-form-videos video-file detach <uuid>

Attaching replaces any video already attached; detaching keeps the file itself. Upload the video in the web app first — file uploads use the browser session, not the API token, so the CLI does not upload.

People

The same five actions, plus a metadata sub-resource.

unleashed people create --name "Ada Lovelace" --email ada@example.test \
    --metadata twitter=@ada --metadata source=cli
unleashed people list --no-has-email --last-outreach-before 2026-06-01 --sort last_outreach
unleashed people list --podcast-uuid 7f3a1c2e-...        # people who appeared on a show
unleashed people get <uuid>
unleashed people update <uuid> --last-outreach 2026-09-22 --clear-email
unleashed people delete <uuid>

--last-outreach-before includes people never contacted at all — they are the most overdue. last_outreach cannot be cleared once set, so it has no --clear- flag.

Metadata

Metadata is a flat map of strings, numbers, booleans, and nulls. --metadata KEY=VALUE on create and update sends strings, and update merges into the existing keys. To delete keys, send typed values, or replace the map, use the sub-resource:

unleashed people metadata merge <uuid> --set tier=gold --unset twitter
unleashed people metadata merge <uuid> --cli-input-json '{"episodes": 3, "vip": true}'
unleashed people metadata replace <uuid> --set only=this      # every other key is removed
unleashed people metadata replace <uuid> --cli-input-json '{}' # clear every key

replace with no --set and no --cli-input-json is refused rather than wiping the map.

Dry run

Every action takes --dry-run, which prints the request with the token redacted and sends nothing:

$ unleashed episodes create --dry-run --podcast-uuid ... --name ... --target-published-date ...
# profile default (from /Users/you/work/client-a/.unleashed/config)
POST https://unleashedpodcasts.com/apiv1/episodes
Accept: application/json
Authorization: Bearer ***redacted***
Content-Type: application/json

{
  "podcast_uuid": "7f3a1c2e-...",
  "name": "Episode 101: Something Useful",
  "target_published_date": "2026-09-15"
}

Episode transcripts

An episode's most recent transcript is read-only.

unleashed episodes transcript get <uuid>                    # status plus the full text
unleashed episodes transcript get <uuid> --format none      # status only, for polling
unleashed episodes transcript utterances list <uuid>        # speaker-attributed segments
unleashed episodes transcript utterances list <uuid> --with-words --max-items 50

An unfinished transcript reads with a null text and an empty utterance list, so the same command can be polled. An episode with no transcript at all exits 5.

Ideas

The episode-idea backlog. The podcast is optional; an idea without one sits in the general backlog.

unleashed ideas create --title "Interview a sound engineer" --podcast-uuid <uuid>
unleashed ideas list --status open --unassigned --sort position
unleashed ideas get <uuid>
unleashed ideas update <uuid> --status converted --converted-episode-uuid <episode-uuid>
unleashed ideas delete <uuid>

--status and --converted-episode-uuid exist only on update, and the API accepts a converted episode only alongside --status converted.

Tasks

Your own tasks, optionally tied to an episode. These routes need the tasks:read and tasks:write abilities; a token issued before they existed exits 8 — run unleashed login again.

unleashed tasks create --title "Book the guest" --due-date 2026-11-10 --episode-uuid <uuid>
unleashed tasks list --status todo --sort due_date
unleashed tasks get <uuid>
unleashed tasks update <uuid> --status completed --clear-due-date
unleashed tasks delete <uuid>

Leads

Shows worth approaching. The five actions, plus batch create.

unleashed leads create --show-name "The Example Show" --feed-url https://example.test/feed.xml
unleashed leads list --status Identified --min-llm-estimate 60 --sort -llm_estimate
unleashed leads update <uuid> --status Contacted

A single duplicate lead exits 6. Pass a JSON list of up to 25 leads to create them in one request:

unleashed leads create --cli-input-json "$(cat leads.json)"

The API answers a list with per-item outcomes — created, duplicate, or invalid — and one bad item never fails the rest. The CLI prints every outcome, then exits 3 if any item was invalid.

Output

Flag Behaviour
--output table Human-readable key/value block. Default on a TTY.
--output json The record as JSON. Default when piped.
--output text Tab-separated values, for awk and cut.
--query A JMESPath expression applied before rendering.
--dry-run Print the request with the token redacted and send nothing.
unleashed episodes create --query uuid --output text ...   # prints just the new UUID
unleashed episodes list --query "[].[uuid,name,status]" --output text | column -t

Exit codes

Code Meaning
0 Success
1 Unexpected or transport error
2 Usage error, including missing or unreadable configuration
3 Validation rejected by the API (422)
4 Auth failure (401), or login was denied or failed
5 Not found (404)
6 Conflict (409)
7 Rate limited after retries were exhausted (429)
8 Forbidden: the token lacks the ability (403)

Scripts can branch on these without parsing output.

A note on the Python package

This package is a CLI. The classes inside it are internal, unstable, and may change in any release — do not import them. A supported library API is planned once the resource abstraction has been tested against a second resource.

Contributing

See CONTRIBUTING.md. Every change is test-first and the build fails below 90% coverage.

License

MIT. See LICENSE.

Metadata

Release files for unleashed-podcasts-cli 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for unleashed-podcasts-cli 0.4.0
File Size Uploaded Provenance Actions
unleashed_podcasts_cli-0.4.0.tar.gz 107.0 kB With provenance Details for unleashed_podcasts_cli-0.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for unleashed-podcasts-cli 0.4.0
File Interpreter ABI Platform Actions
unleashed_podcasts_cli-0.4.0-py3-none-any.whl Python 3 none any Details for unleashed_podcasts_cli-0.4.0-py3-none-any.whl

Total release size: 144.0 kB

Release files / unleashed_podcasts_cli-0.4.0.tar.gz

Download URL unleashed_podcasts_cli-0.4.0.tar.gz
Size 107.0 kB
Tags Source
SHA-256 checksum
How to use checksums
d9319cd2ef57bee841797aa5fb006778e92533547d8a52147ae5adbfeb7aedcc
BLAKE2b-256 checksum
How to use checksums
137d8f877910572b6168e86d7bde2012ba775543c412ba867fca3af836363457
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Release files / unleashed_podcasts_cli-0.4.0-py3-none-any.whl

Download URL unleashed_podcasts_cli-0.4.0-py3-none-any.whl
Size 37.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0bfae03ef76f882280cdc7284da3fd04a7588b09559b26b9d6936fc338620cd0
BLAKE2b-256 checksum
How to use checksums
f8f20cb47b1fb21a898e17edd7eb3ec32e297842b9bb0c4a8da516432445e316
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Security for unleashed-podcasts-cli 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for unleashed_podcasts_cli-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page