> ## Documentation Index
> Fetch the complete documentation index at: https://streamloop.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Create or replace a resource

> The whole resource, as GET answers it: a `frame/<id>` `{ name, description?, transition?, code }` (code: its `<Frame>…</Frame>` element in JSX) or `{ name, root }` (JSON); a component `{ description, props, tsx }`; a source its fields. Or the code alone, with `Content-Type: text/jsx` (a `frame/<id>`), `text/tsx` (a component) or `text/typescript` (the script). Built and checked as the studio checks it: a refused change changes nothing and answers every problem (422 CHECK_FAILED, `errors`). `If-Match`: the revision you read; 412 PRECONDITION_FAILED (with `currentRevision`) if it changed since (`*`: it must exist); `If-None-Match: *` creates only. A new resource answers 201 with its Location. Code sent as the wrong kind for its path, or a body that is neither JSON nor code (`application/xml`), is 415.



## OpenAPI

````yaml /api-reference/scenes-openapi.json put /scenes/{id}/resources/{path}
openapi: 3.1.0
info:
  title: Streamloop Scenes API
  version: 1.0.0
  description: >-
    Build and run scenes: live, designed streams. A scene (id `scn_…`) holds
    frames — full arrangements of layers (text, pictures, video, web pages,
    tables, tickers), one on air at a time — each at `frame/<id>`, plus the
    sources it reads (cameras, RTMP/SRT inputs, files, playlists, URLs, Google
    Sheets), its components and its script. You edit its draft as resources at
    paths (`frame/intro`, `frame/intro/title`, `source/data/headlines`,
    `component/LowerThird`, `script/show.ts`); every write is built and checked
    as the Streamloop studio checks it, and studios open on it see it at once.
    Nothing reaches viewers until you publish. To put a scene on a stream: `PUT
    /v1/streams/{id}/scene` (Streamloop REST API), then start the stream.


    This API follows the conventions of Streamloop's REST API: the same
    authentication (`X-API-Key`, an OAuth bearer, or the dashboard's session),
    `X-Workspace-Id`, RFC 7807 problems with a stable `code`, cursor pages,
    `Idempotency-Key` on POST and PATCH, and `RateLimit-*` headers (600 requests
    a minute).
servers:
  - url: https://api.streamloop.app/v1
security:
  - apiKey: []
  - oauth: []
tags:
  - name: scenes
    description: Create, list, read and delete scenes.
  - name: draft
    description: >-
      Read and change the scene's draft, resource by resource. Start with `GET
      /scenes/{id}/resources` (the kinds of thing it holds) and `GET
      /scenes/{id}/resources/element/*` (the building blocks and their props).
  - name: publishing
    description: Put the draft on air, see what was published, go back.
  - name: live
    description: What a stream playing the scene has on air, and the operator's actions.
  - name: inputs
    description: >-
      Live inputs: ingest keys for RTMP/RTMPS/SRT encoders, RTSP cameras, and
      secrets the scene's tables use.
paths:
  /scenes/{id}/resources/{path}:
    parameters:
      - $ref: '#/components/parameters/id'
      - $ref: '#/components/parameters/path'
      - $ref: '#/components/parameters/workspace'
    put:
      tags:
        - draft
      summary: Create or replace a resource
      description: >-
        The whole resource, as GET answers it: a `frame/<id>` `{ name,
        description?, transition?, code }` (code: its `<Frame>…</Frame>` element
        in JSX) or `{ name, root }` (JSON); a component `{ description, props,
        tsx }`; a source its fields. Or the code alone, with `Content-Type:
        text/jsx` (a `frame/<id>`), `text/tsx` (a component) or
        `text/typescript` (the script). Built and checked as the studio checks
        it: a refused change changes nothing and answers every problem (422
        CHECK_FAILED, `errors`). `If-Match`: the revision you read; 412
        PRECONDITION_FAILED (with `currentRevision`) if it changed since (`*`:
        it must exist); `If-None-Match: *` creates only. A new resource answers
        201 with its Location. Code sent as the wrong kind for its path, or a
        body that is neither JSON nor code (`application/xml`), is 415.
      operationId: putResource
      parameters:
        - $ref: '#/components/parameters/ifMatch'
        - $ref: '#/components/parameters/ifNoneMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
            example:
              name: Weather
              description: London weather over a dark gradient
              code: >-
                <Frame background={{ type: "gradient", from: "#05070F", to:
                "#1B2A4A", angle: 160 }}>
                  <Text id="temp" value={data.weather.rows[0].temperature_2m} x={96} y={54} style={{ fontSize: 72, color: "#FFFFFF" }} />
                </Frame>
          text/jsx:
            schema:
              type: string
          text/tsx:
            schema:
              type: string
          text/typescript:
            schema:
              type: string
      responses:
        '200':
          $ref: '#/components/responses/Answer'
        '201':
          description: 'Made: the answer, with Location'
          headers:
            Location:
              schema:
                type: string
            ETag:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Answer'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '415':
          $ref: '#/components/responses/Problem'
        '422':
          $ref: '#/components/responses/Refused'
        4XX:
          $ref: '#/components/responses/Problem'
        5XX:
          $ref: '#/components/responses/Problem'
components:
  parameters:
    id:
      name: id
      in: path
      required: true
      schema:
        type: string
        pattern: ^scn_
      description: The scene's id (`scn_…`)
    path:
      name: path
      in: path
      required: true
      schema:
        type: string
      description: >-
        A resource path, slashes and all: `frame/intro`, `frame/intro/title`,
        `source/data/headlines/rows`, `component/LowerThird`, `script/show.ts`,
        a type (`element/Text`) or a list (`frame/*`)
      example: frame/intro
    workspace:
      name: X-Workspace-Id
      in: header
      schema:
        type: string
      description: >-
        The workspace to act in. Required with an API key; otherwise the
        session's own.
    ifMatch:
      name: If-Match
      in: header
      schema:
        type: string
      description: >-
        The revision (ETag) you read — one, or a comma-separated list of which
        any current one matches — compared strongly (a weak `W/"…"` tag never
        matches): refused with 412 PRECONDITION_FAILED if the resource changed
        since or no longer exists (the problem carries `currentRevision`, null
        when it is gone, and for a layer `frameRevision`: either is taken). A
        layer takes its own revision or that of the `frame/<id>` it is in. `*`:
        it must exist. Checked atomically with the write: of two writes sent at
        once with the same revision, one lands and the other is 412.
    ifNoneMatch:
      name: If-None-Match
      in: header
      schema:
        type: string
      description: >-
        `*`: create only — 412 if the resource exists already. On a GET, the
        ETag you hold (a weak `W/"…"` one too): 304 when nothing changed.
  responses:
    Answer:
      description: The answer
      headers:
        ETag:
          schema:
            type: string
          description: The resource's revision
        Draft-Revision:
          schema:
            type: string
          description: >-
            The whole draft's revision (`draftRevision`), what publish and
            discard take
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Answer'
    BadRequest:
      description: >-
        BAD_REQUEST (400): a body or query that can't be parsed (not JSON);
        INVALID_INPUT (422): one that parses but doesn't fit — an unknown or
        missing field or query parameter, a value of the wrong type or out of
        range. `detail` names the field
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotFound:
      description: >-
        NOT_FOUND: no such scene (in this workspace) or resource — `detail` says
        what there is
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Conflict:
      description: >-
        CONFLICT (a revision given in the body moved — `revisions` of a remove,
        a discard's draftRevision; `currentRevision`; If-Match is 412 instead),
        EDIT_NO_MATCH (the text to replace isn't there; the closest line is
        quoted), EDIT_AMBIGUOUS (it is there more than once), IN_USE (something
        uses it; named), CONFIRM_REQUIRED (on air: send confirm: true to accept
        it), STALE_PUBLISH (a version was published after this draft's base, or
        after the version a revert saw: `publishedVersion`, `basedOn`,
        `reverted`, `publishedVersionIsRevert`; after a revert, publish with
        overwritePublished or discard the draft to match it), DRAFT_NOT_SAVED
        (the server doesn't have those edits yet; retry)
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    PreconditionFailed:
      description: >-
        PRECONDITION_FAILED: the If-Match revision is not the resource's now
        (`currentRevision`), or If-Match: * and it doesn't exist
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Problem:
      description: An RFC 7807 problem; branch on `code`
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Refused:
      description: >-
        CHECK_FAILED / SCENE_INVALID: refused by the check the studio runs,
        nothing changed — `errors` lists each problem with its line or path
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  schemas:
    Answer:
      type: object
      description: >-
        What a draft operation answers. `summary` says what happened in a
        sentence; `value` is what a read found.
      properties:
        summary:
          type: string
        value:
          description: >-
            What was read: a resource (with `revision` and `children`), a type
            (what it takes), or a list
          anyOf:
            - $ref: '#/components/schemas/FrameResource'
            - $ref: '#/components/schemas/LayerResource'
            - $ref: '#/components/schemas/TableResource'
            - $ref: '#/components/schemas/ComponentResource'
            - $ref: '#/components/schemas/ControlResource'
            - $ref: '#/components/schemas/CodeResource'
            - $ref: '#/components/schemas/TypeDescription'
            - type: array
              items:
                $ref: '#/components/schemas/ListEntry'
        path:
          type: string
          description: 'A write: where it is stored (a new id is made unique)'
        revision:
          type: string
          description: 'A write: the resource''s new revision'
        frameRevision:
          type: string
          description: >-
            A layer's write: the revision of the `frame/<id>` it is in (If-Match
            takes either)
        created:
          type: boolean
        diff:
          type: string
          description: 'A write: a unified diff of what changed'
        removed:
          type: array
          items:
            type: string
        draftRevision:
          type: string
          description: 'The whole draft as this answer saw it: publish takes it'
      additionalProperties: true
    FrameResource:
      type: object
      description: frame/<id>
      required:
        - path
        - revision
        - code
      properties:
        path:
          type: string
        revision:
          type: string
        name:
          type: string
        description:
          type: string
        transition:
          type: object
          properties:
            kind:
              type: string
              enum:
                - cut
                - fade
                - slide
                - wipe
                - dip
            ms:
              type: number
        code:
          type: string
          description: Its <Frame>…</Frame> element in JSX (stored formatted)
        sound:
          type: string
          description: What it sends to air (read-only)
        children:
          type: array
          items:
            $ref: '#/components/schemas/Child'
    LayerResource:
      type: object
      description: frame/<id>/<layer>
      required:
        - path
        - type
      properties:
        path:
          type: string
        revision:
          type: string
        type:
          type: string
          description: Text, Stack, Image, … or a component
        props:
          type: object
          additionalProperties: true
        name:
          type: string
        description:
          type: string
        ancestors:
          type: array
          items:
            type: string
        children:
          type: array
          items:
            $ref: '#/components/schemas/Child'
    TableResource:
      type: object
      description: source/data/<id> (other sources are like it, with their kind's fields)
      properties:
        path:
          type: string
        revision:
          type: string
        name:
          type: string
        description:
          type: string
        connector:
          type: string
          enum:
            - inline
            - file
            - csv
            - json
            - xlsx
            - sheets
            - html
        url:
          type: string
        file:
          type: string
          description: A file table's file (an upload's id)
        pick:
          type: string
        headers:
          type: object
          additionalProperties:
            type: string
        refreshSeconds:
          type: number
        rows:
          type: array
          items:
            type: object
        schema:
          type: object
          description: Its columns (inferred from the rows)
        children:
          type: array
          items:
            $ref: '#/components/schemas/Child'
    ComponentResource:
      type: object
      description: component/<id>
      properties:
        path:
          type: string
        revision:
          type: string
        description:
          type: string
        props:
          type: object
          description: name → JSON Schema
        takesChildren:
          type: boolean
        tsx:
          type: string
        builtin:
          type: boolean
    ControlResource:
      type: object
      description: 'control/<id>: an operator input'
      properties:
        path:
          type: string
        revision:
          type: string
        type:
          type: string
          enum:
            - toggle
            - text
            - number
            - select
            - button
        label:
          type: string
        description:
          type: string
        group:
          type: string
        default: {}
    CodeResource:
      type: object
      description: script/show.ts, script/state.ts
      properties:
        path:
          type: string
        revision:
          type: string
        code:
          type: string
    TypeDescription:
      type: object
      description: 'A type (frame, source/data, element/Text, …): what it takes'
      properties:
        path:
          type: string
        description:
          type: string
        props:
          type: object
          additionalProperties: true
        style:
          type: object
          additionalProperties:
            type: string
        example: {}
        subtypes:
          type: array
          items:
            type: object
    ListEntry:
      type: object
      properties:
        path:
          type: string
        name:
          type: string
        description:
          type: string
      additionalProperties: true
    Problem:
      type: object
      required:
        - type
        - title
        - status
        - code
        - detail
      properties:
        type:
          type: string
          const: about:blank
        title:
          type: string
        status:
          type: integer
        code:
          type: string
          description: >-
            Stable — branch on it: BAD_REQUEST (400), WORKSPACE_REQUIRED (400),
            UNAUTHENTICATED (401), INSUFFICIENT_SCOPE (403), WORKSPACE_FORBIDDEN
            (403), NOT_FOUND (404), METHOD_NOT_ALLOWED (405), READ_ONLY (422),
            CONFLICT (409; currentRevision), EDIT_NO_MATCH (409), EDIT_AMBIGUOUS
            (409), IN_USE (409), CONFIRM_REQUIRED (409), STALE_PUBLISH (409;
            publishedVersion, basedOn, reverted, publishedVersionIsRevert),
            REVISION_REQUIRED (MCP only: a set or remove of an existing resource
            without its revision), NOT_PUBLISHED (409), SCENE_IN_USE (409),
            DRAFT_NOT_SAVED (409), SCENE_OFFLINE (409), SCENE_REFUSED (409),
            INGEST_KEY_REVOKED (409), CAMERA_PULL_GONE (409), SECRETS_FULL
            (409), PRECONDITION_FAILED (412; currentRevision), TOO_LARGE (413),
            UNSUPPORTED_MEDIA_TYPE (415), INVALID_INPUT (422),
            IDEMPOTENCY_KEY_REUSED (422), CHECK_FAILED (422), SCENE_INVALID
            (422), CAMERA_INVALID (422), CAMERA_PRIVATE_ADDRESS (422),
            SECRET_MISSING (422), RATE_LIMITED (429), CAMERA_TEST_BUSY (429),
            INTERNAL (500), NOT_SUPPORTED (501), UNAVAILABLE (502 or 503),
            INGEST_UNAVAILABLE (503), CAMERAS_UNAVAILABLE (503),
            SECRETS_UNAVAILABLE (503), SCENE_TIMEOUT (504)
        detail:
          type: string
          description: What happened and what to do
        errors:
          type: array
          description: Each problem, when there are several (a check's findings)
          items:
            type: object
            properties:
              line:
                type: integer
              path:
                type: string
              node:
                type: string
              message:
                type: string
            additionalProperties: true
        currentRevision:
          type:
            - string
            - 'null'
          description: >-
            CONFLICT / PRECONDITION_FAILED: the resource's revision now; null
            when it no longer exists
    Child:
      type: object
      properties:
        path:
          type: string
        description:
          type: string
        as:
          type: string
          enum:
            - image
        args:
          type: object
          additionalProperties:
            type: string
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        A Streamloop API key (`sl_…`). It carries no workspace: send
        `X-Workspace-Id` too (400 WORKSPACE_REQUIRED otherwise).
    oauth:
      type: http
      scheme: bearer
      description: >-
        An OAuth access token (as Streamloop's MCP server uses). Each operation
        needs the scope named in its `x-scope`: streamloop:read ⊂
        streamloop:write ⊂ streamloop:destructive (403 INSUFFICIENT_SCOPE).

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.