Throughline Docs

REST API v1

Throughline's external API exposes a production's graph — scenes, assets, schedule, approvals, deliveries, people, activity — to tools that live around the production rather than inside the app: pipeline scripts, reporting dashboards, delivery trackers, and similar integrations.

It is a read-mostly, write-carefully surface. The read side mirrors what crew see in the app, scoped to one production. The write side is deliberately narrow: create an asset, attach a note, decide an approval — because nothing in Throughline moves without an accountable person (or a key acting on one's behalf) behind it.

Base URL

https://throughline-backend-idkf.onrender.com/api/v1

This host is provisional for the pilot. All requests and JSON bodies use Content-Type: application/json.

Authentication

Every request carries a Throughline access key as a bearer token:

Authorization: Bearer tl_your_access_key_here

A key is minted by a production's admin, from inside Throughline, for a specific person or integration's use — one key, one label, one production. The full token is shown once, at creation; after that only a recognizable prefix is kept. There is no cross-production surface: a key minted on one production sees that production and nothing else.

Each key carries one or both scopes:

ScopeGrants
readEvery GET endpoint below
writeEvery POST endpoint below

Auth errors

StatusMeaning
401Missing or malformed Authorization header, or an unknown/revoked key
403The key is valid but lacks the scope the endpoint needs

Revoking a key from Settings takes effect immediately.

Read endpoints

All of the following require the read scope.

GET /production

The production the key belongs to, with headline counts.

curl -s https://throughline-backend-idkf.onrender.com/api/v1/production \
  -H "Authorization: Bearer $TL_KEY"
{
  "id": 1,
  "title": "Example Production",
  "logline": "One line describing the show.",
  "status": "active",
  "phase": "production",
  "accent": "sea-glass",
  "format": "single",
  "stats": {
    "departments": 12,
    "open_approvals": 3,
    "deliveries_in_flight": 2,
    "scenes": 44,
    "nodes": 210
  }
}

An episodic production also carries "format": "episodic" and a season_number. accent is a key into a small curated palette, never a raw color — never document one you didn't see returned.

GET /departments

Every department on the production.

curl -s https://throughline-backend-idkf.onrender.com/api/v1/departments \
  -H "Authorization: Bearer $TL_KEY"
[
  {
    "id": 7,
    "slug": "props",
    "name": "Props",
    "cluster": "art",
    "phases": ["prep", "production"],
    "mission": "Every object an actor touches.",
    "key_roles": ["Props Master", "Props Assistant"],
    "privacy": "open",
    "unveiled": true
  }
]

GET /departments/{slug}

One department, with its people, its registry, and an asset count.

curl -s https://throughline-backend-idkf.onrender.com/api/v1/departments/props \
  -H "Authorization: Bearer $TL_KEY"
{
  "id": 7,
  "slug": "props",
  "name": "Props",
  "cluster": "art",
  "registry": {},
  "people": [
    { "id": 14, "name": "<person name>", "role": "Props Master", "is_hod": true }
  ],
  "assets": 38
}

GET /scenes

The current script breakdown, in script order.

curl -s https://throughline-backend-idkf.onrender.com/api/v1/scenes \
  -H "Authorization: Bearer $TL_KEY"
[
  {
    "node_id": 41,
    "number": "12",
    "slugline": "EXT. LIGHTHOUSE GALLERY - NIGHT",
    "synopsis": "One line describing the scene.",
    "status": "active",
    "revision_color": "Blue",
    "elements": { "props": ["Brass compass"], "cast": ["KEEPER"] }
  }
]

On an episodic production, every scene also carries episode_id and episode_code (e.g. "S2E7"); a single-output production's response omits both keys entirely rather than sending them as null.

GET /assets

Non-scene graph nodes — props, locations, characters, shots, and so on. Every optional query parameter narrows the list:

ParameterMeaning
departmentDepartment slug, e.g. props
kindNode kind, e.g. asset, character, location
statusOne of planned, active, blocked, done
curl -s "https://throughline-backend-idkf.onrender.com/api/v1/assets?department=props&status=active" \
  -H "Authorization: Bearer $TL_KEY"
[
  {
    "node_id": 112,
    "label": "Brass compass",
    "kind": "asset",
    "status": "active",
    "department": "props",
    "versions": 3,
    "updated_at": "2026-07-09T17:42:11Z"
  }
]

Rows are logistical — labels, statuses, version counts — never media.

GET /assets/{node_id}

One asset in full. Released versions carry media; anything not yet released comes back as a locked stub, the same rule the app applies to anyone outside the owning department.

curl -s https://throughline-backend-idkf.onrender.com/api/v1/assets/112 \
  -H "Authorization: Bearer $TL_KEY"

GET /schedule

Shoot days with their scenes.

curl -s https://throughline-backend-idkf.onrender.com/api/v1/schedule \
  -H "Authorization: Bearer $TL_KEY"
[
  {
    "id": 3,
    "day_number": 3,
    "date": "2026-07-10",
    "general_call": "06:30",
    "status": "scheduled",
    "scenes": [
      { "node_id": 41, "number": "12", "slugline": "EXT. LIGHTHOUSE GALLERY - NIGHT" }
    ]
  }
]

episode_id is a repeatable query parameter (?episode_id=1&episode_id=2) on episodic productions. It marks matching strips rather than dropping anything from the response, and has no effect on a single-output production.

GET /deliveries

Every delivery — inter-department and external — with its gating approval, if it has one.

curl -s https://throughline-backend-idkf.onrender.com/api/v1/deliveries \
  -H "Authorization: Bearer $TL_KEY"

GET /approvals

Approval chains. Filter with ?status=pending|approved|changes_requested|rejected.

curl -s "https://throughline-backend-idkf.onrender.com/api/v1/approvals?status=pending" \
  -H "Authorization: Bearer $TL_KEY"

GET /script-changes

Detected script-draft differences awaiting a decision. Defaults to ?status=pending; also accepts approved, dismissed, or all. On an episodic production, ?episode_id= scopes to one episode and ?all_episodes=true widens to every episode at once.

GET /activity

The production activity feed, newest first. ?limit= defaults to 50, capped at 200.

GET /people

The crew directory — name, role, department, and HOD/admin flags. No contact details.

GET /jobs

Recent generation jobs (image/video renders). ?limit= as for /activity.

Write endpoints

All of the following require the write scope.

POST /assets

Create a new graph node.

curl -s -X POST https://throughline-backend-idkf.onrender.com/api/v1/assets \
  -H "Authorization: Bearer $TL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label": "Hero lantern", "kind": "asset", "department": "props", "status": "active"}'

