LiveWing
CMS API

Live Data

Push real-time score updates, clock values, and other live data to productions.

Live data is the real-time, frequently-updated state of a production — scores, clock, period, game state, and any other values that change during a broadcast. Updating live data instantly propagates through the snapshot pipeline to all connected graphics.

Update Live Data

Merge new values into a production's live data. Only the provided keys are updated; existing keys are preserved.

POST /api/cms/productions/live

Auth: readwrite

Request:

curl -X POST https://ideal-heron-677.convex.site/api/cms/productions/live \
  -H "Authorization: Bearer lw_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "productionId": "j572prod1...",
    "data": {
      "homeScore": 28,
      "period": "4th",
      "clock": "12:00"
    }
  }'
FieldTypeRequiredDescription
productionIdstringYesThe production ID
dataobjectYesKey-value pairs to merge into live data

Response:

{
  "ok": true
}

How It Works

When you update live data:

  1. The new values are merged into the production's liveData object
  2. If the production has a liveDataSchema, the merged data is validated against it
  3. The production's snapshot is updated atomically
  4. All playout manifests for the production are regenerated in the background
  5. Connected LiveWing devices receive the updated manifest via their real-time subscription

This entire pipeline typically completes in under 200ms.

Typical Integration: Score Feed

A common use case is feeding scores from an external source (StatBroadcast, manual scoreboard, custom app) into LiveWing productions:

import requests

API_KEY = "lw_your_key_here"
BASE_URL = "https://ideal-heron-677.convex.site"
PRODUCTION_ID = "j572prod1..."

def update_score(home_score: int, away_score: int, period: str, clock: str):
    requests.post(
        f"{BASE_URL}/api/cms/productions/live",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "productionId": PRODUCTION_ID,
            "data": {
                "homeScore": home_score,
                "awayScore": away_score,
                "period": period,
                "clock": clock,
            },
        },
    )

# Called whenever the score changes
update_score(28, 21, "4th", "5:32")

Typical Integration: Clock Controller

For a running clock that updates every second:

const API_KEY = "lw_your_key_here";
const BASE_URL = "https://ideal-heron-677.convex.site";
const PRODUCTION_ID = "j572prod1...";

async function updateClock(clock) {
  await fetch(`${BASE_URL}/api/cms/productions/live`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      productionId: PRODUCTION_ID,
      data: { clock },
    }),
  });
}

// Update every second during live play
let seconds = 720; // 12:00
const interval = setInterval(() => {
  seconds--;
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  updateClock(`${mins}:${secs.toString().padStart(2, "0")}`);
  if (seconds <= 0) clearInterval(interval);
}, 1000);

Validation

If the production has a liveDataSchema, all values are validated against the field definitions. For example, if homeScore is defined as a number field, passing a string value will return an error.

Productions without a schema accept any key-value pairs in live data.

Restrictions

  • You cannot update live data on an archived production (returns 400)
  • Live data updates do not generate audit log entries (too frequent during live broadcasts)
  • There is no rate limit on live data updates, but Convex applies a natural throughput limit (~100 mutations/second per production)

On this page