Skip to content
VoxNotch

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.

Interface
HTTP API
Auth
X-API-Key with a personal key
Recipe catalog
/workflow-recipes
Built-in runs
manual_clip_render, podcast_studio_production_loop
Network caveat
Webhooks and S3 destinations are explicit egress

Recipe automation board with upload, watch folder, webhook, destination, and workflow cards.

Before you run examples

shell
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.

A simple automation flow from media upload to local transcript job polling.
Expected flow: upload request, queued job, polling loop, local recording.

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.

A local watch folder automation flow ending in a transcription job.
Expected flow: allowed input folder, scan-now, recording, local job.

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.

A webhook notification workflow from job event to downstream receiver.
Expected flow: job event, signed webhook, receiver, reconciliation.

Local destination send

Create a local destination under an allowed output root and push a recording export through the recording send route.

A local destination workflow moving a transcript export to an allowed folder.
Expected flow: recording, export, allowed local destination, delivery audit.

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.

A manual clip render workflow from recording range to MP4 artifact.
Expected flow: recording, selected range, EDL, local rendered 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.

A podcast automation workflow from episode assets to mastered audio and video branches.
Expected flow: episode assets, decisions, mastered audio, video branches.

Recipe 1

shell
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"
fi

If the upload is detected as a duplicate, job_id may be null and the existing recording_id is returned instead.

Recipe 2

shell
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

shell
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

shell
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

shell
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

shell
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

shell
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

  1. 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.
  2. Payload fields were checked against web/backend/app/models/ingestion.py, workflow.py, and recording.py.
  3. 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.