Stories

Docs API reference

Stories

A story is what a stage plays: chapters (scenes) in order, looping. A chapter can roll a template through a collection, play for a set time (two minutes of TV after the news), or play only at certain times of day. Put a story on a stage with PUT /stages/{id}/idle or POST /bookings.

List stories

GET /stories authoring

Parameters

  • limit integer query

    Page size, 1–200.

    1–200. Default 50

  • cursor string query

    The next_cursor of the previous page.

Responses

  • 200 A page of stories.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the role is read-only.
Response fields (200)
  • items object[] required
    5 fields in items
    • id string required
    • title string required
    • description string
    • status string
    • created_at integer or null
  • next_cursor string or null required

    Pass as cursor for the next page; null on the last page.

curl "https://www.wunderframe.com/api/v1/stories?limit=50" \
  -H "Authorization: Bearer $WUNDERFRAME_API_KEY"
Response 200
{
  "items": [
    {
      "id": "sto_news_tv",
      "title": "News and TV",
      "description": "",
      "status": "draft",
      "created_at": 1789990000000
    }
  ],
  "next_cursor": null
}
Error example
Response 401
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key."
  }
}

Create a story

POST /stories authoring

Request body required

  • title string required
  • description string

Responses

  • 201 The story.
  • 400 The request is invalid — details.field names the field when there is one.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the role is read-only.
Response fields (201)
  • id string required
  • title string required
  • description string
  • status string
  • created_at integer or null
curl -X POST "https://www.wunderframe.com/api/v1/stories" \
  -H "Authorization: Bearer $WUNDERFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "News and TV"
}'
Response 201
{
  "id": "sto_news_tv",
  "title": "News and TV",
  "description": "",
  "status": "draft",
  "created_at": 1789990000000
}
Error example
Response 400
{
  "error": {
    "code": "invalid",
    "message": "The request is invalid — `details.field` names the field when there is one."
  }
}

Get a story with its chapters

GET /stories/{id} authoring

Parameters

  • id string required path

    The story id.

Responses

  • 200 The story.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the role is read-only.
  • 404 Not found in this workspace.
Response fields (200)
  • id string required
  • title string required
  • description string
  • status string
  • created_at integer or null
  • chapters object[]
    5 fields in chapters
    • id string required
    • index integer required

      0 = first.

    • scene_id string required
    • scene_title string or null
    • settings object required

      Only the given settings change. play_for_ms: the chapter plays exactly this long — its last moment holds, or it is cut (not on a rolling chapter). shot_duration_ms: every moment’s hold. roll: { collection_id, per_pass, order: { by: "added"|"field"|"collection", field, direction } } plays the template once per record, per_pass each round; for advertising add weight_field (share of voice), start_field / end_field (a date window, venue time), duration_field (seconds per record, 3–120) and daypart_field (daypart names) — a roll for the same collection changes only the keys given. pick: a session slot. plays_during: { daypart_ids, days }. fills_gaps, start_shot_id. detail: { collection_id } — the chapter never plays in the loop; a visitor opens it by choosing a record of that collection (an agenda board row with open_detail, or a row of a collection Live list with props.open_detail), filled with that record. One per collection per story.

      8 fields in settings
      • play_for_ms integer or null

        At least 1000

      • shot_duration_ms integer or null

        At least 1000

      • start_shot_id string or null
      • roll object or null

        A roll through a collection (ADR-0020, ADR-0057). Every *_field names a field of the records; null or "" = not used.

        11 fields in roll
        • collection_id string
        • per_pass integer

          1–20. Default 5

        • order object
          3 fields in order
          • by string

            One of added, field, collection

          • field string or null
          • direction string

            One of desc, asc

        • match map of string
        • include_stale boolean

          Default false

        • max_age_h number or null
        • weight_field string or null

          Numeric field: share of voice. Missing value = 1, 0 = paused, max 1000.

        • start_field string or null

          Date or datetime field: the record plays from then (venue time).

        • end_field string or null

          Date or datetime field: the record plays until then; a date alone = through that day.

        • duration_field string or null

          Number field (or m:ss): seconds the record plays, clamped 3–120.

        • daypart_field string or null

          Text field naming dayparts ("Morning, Evening"): the record plays only then.

      • pick object or null
      • plays_during object or null
      • fills_gaps boolean
      • detail object
        1 fields in detail
        • collection_id string required
