{
  "asyncapi": "3.0.0",
  "info": {
    "title": "OverMox Real-time Event Stream",
    "version": "1.0.0",
    "description": "Server-pushed event stream over SignalR/WebSocket. Clients subscribe by calling Subscribe(eventTypes) on the hub at /api/events; the server broadcasts each event as an envelope (type, timestamp, data) as it fires. SubscribeFiltered(patterns, objects, groups, clips) additionally narrows delivery by event data: composition is AND on top of the pattern match, and each non-empty list filters on the matching data key (object, group, clip) with case-sensitive values. An event lacking a filtered key while that filter is set is excluded; an unset or empty list is a wildcard. SubscribeFiltered accumulates patterns like Subscribe but atomically replaces the connection's whole filter snapshot; filters persist until replaced, last pattern removed, or disconnect. Generated from .sync-registry.yaml; do not hand-edit. Run `python -m tools.sync fix` to regenerate."
  },
  "servers": {
    "default": {
      "host": "localhost:5000",
      "pathname": "/api/events",
      "protocol": "ws",
      "description": "SignalR hub. Wire protocol is WebSocket."
    }
  },
  "channels": {
    "eventStream": {
      "address": "/api/events",
      "description": "Bidirectional channel. Server pushes event envelopes (type, timestamp, data).",
      "messages": {
        "AnimationPlaybackCompleted": {
          "$ref": "#/components/messages/AnimationPlaybackCompleted"
        },
        "AnimationPlaybackDirectionChanged": {
          "$ref": "#/components/messages/AnimationPlaybackDirectionChanged"
        },
        "AnimationPlaybackHeld": {
          "$ref": "#/components/messages/AnimationPlaybackHeld"
        },
        "AnimationPlaybackLooped": {
          "$ref": "#/components/messages/AnimationPlaybackLooped"
        },
        "AnimationPlaybackMarker": {
          "$ref": "#/components/messages/AnimationPlaybackMarker"
        },
        "AnimationPlaybackPaused": {
          "$ref": "#/components/messages/AnimationPlaybackPaused"
        },
        "AnimationPlaybackResumed": {
          "$ref": "#/components/messages/AnimationPlaybackResumed"
        },
        "AnimationPlaybackStarted": {
          "$ref": "#/components/messages/AnimationPlaybackStarted"
        },
        "AnimationPlaybackStopped": {
          "$ref": "#/components/messages/AnimationPlaybackStopped"
        },
        "CameraChanged": {
          "$ref": "#/components/messages/CameraChanged"
        },
        "ConsoleLog": {
          "$ref": "#/components/messages/ConsoleLog"
        },
        "GraphRunning": {
          "$ref": "#/components/messages/GraphRunning"
        },
        "GraphStopped": {
          "$ref": "#/components/messages/GraphStopped"
        },
        "MidiInput": {
          "$ref": "#/components/messages/MidiInput"
        },
        "NodeDone": {
          "$ref": "#/components/messages/NodeDone"
        },
        "NodeExecuting": {
          "$ref": "#/components/messages/NodeExecuting"
        },
        "OvermoxConnected": {
          "$ref": "#/components/messages/OvermoxConnected"
        },
        "OvermoxDisconnected": {
          "$ref": "#/components/messages/OvermoxDisconnected"
        },
        "PortsUpdated": {
          "$ref": "#/components/messages/PortsUpdated"
        },
        "ProcessExited": {
          "$ref": "#/components/messages/ProcessExited"
        },
        "ProcessLoaded": {
          "$ref": "#/components/messages/ProcessLoaded"
        },
        "ProjectClosed": {
          "$ref": "#/components/messages/ProjectClosed"
        },
        "ProjectOpened": {
          "$ref": "#/components/messages/ProjectOpened"
        },
        "SceneActivated": {
          "$ref": "#/components/messages/SceneActivated"
        },
        "SceneDeactivated": {
          "$ref": "#/components/messages/SceneDeactivated"
        },
        "StreamingEvent": {
          "$ref": "#/components/messages/StreamingEvent"
        },
        "TweenCancelled": {
          "$ref": "#/components/messages/TweenCancelled"
        },
        "TweenCompleted": {
          "$ref": "#/components/messages/TweenCompleted"
        },
        "VariableChanged": {
          "$ref": "#/components/messages/VariableChanged"
        },
        "VariableDeleted": {
          "$ref": "#/components/messages/VariableDeleted"
        },
        "WebhookReceived": {
          "$ref": "#/components/messages/WebhookReceived"
        }
      }
    }
  },
  "operations": {
    "receiveEvents": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/eventStream"
      },
      "messages": [
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackCompleted"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackDirectionChanged"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackHeld"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackLooped"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackMarker"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackPaused"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackResumed"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackStarted"
        },
        {
          "$ref": "#/channels/eventStream/messages/AnimationPlaybackStopped"
        },
        {
          "$ref": "#/channels/eventStream/messages/CameraChanged"
        },
        {
          "$ref": "#/channels/eventStream/messages/ConsoleLog"
        },
        {
          "$ref": "#/channels/eventStream/messages/GraphRunning"
        },
        {
          "$ref": "#/channels/eventStream/messages/GraphStopped"
        },
        {
          "$ref": "#/channels/eventStream/messages/MidiInput"
        },
        {
          "$ref": "#/channels/eventStream/messages/NodeDone"
        },
        {
          "$ref": "#/channels/eventStream/messages/NodeExecuting"
        },
        {
          "$ref": "#/channels/eventStream/messages/OvermoxConnected"
        },
        {
          "$ref": "#/channels/eventStream/messages/OvermoxDisconnected"
        },
        {
          "$ref": "#/channels/eventStream/messages/PortsUpdated"
        },
        {
          "$ref": "#/channels/eventStream/messages/ProcessExited"
        },
        {
          "$ref": "#/channels/eventStream/messages/ProcessLoaded"
        },
        {
          "$ref": "#/channels/eventStream/messages/ProjectClosed"
        },
        {
          "$ref": "#/channels/eventStream/messages/ProjectOpened"
        },
        {
          "$ref": "#/channels/eventStream/messages/SceneActivated"
        },
        {
          "$ref": "#/channels/eventStream/messages/SceneDeactivated"
        },
        {
          "$ref": "#/channels/eventStream/messages/StreamingEvent"
        },
        {
          "$ref": "#/channels/eventStream/messages/TweenCancelled"
        },
        {
          "$ref": "#/channels/eventStream/messages/TweenCompleted"
        },
        {
          "$ref": "#/channels/eventStream/messages/VariableChanged"
        },
        {
          "$ref": "#/channels/eventStream/messages/VariableDeleted"
        },
        {
          "$ref": "#/channels/eventStream/messages/WebhookReceived"
        }
      ]
    }
  },
  "components": {
    "messages": {
      "AnimationPlaybackCompleted": {
        "name": "animation.playback.completed",
        "title": "Animation Playback Completed",
        "summary": "Fires when a playback reaches its natural end (Once clips).",
        "x-streaming-facing": true,
        "description": "Reason is always natural; explicit or superseding stops emit animation.playback.stopped instead. This is the event that replaces hard-coded choreography timers. supersededBy is optional: it names the replacing session's playbackId and appears only when a fresh play directly displaces this completed clip while it still holds its layer. A clip that ends on its own carries no supersededBy.\n\n*Example: When a celebration clip finishes, trigger the follow-up idle animation on animation.playback.completed instead of guessing the clip duration.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.completed"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackCompletedData"
            }
          }
        }
      },
      "AnimationPlaybackDirectionChanged": {
        "name": "animation.playback.direction.changed",
        "title": "Animation Playback Direction Changed",
        "summary": "Fires at every PingPong reflection.",
        "x-streaming-facing": true,
        "description": "Direction reports the new travel direction (forward or backward) of the sampled motion. High-frequency under short loops (one event per wrap boundary, coalesced to at most one per frame per playback); unthrottled by design like midi.input.\n\n*Example: Flip an overlay arrow each time a patrolling prop reverses, driven by animation.playback.direction.changed.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.direction.changed"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackDirectionChangedData"
            }
          }
        }
      },
      "AnimationPlaybackHeld": {
        "name": "animation.playback.held",
        "title": "Animation Playback Held",
        "summary": "Fires once when a ClampForever playback reaches its end hold.",
        "x-streaming-facing": true,
        "description": "The playback stays active and keeps masking lower layers until an explicit stop; held marks the moment the pose froze.\n\n*Example: When a pose clip settles into its hold, fire the camera zoom on animation.playback.held.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.held"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackHeldData"
            }
          }
        }
      },
      "AnimationPlaybackLooped": {
        "name": "animation.playback.looped",
        "title": "Animation Playback Looped",
        "summary": "Fires once per completed loop of a looping playback.",
        "x-streaming-facing": true,
        "description": "Under Loop it fires at every wrap boundary; under PingPong it fires only on full round trips (each reflection fires directionChanged instead). Cycle carries the completed loop count. High-frequency under short loops (one event per wrap boundary, coalesced to at most one per frame per playback); unthrottled by design like midi.input.\n\n*Example: After three loops of a banner wave, advance the scene by counting animation.playback.looped events.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.looped"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackLoopedData"
            }
          }
        }
      },
      "AnimationPlaybackMarker": {
        "name": "animation.playback.marker",
        "title": "Animation Playback Marker",
        "summary": "Fires when a playing clip crosses a marker position attached to the play or playSet request.",
        "x-streaming-facing": true,
        "description": "Marker carries the request-supplied name and at the normalized position (0 to 1) it was attached at. Crossings are frame-quantized (frame is the engine frame count at detection), paused or held frames fire nothing, and a seek jump skips the crossings it lands past: markers report playback progress, not scrubbing. Markers re-arm per cycle by wrap mode: Loop fires one per cycle, PingPong fires two per round trip, and Once or ClampForever never re-arm.\n\n*Example: Attach a marker named \"impact\" at 0.6 to a punch clip and fire the hit sound on animation.playback.marker instead of a timer.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.marker"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackMarkerData"
            }
          }
        }
      },
      "AnimationPlaybackPaused": {
        "name": "animation.playback.paused",
        "title": "Animation Playback Paused",
        "summary": "Fires when a playback is paused.",
        "x-streaming-facing": true,
        "description": "Pause is a speed-0 hold on the current frame; resume continues from the same frame.\n\n*Example: When chat votes freeze, verify the avatar actually froze via animation.playback.paused before showing the freeze overlay.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.paused"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackPausedData"
            }
          }
        }
      },
      "AnimationPlaybackResumed": {
        "name": "animation.playback.resumed",
        "title": "Animation Playback Resumed",
        "summary": "Fires when a paused playback resumes.",
        "x-streaming-facing": true,
        "description": "Playback continues from the paused frame at the pre-pause speed.\n\n*Example: On !unfreeze, wait for animation.playback.resumed before hiding the freeze overlay.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.resumed"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackResumedData"
            }
          }
        }
      },
      "AnimationPlaybackStarted": {
        "name": "animation.playback.started",
        "title": "Animation Playback Started",
        "summary": "Fires when an animation playback starts on a scene object.",
        "x-streaming-facing": true,
        "description": "Returns the object tag, the playback session id, and the clip name. The group key is always present on every animation.playback event: the group the play named, or null for a play without one. A replay of the same clip emits stopped (reason replaced) followed by a fresh started with a new playbackId. When this playback replaces an active session, the payload also carries supersedes, the playbackId of the session it displaced.\n\n*Example: When a viewer redeems a dance reward, listen for animation.playback.started to sync an on-stream overlay with the exact moment the clip begins.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.started"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackStartedData"
            }
          }
        }
      },
      "AnimationPlaybackStopped": {
        "name": "animation.playback.stopped",
        "title": "Animation Playback Stopped",
        "summary": "Fires when a playback is stopped before its natural end.",
        "x-streaming-facing": true,
        "description": "Reason is explicit (a stop request), replaced (superseded by a replay, retired by a faded play's out-ramp, or handed its blend layer over to an incoming playback), or sceneUnload (the object was torn down). supersededBy is optional: it names the playback that took over and appears whenever one directly displaced this session, which covers a replay of the same clip, a licensed same-group replaceGroup play on animation.play or animation.playSet, and the pinned-layer handoff on animation.transition. Retirement stops that hand nothing over (a faded play's out-ramp) report reason replaced without supersededBy.\n\n*Example: When a moderator types !calm, confirm the hype clip actually halted by waiting for animation.playback.stopped.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "animation.playback.stopped"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/AnimationPlaybackStoppedData"
            }
          }
        }
      },
      "CameraChanged": {
        "name": "camera.changed",
        "title": "Camera Changed",
        "summary": "Fires when the active scene's main camera changes.",
        "x-streaming-facing": true,
        "description": "Carries the new main camera's tag and name, or empty values when none resolves. Subscribers can drive a tally light or swap camera-specific overlays the instant the live camera changes.\n\n*Example: When the main camera switches to your face cam, fade in your webcam frame overlay.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "camera.changed"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/CameraChangedData"
            }
          }
        }
      },
      "ConsoleLog": {
        "name": "console.log",
        "title": "Console Log",
        "summary": "Fires when a console log entry is created in OverMox.",
        "x-streaming-facing": true,
        "description": "Returns the log message text, the entry type as an integer (0 log, 1 warning, 2 error), the contextType as an integer (0 none, 1 graph, 2 node), the context id of the graph or node that wrote the entry (the empty id when contextType is 0) and notificationMessage, the notification text, left out when the entry has none. Streamed for real-time observability of script and graph execution.\n\n*Example: When the audio level peaks above -3dB, log the spike for later replay analysis.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "console.log"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/ConsoleLogData"
            }
          }
        }
      },
      "GraphRunning": {
        "name": "graph.running",
        "title": "Graph Running",
        "summary": "Fires when a graph starts executing.",
        "x-streaming-facing": true,
        "description": "Returns the graphId of the graph that started. The graph runs until graph.stopped fires or POST /api/graphs/{id}/stop is called.\n\n*Example: When a viewer subscribes, run a confetti-spawning graph.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "graph.running"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/GraphRunningData"
            }
          }
        }
      },
      "GraphStopped": {
        "name": "graph.stopped",
        "title": "Graph Stopped",
        "summary": "Fires when a graph stops executing.",
        "x-streaming-facing": true,
        "description": "Returns the graphId of the graph that stopped. Pairs with graph.running to track full execution lifecycle for any subscribed graph.\n\n*Example: When stream ends, listen for graph.stopped on the intro graph and queue the outro.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "graph.stopped"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/GraphStoppedData"
            }
          }
        }
      },
      "MidiInput": {
        "name": "midi.input",
        "title": "Midi Input",
        "summary": "Fires when a MIDI input message arrives from a connected device.",
        "x-streaming-facing": true,
        "description": "Carries subscriptionId, device, eventType (NoteOn, NoteOff, ControlChange, ProgramChange or PitchBend) and timestamp, plus the fields of its type: note, velocity and channel for NoteOn and NoteOff; control, value and channel for ControlChange; value and channel for PitchBend; program and channel for ProgramChange. channel reads 1 to 16 and program 1 to 128, as MIDI devices number them; note, velocity, control and the ControlChange value read 0 to 127, and the PitchBend value 0 to 16383 with 8192 at center. Other MIDI messages are not forwarded. Routed from any MIDI device the runtime is listening on; several devices share one event stream. Keys a message type does not carry are left out.\n\n*Example: When a pad on channel 10 of a drum controller sends NoteOn, flash the stage lights in time with the hit.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "midi.input"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/MidiInputData"
            }
          }
        }
      },
      "NodeDone": {
        "name": "node.done",
        "title": "Node Done",
        "summary": "Fires when a node finishes executing.",
        "x-streaming-facing": true,
        "description": "Returns the nodeId of the node that finished. Pairs with node.executing to trace per-node execution timing across a running graph.\n\n*Example: When chat says '!step', advance the queued sequence after node.done fires on the previous step.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "node.done"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/NodeDoneData"
            }
          }
        }
      },
      "NodeExecuting": {
        "name": "node.executing",
        "title": "Node Executing",
        "summary": "Fires when a node begins executing.",
        "x-streaming-facing": true,
        "description": "Returns the nodeId of the node that started. Useful for visualising live graph execution flow or driving a heads-up display of which node currently holds the cursor.\n\n*Example: When a viewer cheers 100 bits, highlight the receiving node as soon as node.executing fires for it.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "node.executing"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/NodeExecutingData"
            }
          }
        }
      },
      "OvermoxConnected": {
        "name": "overmox.connected",
        "title": "Overmox Connected",
        "summary": "Fires when the OverMox runtime connects to the Controller.",
        "x-streaming-facing": false,
        "description": "Returns the project metadata for the connected OverMox instance, with the project.opened keys (version, path, graphs, variables, restServerURL); the envelope carries no data when no project is open. Marks the start of the connection lifecycle; downstream subscribers may now query scene, graph, and runtime state.",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "overmox.connected"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/OvermoxConnectedData"
            }
          }
        }
      },
      "OvermoxDisconnected": {
        "name": "overmox.disconnected",
        "title": "Overmox Disconnected",
        "summary": "Fires when the OverMox runtime disconnects from the Controller.",
        "x-streaming-facing": false,
        "description": "No payload. Marks the end of the connection lifecycle; downstream subscribers should treat any cached scene, graph, or runtime state as stale until a fresh overmox.connected event arrives.",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "overmox.disconnected"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            }
          }
        }
      },
      "PortsUpdated": {
        "name": "ports.updated",
        "title": "Ports Updated",
        "summary": "Fires when a port value changes, one event per port.",
        "x-streaming-facing": false,
        "description": "Returns one port record per changed port, the record GET /api/ports/{portId}/value returns, whether a running graph or a Layer 1 write (PUT /api/ports/{portId}/value) changed it. A sensitive port's value is left out and hasValue says whether one is set, unless Expose Sensitive Port Values via API is on.",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "ports.updated"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/PortsUpdatedData"
            }
          }
        }
      },
      "ProcessExited": {
        "name": "process.exited",
        "title": "Process Exited",
        "summary": "Fires when a process started via process.start exits.",
        "x-streaming-facing": true,
        "description": "Returns the processId, processName, and exitCode. Pairs with process.loaded to track full lifecycle for any process launched through POST /api/actions/process/start; subscribers filter by processId to react to a specific child.\n\n*Example: When the donation-overlay process crashes, listen for process.exited with the matching processId and queue an automatic relaunch graph.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "process.exited"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/ProcessExitedData"
            }
          }
        }
      },
      "ProcessLoaded": {
        "name": "process.loaded",
        "title": "Process Loaded",
        "summary": "Fires when a process started via process.start finishes loading.",
        "x-streaming-facing": true,
        "description": "Returns the processId, processName, and startTime as an ISO-8601 string. Marks the point at which the operating system reports the launched process as initialised; subscribers filter by processId to gate downstream work on a specific child.\n\n*Example: When a viewer redeems a \"spawn the visualizer\" reward, wait for process.loaded with the new processId before sending input commands to the freshly launched window.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "process.loaded"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/ProcessLoadedData"
            }
          }
        }
      },
      "ProjectClosed": {
        "name": "project.closed",
        "title": "Project Closed",
        "summary": "Fires when the current project closes.",
        "x-streaming-facing": false,
        "description": "No payload. Marks the project lifecycle end; subscribers should release any project-scoped resources and wait for the next project.opened event before resuming project-tied work.",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "project.closed"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            }
          }
        }
      },
      "ProjectOpened": {
        "name": "project.opened",
        "title": "Project Opened",
        "summary": "Fires when a project opens.",
        "x-streaming-facing": false,
        "description": "Returns the project metadata for the newly opened project: version, path, graphs, variables (one record per project variable, with the variable.changed keys variableId, name, variableType and value) and restServerURL. Marks the start of the project lifecycle; subscribers may now query graphs, variables, and assets that belong to the project.",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "project.opened"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/ProjectOpenedData"
            }
          }
        }
      },
      "SceneActivated": {
        "name": "scene.activated",
        "title": "Scene Activated",
        "summary": "Fires when a scene becomes the active scene.",
        "x-streaming-facing": true,
        "description": "Carries the activated scene's tag and name, so subscribers can react the moment a scene goes live instead of polling for the current scene.\n\n*Example: When you switch to your 'Starting Soon' scene, post a stream-starting alert to chat and kick off a countdown.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "scene.activated"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/SceneActivatedData"
            }
          }
        }
      },
      "SceneDeactivated": {
        "name": "scene.deactivated",
        "title": "Scene Deactivated",
        "summary": "Fires when a scene stops being the active scene.",
        "x-streaming-facing": true,
        "description": "Carries the deactivated scene's tag and name. Pair it with scene.activated to measure how long a scene stayed live, or to tear down per-scene state on exit.\n\n*Example: When you switch away from your 'Intermission' scene, stop the on-stream timer.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "scene.deactivated"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/SceneDeactivatedData"
            }
          }
        }
      },
      "StreamingEvent": {
        "name": "streaming.event",
        "title": "Streaming Event",
        "summary": "Fires when a streaming.subscribe subscription delivers a StreamElements or StreamLabs event.",
        "x-streaming-facing": true,
        "description": "Carries the delivering subscription's subscriptionId, platform and eventType, the delivery timestamp, and the event's own fields merged into the same data block, so graphs react to StreamElements and StreamLabs events without per-provider plumbing. Deliveries across all streaming subscriptions are rate-limited to 100 per second; events past the limit in a given second are dropped. A payload holds the fields of its own platform and eventType only.\n\nA streamelements delivery carries its eventType's fields, then isTest (every eventType but chat, streamStatus and counter), then sourcePlatform, then raw. all carries event (the activity type, such as follow, subscriber, tip or cheer), username, displayName, amount and message; follow carries username and displayName; sub carries username, displayName, months, streak, tier, gifted, sender and message; communityGift carries sender, displayName, count and tier; cheer carries username, displayName, bits and message; tip carries username, amount, currency and message; raid carries username, displayName and viewers; superchat carries username, displayName, amount, currency and message; membership carries username, displayName, tier, months, gifted, sender, count and message; channelPoints carries username, displayName, reward and message; hypeTrain carries stage, level, progress and total; chat carries username, displayName, userId, message, color, badges and messageId; streamStatus carries isLive; counter carries counter and value. A field the message does not carry reads as an empty String, 0 or false. isTest is true for an alert emulated from the StreamElements dashboard (a replayed alert included) and false for a live one, so consumers can filter test alerts. sourcePlatform is twitch, youtube or kick, or empty when the event carries no platform (stream status, counters, and tips from the tips feed). raw is the whole gateway message as a JSON string, with a donor's email and location removed; no delivery carries the token. YouTube chat and events need a public stream: StreamElements cannot read a private or unlisted YouTube stream, so none of its chat or events are delivered.\n\nA streamlabs subscription with eventType all delivers each Streamlabs event exactly once, and its raw field is always the raw Streamlabs socket payload as a JSON string, whether or not the event type is recognized. A typed streamlabs eventType carries its named fields (name, message, currency, amount, months or raiders, as the type has them) plus raw, the event message serialized as a JSON string, and name, message and currency are null when the Streamlabs message leaves them out.\n\n*Example: When a viewer cheers 500 bits, a cheer delivery with sourcePlatform twitch and isTest false carries the bits so a graph launches the matching confetti tier.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "streaming.event"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/StreamingEventData"
            }
          }
        }
      },
      "TweenCancelled": {
        "name": "tween.cancelled",
        "title": "Tween Cancelled",
        "summary": "Fires when a tween stops before reaching its end state.",
        "x-streaming-facing": true,
        "description": "Returns the tween identifier, the target object reference, the transform channel the tween was writing, the group identifier the tween belonged to, and why the tween ended early: \"explicit\" for a tween.cancel request, a Tween Cancel node, or a graph stop, \"replaced\" when a new tween took over the same channel. The groupId key is always present and is null for a tween that belonged to no group; a group stopped as a unit reports one event per channel under the same groupId. Graph tween nodes register in the same tween registry as the tween actions, so this event covers tweens a graph started as well as tweens the API started. A stopped tween never also emits tween.completed, so the two events together cover every way a tween can finish exactly once. A replacement retires the prior owner or owners, registers the incoming tween, and only then releases a retired tween's graph continuation, at the next graph update, never inside the command or node execution that performed the replacement. An explicit cancel releases its continuations synchronously.\n\n*Example: When a viewer redeem interrupts a slow banner slide, stop the tween and use tween.cancelled to snap the overlay back to its resting pose.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "tween.cancelled"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/TweenCancelledData"
            }
          }
        }
      },
      "TweenCompleted": {
        "name": "tween.completed",
        "title": "Tween Completed",
        "summary": "Fires when a tween animation reaches its end state.",
        "x-streaming-facing": true,
        "description": "Returns the tween identifier, the target object reference, the transform channel the tween was writing, and the group identifier the tween belongs to. Lets subscribers chain follow-up actions to the exact moment a tween settles, rather than guessing duration. The groupId key is always present and is null for a tween that belongs to no group; it carries the shared identifier for every channel a tween.transform call co-started, so a client counts the identifiers that call returned to know when a whole group has finished. Graph tween nodes register in the same tween registry as the tween actions, so a tween a graph started reports here too.\n\n*Example: When a viewer subscribes, tween a banner from offscreen to centre, then trigger the next graph step on tween.completed.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "tween.completed"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/TweenCompletedData"
            }
          }
        }
      },
      "VariableChanged": {
        "name": "variable.changed",
        "title": "Variable Changed",
        "summary": "Fires when a variable value changes.",
        "x-streaming-facing": true,
        "description": "Returns the variable record: variableId, name, variableType (Boolean, Float, Integer, String, Vector3, Generic or Array) and value, the same record whether a graph, an action or a Layer 1 write changed the variable. The value is a number for Integer and Float, true or false for Boolean, a string for String, an {x, y, z} object for Vector3, the stored JSON value for Generic, and {type, sizeX, sizeY} for Array (the element type and the two sizes, as GET /api/variables/{id} reports it); value is left out when the variable holds none. Subscribers see every assignment, including ones triggered by other graphs or by external API writes through PUT /api/variables/{id}.\n\n*Example: When a viewer donates over $50, increment the donation-total variable and watch variable.changed update the on-screen counter.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "variable.changed"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/VariableChangedData"
            }
          }
        }
      },
      "VariableDeleted": {
        "name": "variable.deleted",
        "title": "Variable Deleted",
        "summary": "Fires when a variable is deleted.",
        "x-streaming-facing": true,
        "description": "Returns the variableId of the removed variable. Subscribers should clear any cached references to that variable so later reads do not resurface stale state.\n\n*Example: When stream ends, delete the per-session vote-tally variable and let variable.deleted clear viewer overlays.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "variable.deleted"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/VariableDeletedData"
            }
          }
        }
      },
      "WebhookReceived": {
        "name": "webhook.received",
        "title": "Webhook Received",
        "summary": "Fires when an inbound HTTP webhook delivers a payload to /api/webhooks/{name}.",
        "x-streaming-facing": true,
        "description": "Broadcasts the listenerId, the request path, the raw request body as payload (a string), and a timestamp. Routes any external HTTP source (donation services, alert dashboards, custom integrations) into the same event stream as native providers.\n\n*Example: When the donation webhook fires above $100, run the milestone celebration graph.*",
        "payload": {
          "type": "object",
          "required": [
            "type",
            "timestamp"
          ],
          "properties": {
            "type": {
              "type": "string",
              "const": "webhook.received"
            },
            "timestamp": {
              "type": "string",
              "format": "date-time"
            },
            "data": {
              "$ref": "#/components/schemas/WebhookReceivedData"
            }
          }
        }
      }
    },
    "schemas": {
      "AnimationPlaybackCompletedData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          },
          "reason": {
            "type": "string"
          },
          "supersededBy": {
            "type": "string"
          }
        }
      },
      "AnimationPlaybackDirectionChangedData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          },
          "direction": {
            "type": "string"
          }
        }
      },
      "AnimationPlaybackHeldData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          }
        }
      },
      "AnimationPlaybackLoopedData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          }
        }
      },
      "AnimationPlaybackMarkerData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          },
          "marker": {
            "type": "string"
          },
          "at": {
            "type": "number"
          }
        }
      },
      "AnimationPlaybackPausedData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          }
        }
      },
      "AnimationPlaybackResumedData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          }
        }
      },
      "AnimationPlaybackStartedData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          },
          "supersedes": {
            "type": "string"
          }
        }
      },
      "AnimationPlaybackStoppedData": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string"
          },
          "playbackId": {
            "type": "string"
          },
          "clip": {
            "type": "string"
          },
          "group": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "cycle": {
            "type": "integer"
          },
          "frame": {
            "type": "integer"
          },
          "reason": {
            "type": "string"
          },
          "supersededBy": {
            "type": "string"
          }
        }
      },
      "CameraChangedData": {
        "type": "object",
        "properties": {
          "tag": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "ConsoleLogData": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "contextType": {
            "type": "integer"
          },
          "context": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "integer"
          },
          "notificationMessage": {
            "type": "string"
          }
        }
      },
      "GraphRunningData": {
        "type": "object",
        "properties": {
          "graphId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "GraphStoppedData": {
        "type": "object",
        "properties": {
          "graphId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "MidiInputData": {
        "type": "object",
        "properties": {
          "subscriptionId": {
            "type": "string"
          },
          "device": {
            "type": "string"
          },
          "eventType": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "note": {
            "type": "integer"
          },
          "velocity": {
            "type": "integer"
          },
          "channel": {
            "type": "integer"
          },
          "control": {
            "type": "integer"
          },
          "value": {
            "type": "integer"
          },
          "program": {
            "type": "integer"
          }
        }
      },
      "NodeDoneData": {
        "type": "object",
        "properties": {
          "nodeId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "NodeExecutingData": {
        "type": "object",
        "properties": {
          "nodeId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "OvermoxConnectedData": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "graphs": {
            "type": "array",
            "items": {}
          },
          "variables": {
            "type": "array",
            "items": {}
          },
          "restServerURL": {
            "type": "string"
          }
        }
      },
      "PortsUpdatedData": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "tag": {
            "type": "string"
          },
          "isInput": {
            "type": "boolean"
          },
          "isConnected": {
            "type": "boolean"
          },
          "value": {
            "description": "Any JSON value; the message description gives its form."
          },
          "isSensitive": {
            "type": "boolean"
          },
          "hasValue": {
            "type": "boolean"
          },
          "options": {
            "type": "array",
            "items": {}
          },
          "isExpandable": {
            "type": "boolean"
          },
          "isExpanded": {
            "type": "boolean"
          },
          "isExpandedChild": {
            "type": "boolean"
          },
          "parentPortId": {
            "type": "string",
            "format": "uuid"
          },
          "affectsNodePorts": {
            "type": "boolean"
          },
          "isDisabled": {
            "type": "boolean"
          }
        }
      },
      "ProcessExitedData": {
        "type": "object",
        "properties": {
          "processId": {
            "type": "integer"
          },
          "processName": {
            "type": "string"
          },
          "exitCode": {
            "type": "integer"
          }
        }
      },
      "ProcessLoadedData": {
        "type": "object",
        "properties": {
          "processId": {
            "type": "integer"
          },
          "processName": {
            "type": "string"
          },
          "startTime": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectOpenedData": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "graphs": {
            "type": "array",
            "items": {}
          },
          "variables": {
            "type": "array",
            "items": {}
          },
          "restServerURL": {
            "type": "string"
          }
        }
      },
      "SceneActivatedData": {
        "type": "object",
        "properties": {
          "tag": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "SceneDeactivatedData": {
        "type": "object",
        "properties": {
          "tag": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "StreamingEventData": {
        "type": "object",
        "properties": {
          "subscriptionId": {
            "type": "string"
          },
          "platform": {
            "type": "string"
          },
          "eventType": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "event": {
            "type": "string"
          },
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "name": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "amount": {
            "type": "number"
          },
          "currency": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "message": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "months": {
            "type": "integer"
          },
          "streak": {
            "type": "integer"
          },
          "tier": {
            "type": "string"
          },
          "gifted": {
            "type": "boolean"
          },
          "sender": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "bits": {
            "type": "integer"
          },
          "viewers": {
            "type": "integer"
          },
          "raiders": {
            "type": "integer"
          },
          "reward": {
            "type": "string"
          },
          "stage": {
            "type": "string"
          },
          "level": {
            "type": "integer"
          },
          "progress": {
            "type": "number"
          },
          "total": {
            "type": "integer"
          },
          "userId": {
            "type": "string"
          },
          "color": {
            "type": "string"
          },
          "badges": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "isLive": {
            "type": "boolean"
          },
          "counter": {
            "type": "string"
          },
          "value": {
            "type": "integer"
          },
          "isTest": {
            "type": "boolean"
          },
          "sourcePlatform": {
            "type": "string"
          },
          "raw": {
            "type": "string"
          }
        }
      },
      "TweenCancelledData": {
        "type": "object",
        "properties": {
          "tweenId": {
            "type": "integer"
          },
          "tag": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "groupId": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "TweenCompletedData": {
        "type": "object",
        "properties": {
          "tweenId": {
            "type": "integer"
          },
          "tag": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "duration": {
            "type": "number"
          },
          "channel": {
            "type": "string"
          },
          "groupId": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "VariableChangedData": {
        "type": "object",
        "properties": {
          "variableId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "variableType": {
            "type": "string"
          },
          "value": {
            "description": "Any JSON value; the message description gives its form."
          }
        }
      },
      "VariableDeletedData": {
        "type": "object",
        "properties": {
          "variableId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "WebhookReceivedData": {
        "type": "object",
        "properties": {
          "listenerId": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "payload": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
