Automate
AI actions and pipelines
Transcribe, tag or convert a file with one call, cached by content; or run actions on every file that lands in a folder, with conditions, chains and ready-made templates.
An action does one thing to one version of a file. The server comes with three, and plugins add more.
| Action | Does | Writes | Needs on the server |
|---|---|---|---|
side.transcribe | Speech to timed text, searchable | The transcript sidecar | ffmpeg, and SIDESPACE_TRANSCRIBE_URL pointing at any OpenAI-compatible /audio/transcriptions service (OpenAI, Groq, a local whisper.cpp or faster-whisper) with SIDESPACE_TRANSCRIBE_KEY |
side.tags | A vision model looks at key frames: description, tags, shot type, setting, mood | The ai.tags sidecar | Claude by default (SIDESPACE_ANTHROPIC_API_KEY), or a local model: SIDESPACE_TAGS_PROVIDER=openai, SIDESPACE_TAGS_URL, SIDESPACE_TAGS_MODEL |
side.convert | ffmpeg presets: mp4, webm, prores-proxy, wav, mp3, jpg, png, webp, gif | A new file next to the source, with a derived sidecar | ffmpeg |
GET /v1/spaces/{sid}/actions lists what this server offers. An action that isn't set up is listed with available: false and says which setting it needs, along with each action's params_schema.
Run one
sidespace actions "Film A"
sidespace run "Film A" /Ep01/a.mov side.transcribe --param language=en
sidespace run "Film A" /Ep01/a.mov side.convert --param preset=mp4 --param max=720
sidespace runs "Film A" /Ep01/a.mov// Waits for the result. Throws SideSpaceError (code run_failed) if it fails.
const run = await side.run("Film A", "/Ep01/a.mov", "side.transcribe", { language: "en" });
console.log(run.cached, run.result?.sidecars?.transcript);run = side.run("Film A", "/Ep01/a.mov", "side.transcribe", {"language": "en"})
print(run["cached"], run["result"]["sidecars"]["transcript"]["text"])curl -X POST "https://side.example.com/v1/spaces/$SPACE/runs" \
-H "Authorization: Bearer $SIDESPACE_TOKEN" -H "Content-Type: application/json" \
-d '{"action": "side.tags", "path": "/Ep01/a.mov"}'
# then GET /v1/spaces/$SPACE/runs/$RUN until state is done or failedA run moves through queued, running, then done, failed or canceled, with progress from 0 to 1 on the way. Editors can start runs; tokens need runs:write. POST …/runs/{rid}/cancel stops one. In the SDKs, startRun/start_run and waitRun/wait_run split starting from waiting.
Cached by content
The same bytes with the same params are processed once. Uploading the file again, copying it or restoring an old version doesn't call the model again: the run comes back at once with cached: true. Results that only write sidecars are reused across spaces; results that make files or renditions are reused within the space. Pass "fresh": true (CLI --fresh) to run again anyway.
Failures and retries
Failures that may pass (a timeout, a 5xx from the model service) retry after 30 seconds and then 3 minutes, three tries in all. Failures that won't (a rejected key, a file with no audio) fail at once with the reason in error.
Changes a run makes are recorded for the person who started it, with actor_kind: "plugin" and actor_via: <action>, so history shows that AI did it.
Pipelines
A pipeline is a standing rule for a space: every newly saved file that matches a path glob and a MIME prefix gets an action. Space managers make them; tokens need pipelines:manage.
sidespace pipeline create "Film A" side.tags --path "/dailies/**" --mime video/ --name "Tag dailies"
sidespace pipeline create "Film A" side.transcribe --mime audio/ --backfill # files already there too
sidespace pipeline list "Film A"
sidespace pipeline enable "Film A" pip_… --offawait side.createPipeline("Film A", { name: "Tag dailies", action: "side.tags", path_glob: "/dailies/**", mime_prefix: "video/" });side.create_pipeline("Film A", "Tag dailies", "side.tags", path_glob="/dailies/**", mime_prefix="video/")Like webhooks, each pipeline follows the journal on its own, from the moment it's created; --backfill (or POST …/pipelines/{pid}/backfill) covers files already there. Versions made by runs never start pipelines, so a "convert to mp4" pipeline can't loop.
Conditions
conditions narrow a pipeline to files that meet all of them: min_size and max_size (bytes), and from the media probe min_duration_s, max_duration_s, min_width, min_height and has_audio. A file that lacks the value fails the condition (an image has no duration), and unknown keys are refused, so a typo can't quietly match everything. A new version still being probed waits for the probe, for at most 15 minutes.
# Review copies of 4K masters only
sidespace pipeline create "Film A" side.convert --mime video/ --param preset=mp4 --param max=1920 --when min_width=2560
# Transcribe videos with sound that are longer than 10 seconds
sidespace pipeline create "Film A" side.transcribe --mime video/ --when has_audio=true --when min_duration_s=10curl -X POST "https://side.example.com/v1/spaces/$SPACE/pipelines" \
-H "Authorization: Bearer $SIDESPACE_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "Transcribe interviews", "action": "side.transcribe", "mime_prefix": "video/", "conditions": {"has_audio": true, "min_duration_s": 10}}'Chains
after: <action> starts a pipeline when a run of that action finishes on a matching file, instead of when the file is saved. Use it for steps that need an earlier result, such as tagging a video only once it is transcribed. Runs are deduplicated by version, action and params, so a cycle stops after one round, and a chained pipeline's backfill takes only files the action it follows is done on.
sidespace pipeline create "Film A" side.tags --mime video/ --after side.transcribeTemplates
Templates are ready-made sets of pipelines for common jobs. They live in the server's code, so an upgrade improves them.
| Template | Does |
|---|---|
video-insights | Tags each new video with AI, and transcribes videos with sound |
review-copies | A 1080p H.264 MP4 next to every new video wider than 2560 pixels |
interviews | Transcribes new audio, and videos with sound |
tag-images | Describes and tags new images |
sidespace pipeline templates "Film A"
sidespace pipeline template "Film A" video-insights --path "/Ep01/**" --backfillcurl "https://side.example.com/v1/spaces/$SPACE/pipeline-templates" -H "Authorization: Bearer $SIDESPACE_TOKEN"
curl -X POST "https://side.example.com/v1/spaces/$SPACE/pipeline-templates/video-insights" \
-H "Authorization: Bearer $SIDESPACE_TOKEN" -H "Content-Type: application/json" \
-d '{"path_glob": "/Ep01/**", "backfill": true}'
# → {"items": [the pipelines it made], "runs": runs started on files already there}Applying a template creates ordinary pipelines, which you then list, pause or delete like any other. A template whose action isn't set up on the server is still listed; its pipelines wait until the server is set up.
In the web app, space managers turn templates on from Automations in a folder's toolbar, for that folder or the whole space, and turn them off there.
From an AI agent
Over MCP, list_actions shows what can be done, run_action starts one and waits (60 seconds by default, up to 300), and get_run checks on it.