curl "https://www.wunderframe.com/api/v1/stories/sto_news_tv" \
  -H "Authorization: Bearer $WUNDERFRAME_API_KEY"
Response 200
{
  "id": "sto_news_tv",
  "title": "News and TV",
  "description": "",
  "status": "draft",
  "created_at": 1789990000000,
  "chapters": []
}
Error example
Response 401
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key."
  }
}

Add a chapter

POST /stories/{id}/chapters authoring

Cast a scene into the story (at the end, or at index) with optional settings — { roll: { collection_id, per_pass } } rolls a template; { play_for_ms: 120000 } plays it for two minutes.

Parameters

  • id string required path

    The story id.

Request body required

  • scene_id string required
  • settings object

    Only the given settings change. play_for_ms: the chapter plays exactly this long — its last moment holds, or it is cut (not on a rolling chapter). shot_duration_ms: every moment’s hold. roll: { collection_id, per_pass, order: { by: "added"|"field"|"collection", field, direction } } plays the template once per record, per_pass each round; for advertising add weight_field (share of voice), start_field / end_field (a date window, venue time), duration_field (seconds per record, 3–120) and daypart_field (daypart names) — a roll for the same collection changes only the keys given. pick: a session slot. plays_during: { daypart_ids, days }. fills_gaps, start_shot_id. detail: { collection_id } — the chapter never plays in the loop; a visitor opens it by choosing a record of that collection (an agenda board row with open_detail, or a row of a collection Live list with props.open_detail), filled with that record. One per collection per story.

    8 fields in settings
    • play_for_ms integer or null

      At least 1000

    • shot_duration_ms integer or null

      At least 1000

    • start_shot_id string or null
    • roll object or null

      A roll through a collection (ADR-0020, ADR-0057). Every *_field names a field of the records; null or "" = not used.

      11 fields in roll
      • collection_id string
      • per_pass integer

        1–20. Default 5

      • order object
        3 fields in order
        • by string

          One of added, field, collection

        • field string or null
        • direction string

          One of desc, asc

      • match map of string
      • include_stale boolean

        Default false

      • max_age_h number or null
      • weight_field string or null

        Numeric field: share of voice. Missing value = 1, 0 = paused, max 1000.

      • start_field string or null

        Date or datetime field: the record plays from then (venue time).

      • end_field string or null

        Date or datetime field: the record plays until then; a date alone = through that day.

      • duration_field string or null

        Number field (or m:ss): seconds the record plays, clamped 3–120.

      • daypart_field string or null

        Text field naming dayparts ("Morning, Evening"): the record plays only then.

    • pick object or null
    • plays_during object or null
    • fills_gaps boolean
    • detail object
      1 fields in detail
      • collection_id string required
  • index integer

Responses

  • 201 The chapter.
  • 400 The request is invalid — details.field names the field when there is one.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the role is read-only.
  • 404 Not found in this workspace.
