Skip to content

Commit 8cf343f

Browse files
committed
feat: add ingest API, offline validation, and persistent credential storage
1 parent 4da3b9d commit 8cf343f

29 files changed

Lines changed: 2030 additions & 103 deletions

README.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,16 @@ All three install paths use the same `@shotstack/cli` package on npm.
2424

2525
## Authentication
2626

27-
Set your API key as an environment variable:
27+
Save your API key once with `shotstack login`. It's stored in `~/.shotstack/credentials.json`, keyed per environment:
28+
29+
```sh
30+
shotstack login # prompts for the key (hidden), saves the v1 key
31+
shotstack login --env stage # save the stage key
32+
echo "$KEY" | shotstack login # non-interactive: read the key from stdin (CI/agents)
33+
shotstack logout [--env <name>] # remove a saved key (--all removes every env)
34+
```
35+
36+
Alternatively, set an environment variable, which **overrides** any saved key:
2837

2938
```sh
3039
export SHOTSTACK_API_KEY=...
@@ -46,6 +55,8 @@ shotstack render template.json --env stage
4655
SHOTSTACK_ENV=stage shotstack render template.json
4756
```
4857

58+
The `ingest` commands target the parallel `https://api.shotstack.io/ingest/{env}` base with the same API key.
59+
4960
## Commands
5061

5162
### `shotstack render <file>`
@@ -87,6 +98,38 @@ shotstack studio my-template.json --output json # emit {"url":"...","shortened":
8798

8899
When a browser can be launched, the command is silent — the URL only opens in the browser. On a headless server (no `$DISPLAY`, no `xdg-open`), the URL is printed to stdout instead so you can copy it elsewhere.
89100