label is required. kind defaults to "asset"; scenes cannot be created here — they come from script breakdown. status must be one of planned, active, blocked, done.

POST /assets/{node_id}/notes

Attach a note to an asset. It lands on the asset's newest version if it has one, or as an activity entry if it doesn't — either way it's on the record.

curl -s -X POST https://throughline-backend-idkf.onrender.com/api/v1/assets/112/notes \
  -H "Authorization: Bearer $TL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "Approved for hero use.", "author": "Editorial pipeline"}'

text is required. author is free text attributed to your integration if omitted.

POST /approvals/{approval_id}/decide

Advance the current step of an approval chain.

curl -s -X POST https://throughline-backend-idkf.onrender.com/api/v1/approvals/12/decide \
  -H "Authorization: Bearer $TL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision": "approved", "decided_by": "Dana Whitfield", "note": "Signed off via external review tool."}'

decision must be "approved" or "changes". decided_by names the person making the call, and a write-scoped key is not a signature — if decided_by isn't the person whose step it actually is, the request is refused rather than silently recorded under someone else's name. Decision-mode approvals refuse an anonymous decision outright; multi-step chains permit omitting it.

Not yet in v1

Budget and purchase-order data, and the assistant's chat interface, are part of the in-app surface today and are not yet exposed to external API keys. If your integration needs either, talk to your Throughline contact — the read/write model above is where they'd land.

Versioning

The path prefix is /api/v1. Changes within it are additive only — new endpoints, new optional fields, new query parameters. Nothing is renamed or removed inside v1; a breaking change would ship as /api/v2.

Timestamps are UTC ISO-8601. IDs are integers, stable within a production. Unknown fields may appear in any response at any time — parse leniently.