Skip to content
VoxNotch

Developers

A private automation surface for recordings, jobs, and media runs.

VoxNotch gives scripts and agents a concrete HTTP surface on the instance you operate: upload media, poll transcription jobs, register watch folders, receive job webhooks, send outputs to local or S3 destinations, and run workflow recipes. The developer layer is API-first and local-first rather than a packaged SDK, provider connector hub, or hosted automation control plane.

Best for
Agents, scripts, internal tools
Interface
HTTP API on your VoxNotch instance
Auth
Scoped personal API keys
Media flow
Upload, transcribe, run recipes, export
Boundary
Only configured destinations leave the box

Developer automation pipeline with API key, upload, worker queue, recipes, webhook, and destination.

Shipped surface

What you can script.

These capabilities are named from the backend routers and models, not from imagined integrations. Routes use the same per-user auth boundary as the app.

  1. Personal API keys

    Create and revoke keys from a signed-in browser session, then call scoped API routes with X-API-Key or an Authorization bearer token shaped like that key.

    Key management itself is session-only. A personal API key cannot mint or revoke more keys.

  2. Upload and job polling

    POST audio or video to /recordings/upload, receive recording_id and maybe job_id, then poll /recordings/upload/{job_id}.

    Duplicates can return status=duplicate with job_id=null. Uploads accept model, language, coverage, normalize, vad, hotwords, and batch_id form fields.

  3. Watch folders

    Register server-local folders under /watch-folders, list detected files, and trigger /scan-now for deterministic ingest.

    This is fail-closed until an operator configures WATCH_ALLOWED_ROOTS and the requested path resolves under one of those server folders.

  4. Webhooks

    Register per-user job lifecycle hooks and test them before relying on them.

    Subscription events are job.queued, job.completed, and job.failed. The /test endpoint sends a ping. Delivery is outbound HTTP; receivers should still reconcile current state through the API.

  5. Destinations

    Create local or S3 output destinations and push a recording export through POST /recordings/{recording_id}/send/{dest_id}.

    Local paths require DEST_ALLOWED_ROOTS. S3 requires optional backend support and is explicit network egress.

  6. Workflow runs

    Create custom workflow DAGs, execute them, list /runs, inspect /runs/{run_id}, or retry from a failed step.

    Built-in recipe IDs are manual_clip_render and podcast_studio_production_loop.

Example setup

shell
export VOXNOTCH_URL="https://voxnotch.example.test"
export VOXNOTCH_API_KEY="vtk_example_do_not_use_this_key"

# Create the real key from a signed-in VoxNotch browser session.
# The plaintext key is shown once, then stored only as a hash.

The host and key are fake. Key creation is deliberately not shown as an API-key curl call because key management is session-only.

Upload and poll

shell
upload_response="$(
  curl -sS -X POST "$VOXNOTCH_URL/recordings/upload" \
    -H "X-API-Key: $VOXNOTCH_API_KEY" \
    -H "Idempotency-Key: field-notes-2026-09-01" \
    -F "file=@./field-notes.m4a" \
    -F "model=base" \
    -F "language=auto" \
    -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 'duplicate or no job queued for recording %s\n' "$recording_id"
fi

The upload route returns 202. A duplicate upload may return an existing recording_id with job_id null, so scripts should branch instead of assuming every upload starts a new worker job.

Watch folder and webhook

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"

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"

Watch folders are server-local. Webhooks leave the box as notifications; the API remains the source of truth for job state.

Destination send

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 an allowed server path. S3 destinations use the same route shape, but they are explicit network egress and require optional S3 support on the backend.

Built-in recipe

shell
curl -sS "$VOXNOTCH_URL/workflow-recipes" \
  -H "X-API-Key: $VOXNOTCH_API_KEY"

run="$(
  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": 12000, "end_ms": 42000, "label": "launch-clip" }
        ]
      },
      "execute": true,
      "idempotency_key": "launch-clip-001"
    }'
)"

run_id="$(printf '%s' "$run" | jq -r '.id')"
curl -sS "$VOXNOTCH_URL/runs/$run_id" \
  -H "X-API-Key: $VOXNOTCH_API_KEY"

manual_clip_render creates cues, selects a manual clip range, builds an EDL, and renders a local MP4 artifact when the underlying media and render path are available.

Custom workflow

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",
      "description": "Small per-user workflow example",
      "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,
    "idempotency_key": "review-package-001"
  }'

Custom DAG nodes must have id and type, and dependencies must point to known node IDs. Useful step types depend on what the local workflow runner supports.

Boundary model

Local automation and egress are different choices.

SurfaceStays local by designCan leave the box
Upload, polling, local recording stateRuns against your own API, queue, database, and storage.Only if your deployment routes traffic through external infrastructure.
Watch folders and local destinationsServer-local paths under approved destinations.Not by themselves. Mounted network storage is your infrastructure choice.
WebhooksConfiguration and run history remain tied to the user that created them.Delivery is outbound HTTP to a URL you configure.
S3 destinationsDestination metadata remains in VoxNotch.Objects are pushed to the bucket, endpoint, and credentials reference you configure.

Checked against uploads.py, watch.py, webhooks.py, destinations.py, workflows.py, and services/workflow/recipes.py on 2026-09-01.

Limits

What this page does not claim.

There is no public SDK claim here. The supported automation story is the shipped HTTP API plus browser-managed personal API keys.

There is no cloud integration marketplace here. Webhooks and S3 destinations exist, but they are owner-configured egress paths.

There is no managed uptime claim here. Queue speed, model speed, webhook reachability, and storage behavior are properties of the machine and network you operate.

Developer questions

Can an API key create another API key?

No. Key management is fenced to a signed-in browser or development session. A personal API key can call scoped API routes, but it cannot mint or revoke keys.

Does running a webhook break the local-first promise?

It changes the privacy boundary for that workflow. The transcript pipeline still runs locally, but a webhook is an outbound HTTP POST to a URL you configure. Treat it as explicit egress.

Can I build my own workflow DAG?

Yes. The backend exposes /workflows and /workflows/{workflow_id}/run for per-user DAGs. Built-in recipes are separate catalog entries for common jobs.

Checked

  1. Endpoint claims were checked against web/backend/app/api/api_keys.py, uploads.py, watch.py, webhooks.py, destinations.py, workflows.py, and export.py.
  2. Payload fields were checked against web/backend/app/models/ingestion.py, workflow.py, and recording.py.
  3. Recipe IDs and recipe behavior were checked against web/backend/app/services/workflow/recipes.py.

Start with one scripted upload.

Create a personal key in your local instance, upload one media file, then decide whether watch folders, webhooks, destinations, or recipes earn their place.