Response fields (201)
  • id string required
  • index integer required

    0 = first.

  • scene_id string required
  • scene_title string or null
  • settings object required

    Only the given settings change. play_for_ms: the chapter plays exactly this long — its last moment holds, or it is cut (not on a rolling chapter). shot_duration_ms: every moment’s hold. roll: { collection_id, per_pass, order: { by: "added"|"field"|"collection", field, direction } } plays the template once per record, per_pass each round; for advertising add weight_field (share of voice), start_field / end_field (a date window, venue time), duration_field (seconds per record, 3–120) and daypart_field (daypart names) — a roll for the same collection changes only the keys given. pick: a session slot. plays_during: { daypart_ids, days }. fills_gaps, start_shot_id. detail: { collection_id } — the chapter never plays in the loop; a visitor opens it by choosing a record of that collection (an agenda board row with open_detail, or a row of a collection Live list with props.open_detail), filled with that record. One per collection per story.

    8 fields in settings
    • play_for_ms integer or null

      At least 1000

    • shot_duration_ms integer or null

      At least 1000

    • start_shot_id string or null
    • roll object or null

      A roll through a collection (ADR-0020, ADR-0057). Every *_field names a field of the records; null or "" = not used.

      11 fields in roll
      • collection_id string
      • per_pass integer

        1–20. Default 5

      • order object
        3 fields in order
        • by string

          One of added, field, collection

        • field string or null
        • direction string

          One of desc, asc

      • match map of string
      • include_stale boolean

        Default false

      • max_age_h number or null
      • weight_field string or null

        Numeric field: share of voice. Missing value = 1, 0 = paused, max 1000.

      • start_field string or null

        Date or datetime field: the record plays from then (venue time).

      • end_field string or null

        Date or datetime field: the record plays until then; a date alone = through that day.

      • duration_field string or null

        Number field (or m:ss): seconds the record plays, clamped 3–120.

      • daypart_field string or null

        Text field naming dayparts ("Morning, Evening"): the record plays only then.

    • pick object or null
    • plays_during object or null
    • fills_gaps boolean
    • detail object
      1 fields in detail
      • collection_id string required
curl -X POST "https://www.wunderframe.com/api/v1/stories/sto_news_tv/chapters" \
  -H "Authorization: Bearer $WUNDERFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "scene_id": "scn_tv",
  "settings": {
    "play_for_ms": 120000
  }
}'
Response 201
{
  "id": "plc_tv",
  "index": 1,
  "scene_id": "scn_tv",
  "scene_title": "TV",
  "settings": {
    "play_for_ms": 120000,
    "shot_duration_ms": null,
    "start_shot_id": null,
    "roll": null,
    "pick": null,
    "plays_during": null,
    "fills_gaps": false
  }
}
Error example
Response 400
{
  "error": {
    "code": "invalid",
    "message": "The request is invalid — `details.field` names the field when there is one."
  }
}

Change a chapter

PATCH /chapters/{id} authoring

Its settings (only the given ones change) and its place in the story.

Parameters

  • id string required path

    The chapter id.

Request body required

  • settings object

    Only the given settings change. play_for_ms: the chapter plays exactly this long — its last moment holds, or it is cut (not on a rolling chapter). shot_duration_ms: every moment’s hold. roll: { collection_id, per_pass, order: { by: "added"|"field"|"collection", field, direction } } plays the template once per record, per_pass each round; for advertising add weight_field (share of voice), start_field / end_field (a date window, venue time), duration_field (seconds per record, 3–120) and daypart_field (daypart names) — a roll for the same collection changes only the keys given. pick: a session slot. plays_during: { daypart_ids, days }. fills_gaps, start_shot_id. detail: { collection_id } — the chapter never plays in the loop; a visitor opens it by choosing a record of that collection (an agenda board row with open_detail, or a row of a collection Live list with props.open_detail), filled with that record. One per collection per story.

    8 fields in settings
    • play_for_ms integer or null

      At least 1000

    • shot_duration_ms integer or null

      At least 1000

    • start_shot_id string or null
    • roll object or null

      A roll through a collection (ADR-0020, ADR-0057). Every *_field names a field of the records; null or "" = not used.

      11 fields in roll
      • collection_id string
      • per_pass integer

        1–20. Default 5

      • order object
        3 fields in order
        • by string

          One of added, field, collection

        • field string or null
        • direction string

          One of desc, asc

      • match map of string
      • include_stale boolean

        Default false

      • max_age_h number or null
      • weight_field string or null

        Numeric field: share of voice. Missing value = 1, 0 = paused, max 1000.

      • start_field string or null

        Date or datetime field: the record plays from then (venue time).

      • end_field string or null

        Date or datetime field: the record plays until then; a date alone = through that day.

      • duration_field string or null

        Number field (or m:ss): seconds the record plays, clamped 3–120.

      • daypart_field string or null

        Text field naming dayparts ("Morning, Evening"): the record plays only then.

    • pick object or null
    • plays_during object or null
    • fills_gaps boolean
    • detail object
      1 fields in detail
      • collection_id string required
  • index integer

