
Recipes
Small recipes for the work around the transcript.
These recipes are for a self-hosted VoxNotch instance. They use shipped HTTP routes and built-in workflow recipes; they do not depend on an external SDK, a hosted control plane, or hidden integration service.
Recipe automation board with upload, watch folder, webhook, destination, and workflow cards.
Before you run examples
export VOXNOTCH_URL="https://voxnotch.example.test"
export VOXNOTCH_API_KEY="vtk_example_do_not_use_this_key"
# The host and key above are fake. Replace them with your instance URL
# and a personal key created from a signed-in VoxNotch browser session.The examples are shaped to match backend routes, but IDs, paths, and keys are placeholders. Use jq if you want to copy the shell variable extraction exactly.
Catalog
Six useful starting points.
Each recipe is intentionally narrow. A useful automation page should leave you with a command you can adapt and a boundary you understand.
Upload, transcribe, poll
Send a media file to /recordings/upload, store the returned job_id, and poll until the job reaches a terminal state. This is the shortest path from a script to a transcript in your local library.
Watch folder to local transcript
Register an allowed server-local folder and trigger scan-now when you need deterministic ingest. It is useful for NAS drops, recorder sync folders, and batch backlogs.
Webhook notification test
Register a job lifecycle webhook and send a ping before relying on it. Webhooks are useful for downstream status updates, not as the only record of work.
Local destination send
Create a local destination under an allowed output root and push a recording export through the recording send route.
Manual clip render
Run the built-in manual_clip_render recipe with a recording and source range. The workflow creates cues, a clip selection, an EDL, a render manifest, and a local MP4 artifact.
Podcast production loop
Run the built-in podcast_studio_production_loop recipe to select episode assets, apply edit decisions, assemble local audio, and render video branches.
Recipe 1
upload_response="$(
curl -sS -X POST "$VOXNOTCH_URL/recordings/upload" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Idempotency-Key: field-notes-001" \
-F "file=@./field-notes.m4a" \
-F "model=base" \
-F "coverage=balanced"
)"
recording_id="$(printf '%s' "$upload_response" | jq -r '.recording_id')"
job_id="$(printf '%s' "$upload_response" | jq -r '.job_id // empty')"
if [ -n "$job_id" ]; then
curl -sS "$VOXNOTCH_URL/recordings/upload/$job_id" \
-H "X-API-Key: $VOXNOTCH_API_KEY"
else
printf 'No new job queued; recording_id=%s\n' "$recording_id"
fiIf the upload is detected as a duplicate, job_id may be null and the existing recording_id is returned instead.
Recipe 2
folder="$(
curl -sS -X POST "$VOXNOTCH_URL/watch-folders" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"path": "/srv/voxnotch/inbox",
"kind": "auto",
"recursive": false,
"enabled": true,
"ingest_existing": true
}'
)"
folder_id="$(printf '%s' "$folder" | jq -r '.id')"
curl -sS -X POST "$VOXNOTCH_URL/watch-folders/$folder_id/scan-now" \
-H "X-API-Key: $VOXNOTCH_API_KEY"The server must have watch-folder intake enabled, and the path must already exist under an approved local root.
Recipe 3
hook="$(
curl -sS -X POST "$VOXNOTCH_URL/webhooks" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.test/voxnotch",
"events": ["job.completed", "job.failed"],
"secret": "replace-with-a-long-random-secret"
}'
)"
hook_id="$(printf '%s' "$hook" | jq -r '.id')"
curl -sS -X POST "$VOXNOTCH_URL/webhooks/$hook_id/test" \
-H "X-API-Key: $VOXNOTCH_API_KEY"The backend validates public http(s) URLs, rejects private network targets, and treats webhook delivery as a notification that receivers should reconcile against current job state.
Recipe 4
dest="$(
curl -sS -X POST "$VOXNOTCH_URL/destinations" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Local exports",
"type": "local",
"path_root": "/srv/voxnotch/exports",
"output_types": ["export", "md"],
"enabled": true
}'
)"
dest_id="$(printf '%s' "$dest" | jq -r '.id')"
curl -sS -X POST "$VOXNOTCH_URL/recordings/$recording_id/send/$dest_id" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"output_type":"export","format":"markdown"}'Local destinations require DEST_ALLOWED_ROOTS. S3 destinations are available only when optional S3 backend support is installed and configured.
Recipe 5
curl -sS -X POST "$VOXNOTCH_URL/workflow-recipes/manual_clip_render/run" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recording_id": "rec_example",
"clip": {
"source_ranges": [
{ "start_ms": 65000, "end_ms": 95000, "label": "answer-that-matters" }
]
},
"render": {
"render_config": { "format": "mp4" }
},
"execute": true,
"idempotency_key": "answer-that-matters-v1"
}'The recipe accepts a source range or selected cue IDs. Reusing an idempotency key is the caller-controlled way to avoid duplicate work.
Recipe 6
curl -sS -X POST "$VOXNOTCH_URL/workflow-recipes/podcast_studio_production_loop/run" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recording_id": "rec_example",
"assemble_audio": {
"formats": ["wav", "mp3"],
"master": true
},
"render_vertical_clip": {
"targets": ["shorts_9_16"]
},
"execute": true,
"idempotency_key": "episode-17-production-loop"
}'The built-in podcast recipe selects assets, applies edit decisions, assembles local audio, and renders video branches.
Recipe 7
workflow="$(
curl -sS -X POST "$VOXNOTCH_URL/workflows" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Review package",
"dag": {
"nodes": [
{ "id": "cues", "type": "cue_generate", "needs": [] },
{ "id": "edl", "type": "edl_build", "needs": ["cues"] }
]
},
"enabled": true
}'
)"
workflow_id="$(printf '%s' "$workflow" | jq -r '.id')"
curl -sS -X POST "$VOXNOTCH_URL/workflows/$workflow_id/run" \
-H "X-API-Key: $VOXNOTCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"recording_id":"rec_example","execute":true}'Custom workflows are available, but useful step behavior is bounded by the local runner. Start with built-in recipes unless you need a custom DAG.
Local-first limits
Every recipe has a boundary.
Upload, polling, watch folders, local destinations, and workflow runs can stay on your machine. They are still limited by your CPU, GPU, disk, queue configuration, and model choices.
Webhooks and S3 destinations are different: they are deliberate egress. Use them when the automation value is worth the network boundary change.
The examples assume a personal API key already exists. The shipped backend only returns the plaintext key once, at creation time.
Checked
- Recipes were written from web/backend/app/api/uploads.py, watch.py, webhooks.py, destinations.py, workflows.py, export.py, and web/backend/app/services/workflow/recipes.py.
- Payload fields were checked against web/backend/app/models/ingestion.py, workflow.py, and recording.py.
- No SDKs, hosted integrations, OAuth provider connectors, or managed automation service are claimed on this page.
Keep the first automation boring.
One upload, one poll, one local output. Then add the parts that earn their complexity.