Skip to content

API Reference

OneSeventyFour edited this page May 14, 2026 · 1 revision

REST API reference

Every Next.js API route under host/byh_app/backyardhero/src/pages/api/. All routes return JSON unless noted; non-2xx responses include an error field.

The browser hits these directly. There's no auth — Backyard Hero assumes a trusted local network. Do not expose port 1776 to the public internet.

Shows

GET /api/shows

List every show.

[
  { "id": 1, "name": "Demo Show", "duration": 30, "version": "...", ... },
  ...
]

POST /api/shows

Create a show.

Body:

{
  "name": "...",
  "duration": 30,
  "version": "...",
  "runtime_version": "...",
  "display_payload": "[]",
  "runtime_payload": "[]",
  "authorization_code": "1234",
  "protocol": "BKYD_TS_HYBRID",
  "audio_file": "{}",
  "receiver_locations": "{}",
  "receiver_labels": "{}",
  "show_receivers": "[]"
}

PATCH /api/shows/[id]

Update fields on a show. Body limit 2 MB (because display_payload can be large for elaborate shows).

DELETE /api/shows/[id]

Delete a show. Cascades to racks (FK).

POST /api/shows/upload-audio

Multipart upload of an audio file. Stored under public/uploads/audio/<filename>. Returns:

{ "url": "/uploads/audio/<filename>", "durationSec": 156.7 }

The caller (Show Builder) saves the URL to Show.audio_file.tracks[].url.

Inventory

GET /api/inventory

List every inventory row.

POST /api/inventory

Create an inventory item.

PATCH /api/inventory/[id]

Update an inventory item.

DELETE /api/inventory/[id]

Delete an inventory item. Cascades to inventoryFiringProfile (FK).

GET /api/inventory/[id]/firing-profile

Fetch the firing profile for one item:

{
  "id": 12,
  "inventory_id": 3,
  "youtube_link": "https://...",
  "youtube_link_start_sec": 5,
  "shot_timestamps": [[100, 250], [500, 700, "#ff0000"], ...]
}

PATCH /api/inventory/[id]/firing-profile

Update the shot_timestamps JSON for one item.

POST /api/inventory/[id]/reprocess-profile

Spawn process_firing_profiles.py for one inventory item. Body can include detection knobs (threshold_ratio, etc.).

POST /api/inventory/reprocess-all-profiles

Spawn process_firing_profiles.py in --reprocess-all mode.

POST /api/inventory/parse-shell-descriptions

Take a paste of human shell descriptions and return structured shells. Calls OpenAI via OPENAI_API_KEY.

Body:

{ "text": "12 x 3\" red peony\n6 x 4\" gold willow with crackle..." }

Response:

{ "shells": [ { "name": "...", "size": 3, "color": "red", "effect": "peony", "count": 12 }, ... ] }

Racks

GET /api/racks?show_id=<id>

List racks for a show.

POST /api/racks

Create a rack. Body includes show_id, name, x_rows, x_spacing, y_rows, y_spacing, cells (JSON), fuses (JSON).

GET /api/racks/[id]

Fetch one rack. Parses cells and fuses JSON into objects.

PATCH /api/racks/[id]

Update a rack.

DELETE /api/racks/[id]

Delete a rack.

Receivers

GET /api/receivers

List every row in the Receivers SQL table. Hydrates JSON columns (cues_data, metadata, config_data) into objects.

POST /api/receivers

Create a receiver row.

{
  "id": "RX146",
  "label": "Main Receiver",
  "type": "BKYD_TS_24_1",
  "cues_data": { "RX146": [1,2,3,4,5,6,7,8] },
  "enabled": 1,
  "metadata": {}
}

GET /api/receivers/[id]

Fetch one receiver.

PATCH /api/receivers/[id]

Update one receiver. Bumps configuration_version. Merges config_data (does not replace).

DELETE /api/receivers/[id]

Delete one receiver. The daemon will forget it on next reload.

POST /api/receivers/reload

Drop { "type": "reload_receivers" } for the daemon. The daemon re-reads the table and reconciles the dongle's poll list.

POST /api/receivers/[id]/retry

Drop { "type": "retry_receiver", "ident": "<id>" } for the daemon.

POST /api/receivers/rxcfg

Broadcast a CONFIG_QUERY to all connected receivers. Optional body field fire_duration_ms (50–5000).

{ "fire_duration_ms": 250 }

POST /api/receivers/[id]/rxcfg

Same but for one receiver.

System

GET /api/system/config

Returns systemcfg.json overlaid with the live Receivers SQL table:

{
  "host_version": 0.08,
  "system": { "dongle_port": "...", "dongle_baud": 115200, "dongle_protocol": "BKYD_TS_HYBRID" },
  "protocols": { ... },
  "types": { ... },
  "receivers": { "RX146": { "label": "...", "type": "...", "cues": {...}, ... } }
}

POST /api/system/config

Write back systemcfg.json. Strips any receivers field from the body before saving (the SQL table is the source of truth).

POST /api/system/cmd_daemon

Drop a JSON file in /tmp/d_cmd/ for the daemon. The body is the command. See Daemon command reference for the full list.

curl -X POST http://localhost:1776/api/system/cmd_daemon \
  -H 'Content-Type: application/json' \
  -d '{"type":"reload_receivers"}'

GET /api/system/system_state

Read /tmp/fw_cursor and /tmp/fw_firing (legacy polling fallback for clients that don't have the WS open).

GET /api/system/rf_scan

Return the contents of /data/last_scan.json (the most recent RF scan result).

POST /api/system/rf_scan

Trigger a new scan via { "type": "scan_radio", "passes": ..., "ch_start": ..., "ch_end": ... } daemon command.

POST /api/system/ota_flash

Stage a base64-encoded firmware blob to /tmp/ota_staging/<filename> and queue { "type": "ota_flash_start", "ident": ..., "image_path": ..., "rate": ... }.

Body:

{
  "ident": "RX146",
  "filename": "os4_receiver_v23.bin",
  "image_b64": "...",
  "rate": 2
}

DELETE /api/system/ota_flash

Queue { "type": "ota_flash_abort" }.

Catalog

GET /api/catalog

Return cached /data/catalog.json (or fallback paths). Used to populate the Add-from-library modal.

GET /api/catalog/refresh

Return the current catalog crawl progress:

{ "status": "running" | "completed" | "error", "current": 42, "total": 200, "message": "...", "timestamp": ... }

POST /api/catalog/refresh

Spawn crawl_catalog.py in the background (no-op if already running).

Other

GET /api/hello

Default Next.js sample route. Useful as a "is the API up" smoke test.

Clone this wiki locally