Responses

  • 200 The chapter.
  • 400 The request is invalid — details.field names the field when there is one.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the role is read-only.
  • 404 Not found in this workspace.
Response fields (200)
  • id string required
  • index integer required

    0 = first.

  • scene_id string required
  • scene_title string or null
  • settings object required

    Only the given settings change. play_for_ms: the chapter plays exactly this long — its last moment holds, or it is cut (not on a rolling chapter). shot_duration_ms: every moment’s hold. roll: { collection_id, per_pass, order: { by: "added"|"field"|"collection", field, direction } } plays the template once per record, per_pass each round; for advertising add weight_field (share of voice), start_field / end_field (a date window, venue time), duration_field (seconds per record, 3–120) and daypart_field (daypart names) — a roll for the same collection changes only the keys given. pick: a session slot. plays_during: { daypart_ids, days }. fills_gaps, start_shot_id. detail: { collection_id } — the chapter never plays in the loop; a visitor opens it by choosing a record of that collection (an agenda board row with open_detail, or a row of a collection Live list with props.open_detail), filled with that record. One per collection per story.

    8 fields in settings
    • play_for_ms integer or null

      At least 1000

    • shot_duration_ms integer or null

      At least 1000

    • start_shot_id string or null
    • roll object or null

      A roll through a collection (ADR-0020, ADR-0057). Every *_field names a field of the records; null or "" = not used.

      11 fields in roll
      • collection_id string
      • per_pass integer

        1–20. Default 5

      • order object
        3 fields in order
        • by string

          One of added, field, collection

        • field string or null
        • direction string

          One of desc, asc

      • match map of string
      • include_stale boolean

        Default false

      • max_age_h number or null
      • weight_field string or null

        Numeric field: share of voice. Missing value = 1, 0 = paused, max 1000.

      • start_field string or null

        Date or datetime field: the record plays from then (venue time).

      • end_field string or null

        Date or datetime field: the record plays until then; a date alone = through that day.

      • duration_field string or null

        Number field (or m:ss): seconds the record plays, clamped 3–120.

      • daypart_field string or null

        Text field naming dayparts ("Morning, Evening"): the record plays only then.

    • pick object or null
    • plays_during object or null
    • fills_gaps boolean
    • detail object
      1 fields in detail
      • collection_id string required
curl -X PATCH "https://www.wunderframe.com/api/v1/chapters/plc_tv" \
  -H "Authorization: Bearer $WUNDERFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "settings": {
    "play_for_ms": 180000
  },
  "index": 1
}'
Response 200
{
  "id": "plc_tv",
  "index": 1,
  "scene_id": "scn_tv",
  "scene_title": "TV",
  "settings": {
    "play_for_ms": 120000,
    "shot_duration_ms": null,
    "start_shot_id": null,
    "roll": null,
    "pick": null,
    "plays_during": null,
    "fills_gaps": false
  }
}
Error example
Response 400
{
  "error": {
    "code": "invalid",
    "message": "The request is invalid — `details.field` names the field when there is one."
  }
}

Remove a chapter

DELETE /chapters/{id} authoring

The scene stays in the library.

Parameters

  • id string required path

    The chapter id.

Responses

  • 204 Removed.
  • 401 Missing or invalid API key.
  • 403 The key lacks the scope, or the role is read-only.
  • 404 Not found in this workspace.
curl -X DELETE "https://www.wunderframe.com/api/v1/chapters/plc_tv" \
  -H "Authorization: Bearer $WUNDERFRAME_API_KEY"

204 — no body.

Error example
Response 401
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key."
  }
}