{
  "markdown": "# Save to Spotify\n\nA command-line tool for saving audio content to Spotify. Built for agents and automation, generate a daily briefing, language lesson, or meeting recap, then push it to Spotify where it's available alongside your other listening.\n\n## What you can make\n\nPoint your agent at content you have, and get a finished episode on Spotify:\n\n- **Morning briefing** — your calendar, tasks, and overnight repo activity as a 3-minute daily brief\n- **Deep dive** — turn a topic, article, or PDF into a personalized audio explainer\n- **Meeting recap** — drop in a transcript, get a 2-minute summary with action items\n- **Travel guide** — a private audio itinerary for your next trip\n- **News roundup** — a daily roundup from the newsletters, feeds, and sites you follow\n- **Bedtime story** — a custom story using your kid's name and favorite setting\n\nDescribe what you want in plain English; the agent sources, scripts, narrates, and saves it.\n\n## Making the audio\n\nThis CLI *saves* audio to Spotify — it does not generate speech itself. You bring a voice engine:\n\n- **Kokoro** (recommended) — free, local, no API key, no limits (~340 MB)\n- **OpenAI TTS** — high quality, needs `OPENAI_API_KEY`\n- **ElevenLabs** — most natural, needs `ELEVENLABS_API_KEY`\n\nNo key? Kokoro is the default. Already have an OpenAI or ElevenLabs key? The agent detects it and offers to use it — no extra setup.\n\nRun `save-to-spotify tts status` to see what's available, or `save-to-spotify tts add` to register any other engine.\n\n## Quick Start\n\nPrompt your agent to install:\n\n```text\n> Install Save to Spotify by running https://saveto.spotify.com/install.sh\n```\n\nInstalls the CLI and exposes the skill to Claude Code, Cursor, Codex, and any agent that reads `.agents/skills/`.\n\nOnce installed, invoke with `/save-to-spotify` (Claude Code) or `$save-to-spotify` (Codex), or just describe what you want in plain English.\n\n## Install\n\n### Curl-bash\n\nSame script the Quick Start prompt runs:\n\n```bash\ncurl -fsSL https://saveto.spotify.com/install.sh | bash\n```\n\nCommon options:\n\n```bash\n# Pin a specific version\ncurl -fsSL https://saveto.spotify.com/install.sh | bash -s -- --version 0.2.0\n\n# Custom install directory\ncurl -fsSL https://saveto.spotify.com/install.sh | bash -s -- --dir ~/.local/bin\n\n# Binary only, skip the agent skill\ncurl -fsSL https://saveto.spotify.com/install.sh | bash -s -- --no-skills\n```\n\n### Skills.sh\n\nInstall the agent skill to Claude Code, Cursor, Codex, and 50+ other agents:\n\n```bash\nnpx skills add spotify/save-to-spotify\n```\n\n### ClawHub / OpenClaw\n\nSave to Spotify is listed on ClawHub as [`@spotify/save-to-spotify`](https://clawhub.ai/spotify/save-to-spotify). Install it into your active OpenClaw workspace:\n\n```bash\nopenclaw skills install @spotify/save-to-spotify\n```\n\nThe ClawHub listing installs the agent skill. The skill uses the `save-to-spotify` CLI, so if the binary is not already on your `PATH`, run the curl-bash installer above.\n\n### Claude Code plugin marketplace\n\n```\n/plugin marketplace add spotify/save-to-spotify\n/plugin install save-to-spotify@save-to-spotify\n```\n\n### Manual\n\nIf you can't run the install script:\n\n1. Download `save-to-spotify-{os}-{arch}-v{version}.zip` and its matching `.sha256` from the [releases page](https://github.com/spotify/save-to-spotify/releases).\n2. Verify integrity:\n   ```bash\n   shasum -c save-to-spotify-darwin-arm64-v0.2.0.zip.sha256\n   ```\n3. Unzip, you get the binary plus a `skills/save-to-spotify/` tree.\n4. Move the binary to a directory on your `PATH` and `chmod +x` it (not needed on Windows).\n5. (Optional) Copy `skills/save-to-spotify/` into your agent's skill directory, e.g. `~/.claude/skills/save-to-spotify/` or `~/.cursor/skills/save-to-spotify/`.\n\n### Windows notes\n\n- Run the install script in **Git Bash** (ships with Git for Windows); it installs `save-to-spotify.exe` to `~/.local/bin` and offers a PATH fix.\n- The Windows binaries are not code-signed yet: SmartScreen may block the first run. Unblock with right-click → Properties → Unblock, or `Unblock-File save-to-spotify.exe` in PowerShell.\n\n### Build from source\n\nRequires [Go](https://go.dev/dl/) 1.21+.\n\n```bash\ngo build -ldflags \"-X github.com/spotify/save-to-spotify/cmd.commit=$(git rev-parse --short HEAD)\" \\\n  -o save-to-spotify .\nsudo mv save-to-spotify /usr/local/bin/\n```\n\n## Authentication\n\nLog in once, the CLI stores your token and refreshes it automatically.\n\n```bash\n# Opens your browser to authorize (default)\nsave-to-spotify auth login\n\n# Headless mode, prints a URL to open on any device, then you paste back the redirect URL\n# Useful for SSH sessions and remote servers\nsave-to-spotify auth login --no-browser\n\n# Check if you're logged in\nsave-to-spotify auth status\n\n# Log out (removes stored credentials)\nsave-to-spotify auth logout\n```\n\nTokens are stored in `~/.config/save-to-spotify/token.json` (using `$XDG_CONFIG_HOME` if set). If you use DPoP-bound tokens (the default), the sibling `dpop_key.json` is also required. For CI and headless environments, see the [CI / Automation guide](docs/ci-automation.md).\n\n## Commands\n\n### upload\n\nThe primary command. Uploads a media file and creates an episode in one step. This is the main entrypoint for agents, a single command handles show creation, file upload, and episode metadata.\n\n```bash\nsave-to-spotify upload <file> --title <title> [flags]\n```\n\n| Flag | Description |\n|---|---|\n| `--title` | Episode title (required) |\n| `--show-id` | Target show ID or URI |\n| `--new-show` | Create a new show with this title and save the file to it |\n| `--summary` | Episode description |\n| `--image` | Cover image file (`.jpg`/`.png`, max 1 MB) |\n| `--language` | Language code (default: `en`) |\n\nIf `--show-id` or `--new-show` is not specified, the CLI will use your most recently created show, or create a new one if none exists.\n\n```bash\n# Save an audio file to Spotify, uses your most recent show (or creates one automatically)\nsave-to-spotify upload ./recap.mp3 --title \"Monday Standup Recap\"\n\n# Save to a specific show\nsave-to-spotify upload ./lesson.mp3 --title \"Spanish Lesson 12\" --show-id spotify:show:abc123\n\n# Organize saved items into separate shows\nsave-to-spotify upload ./lecture.mp3 --title \"CS 101 — Lecture Notes Week 6\" --new-show \"Lecture Notes\"\n\n# With cover image and description\nsave-to-spotify upload ./briefing.mp3 \\\n  --title \"Morning Briefing\" \\\n  --summary \"Weather and calendar for today\" \\\n  --image ./cover.jpg\n```\n\n**Supported formats:** `.mp3`, `.m4a`, `.wav`, `.ogg`.\n\n### shows\n\nShows are containers for your saved content. You can use a single show for everything or create separate shows to organize by topic (e.g. \"Daily Briefings\", \"Language Practice\", \"Meeting Recaps\").\n\n```bash\n# List your shows\nsave-to-spotify shows\n\n# Create a show\nsave-to-spotify shows create --title \"Lecture Notes\" --summary \"Audio summaries of CS 101 lectures\"\n\n# Get show details\nsave-to-spotify shows get <id>\n\n# Delete a show and all its episodes\nsave-to-spotify shows delete <id>\n```\n\n`shows create` flags:\n\n| Flag | Description |\n|---|---|\n| `--title` | Show title (required) |\n| `--summary` | Show description (default: \"(no description)\") |\n| `--image` | Cover image file (`.jpg`/`.png`, max 1 MB) |\n| `--language` | Language code (default: `en`) |\n\n### episodes\n\nEach saved file becomes an episode within a show.\n\n```bash\n# List episodes (for your most recent show)\nsave-to-spotify episodes\n\n# List episodes for a specific show\nsave-to-spotify episodes --show-id spotify:show:abc123\n\n# Create an episode (more control than `upload`, same underlying operation)\nsave-to-spotify episodes create --title \"Sprint Review Notes\" --file ./review.mp3\n\n# Check if an episode is ready for playback\nsave-to-spotify episodes status <episode-id>\n\n# Wait until an episode is ready (default timeout: 5m)\nsave-to-spotify episodes status <episode-id> --wait\n\n# Wait with a custom readiness timeout\nsave-to-spotify episodes status <episode-id> --wait 2m\n\n# Delete an episode\nsave-to-spotify episodes delete <episode-id>\n```\n\n`episodes create` flags:\n\n| Flag | Description |\n|---|---|\n| `--title` | Episode title (required) |\n| `--file` | Media file path (required) |\n| `--show-id` | Target show ID or URI (default: most recent show) |\n| `--summary` | Episode description |\n| `--image` | Cover image file (`.jpg`/`.png`, max 1 MB) |\n| `--language` | Language code |\n\n### timeline\n\nAdd chapter markers and in-player companion content to saved episodes. A timeline can include chapters, images, external links, and Spotify-native references via `spotify_entity`. Prefer `spotify_entity` whenever the thing you want listeners to open already exists on Spotify, prefer full `spotify:...` URIs over bare IDs or `open.spotify.com` URLs, and note that a `link` and a `spotify_entity` can both appear in the same timeline when both destinations are useful.\n\n```bash\n# Get timeline items for an episode\nsave-to-spotify timeline get <episode-id>\n\n# Set (replace all) timeline items from a JSON file\nsave-to-spotify timeline set --episode-id <episode-id> --from-file timeline.json\n\n# Delete all timeline items\nsave-to-spotify timeline delete <episode-id>\n```\n\nExample `timeline.json`:\n\n```json\n{\n  \"items\": [\n    { \"chapter\": { \"title\": \"Introduction\", \"start_time_ms\": 0 } },\n    { \"chapter\": { \"title\": \"Key Concepts\", \"start_time_ms\": 45000 } },\n    { \"spotify_entity\": { \"start_time_ms\": 60000, \"duration_ms\": 30000, \"uri\": \"spotify:track:abc123\" } },\n    { \"link\": { \"start_time_ms\": 95000, \"duration_ms\": 20000, \"url\": \"https://example.com/slides\" } },\n    { \"chapter\": { \"title\": \"Summary\", \"start_time_ms\": 120000 } }\n  ]\n}\n```\n\nTimeline rules:\n\n- `chapter`: optional, but if present there must be at least 2; the first must start at `0`.\n- `image`: requires a positive `duration_ms` and a local `.jpg`/`.png` file path or an `image_token`.\n- `link`: requires a positive `duration_ms` and an HTTP(S) URL.\n- `spotify_entity`: requires a full `spotify:...` URI; `duration_ms` is optional but must be positive when set.\n- Companion items (`image`, `link`, `spotify_entity`) must not overlap in time, but a `link` and a `spotify_entity` may share the same segment/chapter as long as their windows do not overlap.\n\n### list\n\nAlternative command for listing your content.\n\n```bash\n# List episodes for the most recent show\nsave-to-spotify list\n\n# List episodes for a specific show\nsave-to-spotify list episodes --show-id spotify:show:abc123\n\n# List all shows\nsave-to-spotify list shows\n```\n\n### Other commands\n\n| Command | Description |\n|---|---|\n| `update` | Check for and install CLI updates |\n| `token` | Print access token to stdout (for piping to `curl`, etc.) |\n| `version` | Print version and commit hash |\n\n## Global flags\n\n| Flag | Description |\n|---|---|\n| `--json` | Output results as JSON (for scripting and automation) |\n| `--timeout <duration>` | API request timeout (default: `30s`, e.g. `1m`, `90s`) |\n\n## Documentation\n\n- [Agent integration](docs/agent-integration.md) — JSON mode, typical workflow, error handling\n- [CI / Automation](docs/ci-automation.md) — GitHub Actions setup, headless auth, credential persistence\n\n## Environment variables\n\n| Variable | Purpose | Default |\n|---|---|---|\n| `SAVE_TO_SPOTIFY_AUTH_TOKEN` | Bearer token override (expires in ~1 hour, no auto-refresh — see [CI guide](docs/ci-automation.md)) | — |\n| `SAVE_TO_SPOTIFY_BACKEND_URL` | Override the backend API URL | `https://saveto.spotify.com` |\n| `SAVE_TO_SPOTIFY_TIMEOUT` | API request timeout (e.g. `30s`, `2m`) | `30s` |\n| `SAVE_TO_SPOTIFY_CLIENT_ID` | OAuth client ID override | built-in |\n| `SAVE_TO_SPOTIFY_NO_UPDATE_CHECK` | Disable passive update checks | off |\n| `SAVE_TO_SPOTIFY_HEADERS` | Additional headers for backend requests (JSON object of header name/value pairs; only `X-STS-*` headers are accepted) | — |\n",
  "bytes": 11828,
  "sha": "113a88c8a0bb51d183e48cc7753581d60eaa77a029a5f5e45ee540fe824b4889",
  "repo_slug": "spotify/save-to-spotify",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_spotify_save_to_spotify_save_to_spotify_a90edeba/readme"
}