101+
### `shotstack ingest <subcommand>`
102+
103+
Uploads your own local files (or fetches remote URLs) via the [Ingest API](https://shotstack.io/docs/guide/ingesting-footage/ingest-api/) and hosts them on Shotstack so they can be referenced from an Edit. An Edit can only reference media by URL, so this is how you get a local clip, image, audio file, or font into a render.
104+
105+
```sh
106+
# Upload a local file; --watch polls until ready and prints the hosted URL.
107+
shotstack ingest upload ./clip.mp4 --watch
108+
# → ready https://shotstack-ingest-api-v1-sources.s3.ap-southeast-2.amazonaws.com/.../source.mp4
109+
110+
shotstack ingest upload ./clip.mp4 --output json # returns {"id":"..."} immediately (no URL yet)
111+
shotstack ingest fetch https://example.com/v.mp4 --watch # host a copy of a remote URL
112+
shotstack ingest status <source-id> --watch # poll an existing source
113+
shotstack ingest list --output json # list ingested sources
114+
shotstack ingest delete <source-id> # remove a source from storage
115+
```
116+
117+
Sources are stored until you delete them. The bare `upload`/`fetch` commands return only an id — use `--watch` (or `ingest status`) to get the hosted URL once ingestion is `ready`.
118+
119+
### `shotstack login` / `shotstack logout`
120+
121+
Saves (or removes) your API key so it persists across shell sessions. Keys are stored per environment in `~/.shotstack/credentials.json`.
122+
123+
```sh
124+
shotstack login # prompt, save the v1 (production) key
125+
shotstack login --env stage # save the stage key
126+
echo "$KEY" | shotstack login --env v1 # non-interactive (CI / agents)
127+
shotstack logout --env stage # remove the stage key
128+
shotstack logout --all # remove every saved key
129+
```
130+
131+
The `SHOTSTACK_API_KEY` env var always takes precedence over a saved key, so CI and automation are unaffected.
132+
90133
### `shotstack feedback`
91134

92135
Opens a pre-filled GitHub issue with a sanitised dossier of your last 5 CLI invocations (render IDs, errors, exit codes). API keys and signed URLs are stripped at write time. You review and submit in your browser; nothing is transmitted automatically. Inspect the log at `~/.shotstack/log.jsonl`.

SKILL.md

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
22
name: shotstack
33
description: |
4-
Render video and poll render status via the Shotstack API.
5-
Use when generating clips, building automated video pipelines, or orchestrating
6-
cloud video renders from a script, agent, or CI workflow.
4+
Render video, poll render status, and upload local source files via the
5+
Shotstack API. Use when generating clips, building automated video pipelines,
6+
uploading footage/audio/images to use in an Edit, or orchestrating cloud video
7+
renders from a script, agent, or CI workflow.
78
NOT for: real-time streaming, or live broadcasting.
89
license: Apache-2.0
910
---
@@ -12,15 +13,28 @@ license: Apache-2.0
1213

1314
This skill loads in **terminal-based AI agents** (Claude Code, Cursor, Codex CLI, Gemini CLI, etc.). All operations here are shell commands. There is no embedded UI, no iframe, no inline canvas, no MCP tool surface — only `shotstack` invocations from a terminal. To hand off to a human, run `shotstack studio <file>`; this opens the user's default browser to a `shotstack.studio` URL. Tell the user to click Render *in the browser tab*, not in any UI inside the terminal.
1415

15-
Two commands for the Shotstack video rendering API. `render` submits an Edit JSON and returns a render ID; `status` polls a render until done.
16+
Commands for the Shotstack video rendering API. `render` submits an Edit JSON and returns a render ID; `status` polls a render until done. `ingest` uploads your own local files (or fetches remote URLs) and hosts them so they can be referenced from an Edit. `validate` lints an Edit — run it before every render. `studio` opens an edit in the browser editor for a human to preview, tweak, and render.
17+
18+
**Default loop — no API key required:** compose → `validate` (offline lint) → `studio` (browser preview). Only the API-backed commands (`render`, `status`, `ingest`) need a key. Reach for `render` when you specifically need a final MP4 exported in the cloud; reach for `studio` for everything else — it is the cheapest way to see an edit and the right default for any creative a human will review.
1619

1720
## Authentication
1821

22+
Save your API key once with `shotstack login` — it's stored in `~/.shotstack/credentials.json` (chmod 600), per environment, so you don't re-enter it every session:
23+
24+
```sh
25+
shotstack login # prompts (hidden input); saves the v1 (production) key
26+
shotstack login --env stage # save the stage key (Shotstack keys are env-specific)
27+
echo "$KEY" | shotstack login # non-interactive (CI / agents): read the key from stdin
28+
shotstack logout --env stage # forget one env; shotstack logout --all forgets all
29+
```
30+
31+
Or set an environment variable (overrides the saved key — best for CI):
32+
1933
```sh
2034
export SHOTSTACK_API_KEY=...
2135
```
2236

23-
Get a key at <https://app.shotstack.io>. Without a key, every command exits with code 1.
37+
Resolution precedence: **`SHOTSTACK_API_KEY` env var → saved key for the target `--env` → error.** Get a key at <https://app.shotstack.io>. **`validate` and `studio` need no key** — only the API-backed commands (`render`, `status`, `ingest`) require one; without a key *those* exit 1, while compose → validate → studio keeps working.
2438

2539
## Environments
2640

@@ -29,27 +43,49 @@ Get a key at <https://app.shotstack.io>. Without a key, every command exits with
2943
--env v1 → https://api.shotstack.io/edit/v1 (production, default)
3044
```
3145

32-
Use `--env stage` for experimentation. Stage is free; v1 charges real credits. Override with `SHOTSTACK_ENV` env var for the session.
46+
**The cheapest iteration path is `studio` — not `stage`.** `studio` previews client-side in the browser with no key and no credits (see the Studio section below); iterate there. `--env stage` gives a free cloud render (With watermark) for when you need a rendered video for review; v1 charges real credits. Override the target with the `SHOTSTACK_ENV` env var for the session. `ingest` commands hit the parallel `…/ingest/{env}` base automatically using the same key.
3347

3448
## Quickstart
3549

3650
```sh
37-
# Submit + poll in one command (most agent flows want this).
51+
# 1 — Validate offline. Schema + same-track overlaps + fonts + URLs. No key, no credits.
52+
shotstack validate template.json
53+
54+
# 2 — Preview in the browser. The DEFAULT next step: no key, no credits; a human hits Render.
55+
shotstack studio template.json
56+
# → opens https://shotstack.studio/s/<slug> and prints the short URL
57+
58+
# 3 — Export to MP4. Needs an API key and charges credits. Submit + poll in one command.
3859
shotstack render template.json --watch
3960
# → done https://shotstack-api-v1-output.s3.amazonaws.com/.../01ja7-x8m2k-39rzv-cmvxve.mp4
4061

41-
# Submit only (returns ID).
42-
shotstack render template.json --output json
43-
# → {"id":"01ja7-x8m2k-39rzv-cmvxve"}
44-
45-
# Poll an existing render to terminal state.
62+
# Poll an existing render to a terminal state.
4663
shotstack status 01ja7-x8m2k-39rzv-cmvxve --watch
4764
# → done https://shotstack-api-v1-output.s3.amazonaws.com/.../01ja7-x8m2k-39rzv-cmvxve.mp4
4865
```
4966

50-
## Hand-off to a human before rendering
67+
## Uploading local files to use in an Edit
68+
69+
An Edit can only reference media by **URL** — a local path in an `asset.src` will not render. To use a local file, host it first with `shotstack ingest upload`, then put the returned URL in your Edit.
70+
71+
```sh
72+
# Upload a local file and poll until it's hosted. --watch prints the URL.
73+
shotstack ingest upload ./clip.mp4 --watch --output json
74+
# → {"id":"zzytey4v-...","status":"importing"}
75+
# → {"id":"zzytey4v-...","status":"ready","source":"https://shotstack-ingest-api-v1-sources.s3...amazonaws.com/.../source.mp4",...}
76+
77+
# Capture the hosted URL straight into a variable:
78+
SRC=$(shotstack ingest upload ./clip.mp4 --watch --output json | tail -n1 | jq -r .source)
79+
# …then reference "$SRC" as an asset.src in your Edit JSON and run `shotstack render`.
80+
```
81+
82+
The bare command (no `--watch`) returns only an `id`; the hosted `source` URL does not exist until ingestion is `ready`. Always `--watch` (or follow up with `shotstack ingest status <id> --watch`) when you need the URL.
83+
84+
Other ingest subcommands: `ingest fetch <url>` (host a copy of a remote URL), `ingest status <id>`, `ingest list`, `ingest delete <id>`. **Read [`references/ingest.md`](references/ingest.md) for the full upload→render workflow, status values, and supported file types.**
85+
86+
## Default workflow: preview in Studio (no key, no credits)
5187

52-
When a human is in the loop and may want to tweak the result, prefer **`shotstack studio <file>`** over `shotstack render`. By default it posts the JSON to the share API and opens `https://shotstack.studio/s/<slug>` in the browser — a short, shareable URL. No API key, no render credits charged. The human can play, edit, and decide whether to render.
88+
**`shotstack studio <file>` is the default way to look at an edit** — prefer it over `render` unless you specifically need a cloud-exported MP4. It posts the JSON to https://shotstack.studio and opens `https://shotstack.studio/s/<slug>` in the browser — a short, shareable URL. No API key, no render credits charged; it previews client-side, so it works with no key. The human plays, edits, and decides whether to spend credits on a render.
5389

5490
```sh
5591
shotstack studio template.json # opens browser + prints short URL
@@ -62,15 +98,15 @@ On headless systems (no `xdg-open`, no `$DISPLAY`) the browser launch silently n
6298

6399
If the share API is unreachable, the command falls back to the inline base64url form automatically and prints a stderr warning. Shares expire after 30 days.
64100

65-
Use `render` only when you're confident the JSON is final, or there's no human to review.
101+
**Default to `studio` for anything non-trivial** — multi-scene, multi-track, or any creative an interested human will review. It previews client-side, costs nothing, and lets the human hit Render when it's right. Use `render` directly only for single-shot/simple edits, automated pipelines, or when told "just render it".
66102

67103
## Four CLI rules
68104

69105
1. **Pipe → `--output json`.** Default output is human-readable. When parsing programmatically or piping to another command, always pass `--output json`.
70106

71107
2. **Use `--watch`, not a polling loop.** `shotstack render <file> --watch` submits and polls in one shot; `shotstack status <id> --watch` polls an existing render. Both exit when terminal: `done` (exit 0) or `failed` (exit 1). Don't write `while true; do ...; sleep 3; done`.
72108

73-
3. **Fetch the current schema and docs before generating Edit JSON.** The Shotstack API evolves; LLM training data is often stale. Pull <https://shotstack.io/docs/api/api.edit.json> and <https://shotstack.io/docs/guide/llms-full.txt> for the current schema and guides before composing an Edit from scratch.
109+
3. **Compose from `shared/agent-core.md`, then `shotstack validate`.** Its cheatsheet carries the property names, enums (transition/effect/position/fit) and rich-text fields you need — no schema download required. `shotstack validate <file>` then checks the JSON against the live schema offline and flags same-track overlaps, unloaded fonts and non-public URLs before you spend a credit. (For edge cases the full schema is at <https://shotstack.io/docs/api/api.edit.json> and the guide at <https://shotstack.io/docs/guide/llms-full.txt>.)
74110

75111
4. **Hand off to a human via `shotstack studio <file>` when uncertain.** Don't burn render credits iterating. Generate JSON → `shotstack studio file.json` (opens browser) → human reviews/tweaks → render only when right.
76112

@@ -113,9 +149,13 @@ Authoritative sources, in order of preference:
113149

114150
This skill ships sub-references for the gnarly bits:
115151

152+
- [`references/ingest.md`](references/ingest.md) — uploading local files (and fetching URLs) so they can be used in an Edit: the upload→render workflow, `--watch`, status values, supported types
116153
- [`references/timeline.md`](references/timeline.md) — track layering, transitions, background music via audio assets
154+
- [`references/positioning.md`](references/positioning.md) — coordinate model, `position`/`offset` (fraction of frame, +y up), clip bounding box & `fit`, text sizing, transform order
117155
- [`references/caption.md`](references/caption.md) — sizing per resolution, default style, the 5 named presets, alias pattern (asset type: `rich-caption`)
118156
- [`references/svg.md`](references/svg.md) — required attrs, supported elements
157+
- [`references/html5.md`](references/html5.md) — HTML5 asset: fields, preloaded libs (gsap/d3/anime/lottie), browser harness, sizing, worked examples
158+
- [`references/html5-snippets.md`](references/html5-snippets.md) — copy-paste motion-graphic clips: kinetic headline, count-up/price odometer, shine sweep, pulsing CTA, film grain
119159
- [`references/fonts.md`](references/fonts.md) — built-in fonts, Google Fonts URL pattern, custom-font workflow
120160
- [`references/asset-library.md`](references/asset-library.md) — placeholder videos, images, music
121161
- [`references/troubleshooting.md`](references/troubleshooting.md) — common errors and fixes

bun.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@shotstack/cli",
3-
"version": "0.4.1",
3+
"version": "0.5.0",
44
"description": "Command-line interface for the Shotstack video rendering API.",
55
"license": "Apache-2.0",
66
"homepage": "https://github.com/shotstack/shotstack-cli",
@@ -28,7 +28,7 @@
2828
"test": "bun test"
2929
},
3030
"dependencies": {
31-
"@shotstack/schemas": "^1.10.10",
31+
"@shotstack/schemas": "^1.12.2",
3232
"commander": "^12.1.0",
3333
"zod": "^4.4.3"
3434
},

0 commit comments

Comments
 (0)