# Wowza Video Intelligence Framework API This reference documents every endpoint in the Wowza Video Intelligence Framework (VIF) REST API, including the parameters each one takes and the request and response bodies it works with. VIF adds real-time AI video analysis, such as object detection and scene recognition, to streams running on Wowza Streaming Engine, and this API is served by the Video Intelligence Controller (VIC), a module that runs alongside the Engine's own REST interface, on port 8087 by default. Almost everything you do with this API comes down to one idea: a **config**, the group of settings that tells VIF how to analyze a stream (which detector to run, what to do with the results, and so on). You'll run into a config in three places, and each one uses it a little differently. A **stream group config** applies a config to every stream whose name matches a pattern you choose, such as `cam.*`. An **override** applies a config to one specific stream, by its exact name instead of a pattern. A running **stream** reports the config it's actually using right now, alongside its live connection state and performance. Wherever you see one of these documents in the reference below, look for a `config` member: that's where the settings live. The rest of the document, the match rule, the stream's identity, its live state, just says how that config gets used, and isn't part of the config itself. Configs also build on each other in layers: a stream's final settings come from a default config, then a matching stream group config, then a per-stream override, then any changes made directly to the running stream. Each layer can fill in whatever the one below it left unset. The `Config` schema, wherever it appears in this reference, explains exactly how that merge works, including the two members, `detector` and `listeners`, that don't merge field by field like the rest. Endpoints are grouped below by what they work with: durable configuration you save to disk, the live streams currently running, one-off video analysis jobs, connectivity checks, and framework-wide status. Each group's description explains what you'll find in it. A few things are worth knowing before you make your first request. Every error this API returns is `application/problem+json` (RFC 7807), so `error.title`, `error.status`, and `error.detail` tell you what went wrong; the exceptions are failures the API never gets to handle, like a URL the Engine can't route or an Engine-level authentication or license failure, which come back in the Engine's own `{success, code, message}` format instead. Every document you can edit under `/persist` comes with an `ETag` header; send that value back as `If-Match` when you write, and the API answers `428` if you forget it and `412` if it's gone stale, so you never overwrite a change you haven't seen yet. PATCH requests are a JSON Merge Patch (RFC 7386), not a full replacement: send only the fields you want to change, and anything you leave out keeps its current value. Sending a field as `null` removes it instead, letting it inherit from whatever's underneath, except on a running stream, where `null` isn't accepted for `config` or `config.active`, because a running instance needs values to actually run with; use `POST .../reset` there if you want to discard your changes instead. The verbs you'll use are GET, POST, PATCH, and DELETE: POST creates a document (`409` if one already exists by that name) or triggers an action, like resetting a stream or resuming a job; PATCH edits a document that already exists (`404` if it doesn't); DELETE removes one. This API has no PUT. Wherever an endpoint isn't JSON, uploading or downloading a video file, for example, its description says so explicitly; everything else you send or receive is JSON.
Note: This is version 2 of the VIF REST API. Version 1 is deprecated, and we strongly recommend moving any integration that writes configuration to v2. Saving through v1 rewrites your configuration files in an older format: a per-stream override saved that way loses what made it an override, and comes back instead as a stream group config matched to that one stream's literal name. Reading through v1 stays safe at any time; just make sure every integration that writes configuration moves to v2, and that you don't mix the two on one installation.
## Authentication Engine REST credentials (`engineBasic`). Requests are additionally subject to the Engine license entitlement (402 without it) and Engine RBAC (the "basic" role is read-only). Version: 2.0.0 ## Servers ``` {scheme}://{host}:{port}/v2/vif ``` Variables: - `scheme` Default: "http" Enum: "http", "https" - `host` Default: "localhost" - `port` Default: "8087" ## Security ### engineBasic Wowza Streaming Engine REST credentials (basic; digest is supported by the Engine but not by browser-based generated clients). Subject to the Engine license entitlement and Engine RBAC. Type: http Scheme: basic ## Download OpenAPI description [Wowza Video Intelligence Framework API](https://developer.wowza.com/_bundle/docs/wowza-video-intelligence-framework/api/@current/openapi.yaml) ## Server Framework-wide status: which VIS instances are connected, which streams are running, and which detection models are available on them. These endpoints are read-only. ### Framework status - [GET /status](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/server/getserverstatus.md): Host info, running streams and connected VIS instances. ### Available detection models - [GET /models](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/server/getmodelcatalog.md): Detection models available on the connected VIS instances. ### Available listener types - [GET /listener-types](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/server/listlistenertypes.md): The listener implementations discovered on this Engine — what a config UI offers as the choices for a custom listener's class_name. Advisory, not a gate: discovery scans the Engine's own lib path, and a class it misses can still run. ## Runtime The stream instances currently running. A GET here shows a stream's live, resolved configuration alongside its health and performance. Writes are ephemeral: they change only the running instance, are never saved to disk, and are lost if the stream or the Engine restarts. A `404` on any Runtime endpoint means the stream isn't currently running, not that it doesn't exist at all. ### List running streams - [GET /runtime/streams](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/listruntimestreams.md) ### One running stream - [GET /runtime/apps/{app}/streams/{stream}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/getruntimestream.md): Effective config, health and performance for the stream. ### Update stream settings (ephemeral) - [PATCH /runtime/apps/{app}/streams/{stream}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/updateruntimestream.md): JSON Merge Patch over the stream's config, applied to the running instance and never saved: a stream or Engine restart reverts it. The body is {"config": {…}}; a member at the top level other than config (and the etag a listed document carries, which is ignored) is a 400. config.detector and config.listeners have their own subresources; naming either one here is a 409. ### Discard the stream's ephemeral changes - [POST /runtime/apps/{app}/streams/{stream}/reset](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/resetruntimestream.md): Action endpoint: reverts the running instance to its persisted configuration (stream group config plus override) and applies it live, immediately — restarting the detector session when the change calls for it, never the stream itself. Returns the resulting stream — fresh state and ETag, so the client needs no follow-up GET. Idempotent — succeeds whether or not ephemeral changes exist. No If-Match: this is an action, not a conditional edit. ### Effective detector - [GET /runtime/apps/{app}/streams/{stream}/detector](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/getruntimedetector.md) ### Update detector fields (ephemeral) - [PATCH /runtime/apps/{app}/streams/{stream}/detector](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/updateruntimedetector.md): JSON Merge Patch within the detector, memory only. A patch that changes type replaces the whole detector section. 404 when the stream is not running or runs no detector. ### Effective listeners - [GET /runtime/apps/{app}/streams/{stream}/listeners](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/listruntimelisteners.md) ### Add a listener (ephemeral) - [POST /runtime/apps/{app}/streams/{stream}/listeners](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/createruntimelistener.md): The listener's name travels in the body's name member, required here (400 without it). 409 when a listener with that name already exists. ### One effective listener - [GET /runtime/apps/{app}/streams/{stream}/listeners/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/getruntimelistener.md) ### Update listener fields (ephemeral) - [PATCH /runtime/apps/{app}/streams/{stream}/listeners/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/updateruntimelistener.md) ### Remove a listener (ephemeral) - [DELETE /runtime/apps/{app}/streams/{stream}/listeners/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/deleteruntimelistener.md) ### Thumbnail image of the running stream - [GET /runtime/apps/{app}/streams/{stream}/thumbnail](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/runtime/getthumbnail.md): The latest decoded frame, or a given one. 404 when the stream is not running or has produced no frame yet. ## Persist The durable configuration that lives on disk whether or not a stream is running: stream group configs, which apply automatically to any stream whose name matches a pattern, and per-stream overrides. Writing here saves the document, and if a running stream is governed by it, applies the change to that stream immediately. You can also write an override for a stream that isn't running yet; it takes effect as soon as the stream starts. ### Default config - [GET /persist/configs/default](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/getdefaultconfig.md) ### Update fields of the default config - [PATCH /persist/configs/default](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/updatedefaultconfig.md) ### List stream group configs - [GET /persist/stream-group-configs](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/liststreamgroupconfigs.md): Each config applies to every stream whose application and name match its rule. ### Create a stream group config - [POST /persist/stream-group-configs](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/createstreamgroupconfig.md): name, match.application and match.stream_pattern are required (400 without them). 409 when a stream group config already answers to that name, and for the reserved name default. ### One stream group config - [GET /persist/stream-group-configs/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/getstreamgroupconfig.md) ### Update fields of a stream group config - [PATCH /persist/stream-group-configs/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/updatestreamgroupconfig.md): The match rule can be rewritten freely; the name cannot, because it is the group's identity — a patch that changes it is a 409. A patch that leaves the group without match.application or match.stream_pattern is a 400. ### Delete a stream group config - [DELETE /persist/stream-group-configs/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/deletestreamgroupconfig.md): The streams it governs fall back to what the remaining layers resolve to, without republishing. 409 for the reserved default config, which cannot be deleted. ### List the saved per-stream overrides - [GET /persist/stream-config-overrides](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/liststreamconfigoverrides.md): Every stored override at once, so a client can enumerate saved configuration without knowing the stream names in advance. Each entry carries its stream's identity in application and stream and its own etag; the documents themselves are read and written one at a time under /persist/apps/{app}/streams/{stream}. ### The stream's saved override document - [GET /persist/apps/{app}/streams/{stream}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/getstreamconfigoverride.md): 404 means the stream has no override — it follows its stream group config entirely. ### Create the stream's override - [POST /persist/apps/{app}/streams/{stream}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/createstreamconfigoverride.md): Creates the override document and applies it if the stream is running; 409 when the stream already has one. Works for streams that are not running — the override takes effect when the stream starts. Edits go through PATCH. ### Update fields of the stream's override - [PATCH /persist/apps/{app}/streams/{stream}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/updatestreamconfigoverride.md) ### Delete the stream's override - [DELETE /persist/apps/{app}/streams/{stream}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/deletestreamconfigoverride.md) ### The detector saved in the stream's override - [GET /persist/apps/{app}/streams/{stream}/detector](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/getpersisteddetector.md): 404 means the override does not set a detector — its stream group config's applies. ### Update saved detector fields - [PATCH /persist/apps/{app}/streams/{stream}/detector](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/updatepersisteddetector.md): JSON Merge Patch within the saved detector. A patch that changes type replaces the whole detector section. 404 when the override sets no detector — add one by patching the override document with {"config": {"detector": {...}}}. ### Remove the override detector - [DELETE /persist/apps/{app}/streams/{stream}/detector](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/deletepersisteddetector.md): The stream inherits its stream group config's detector instead. ### The saved override listeners - [GET /persist/apps/{app}/streams/{stream}/listeners](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/listpersistedlisteners.md): 404 when the stream has no override, like its sibling aspects. ### Create a saved listener (and apply if running) - [POST /persist/apps/{app}/streams/{stream}/listeners](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/createpersistedlistener.md): The listener's name travels in the body's name member, required here (400 without it). 409 when a listener with that name already exists. Creates the override when the stream has none. ### One saved listener - [GET /persist/apps/{app}/streams/{stream}/listeners/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/getpersistedlistener.md) ### Update saved listener fields - [PATCH /persist/apps/{app}/streams/{stream}/listeners/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/updatepersistedlistener.md) ### Remove a saved listener - [DELETE /persist/apps/{app}/streams/{stream}/listeners/{name}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/deletepersistedlistener.md) ### The VOD settings document - [GET /persist/vod-settings](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/getvodsettings.md): How this Engine runs on-demand analysis: how many jobs run at once and how many are kept, how long a finished job stays, where the content and the job records live, the default lifecycle webhook and its credential, whether jobs resume on their own, and the upload cap. Stored in its own file, conf.modules/vif/vod/settings.json, beside the stream configuration and never part of it. ### Edit the VOD settings - [PATCH /persist/vod-settings](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/updatevodsettings.md): A merge patch, applied to the running registry at once — a smaller max_jobs evicts, a new job_ttl_seconds reschedules retention, a new webhook or default takes effect for the next job. content_dir and jobs_dir are read at Engine start and apply at the next one. 400 for a bound violation or a lifecycle_webhook_secret naming no configured secret. ### The names of the configured secrets - [GET /persist/secrets](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/getsecrets.md): The credentials a job's lifecycle webhook may be authorized with, by name only — a value never leaves the Engine. Stored in conf.modules/vif/vod/secrets.json. ### Set, rotate or remove secrets - [PATCH /persist/secrets](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/persist/updatesecrets.md): A merge patch over values: a string sets or rotates the named secret, null removes it, a name not mentioned is kept. The revision covers the values, so a rotation changes the ETag even though the answer shows names only. 400 when a removal would leave the VOD settings' lifecycle_webhook_secret naming nothing. ## Probes Connectivity checks for external endpoints, such as the vision-language model a detector calls out to. Use these before you save a configuration, to confirm VIF can actually reach the endpoint you're pointing it at. ### Check connectivity to an OpenAI-compatible VLM endpoint - [POST /probes/vlm-endpoint](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/probes/probevlmendpoint.md) ## VOD On-demand analysis: the video files under the Engine's content directory, and the jobs that analyze them. Unlike a running stream, a job outlives its analysis and is neither a config nor a stream. It just names the config it should use when you submit it, and keeps a record of what it actually used once it's done. ### List the files a VOD job can analyze - [GET /vod/files](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/listvodfiles.md): Every analyzable container under the Engine's content directory, as the relative paths a job names its file by, newest first. At most 500 entries are listed; truncated: true marks a listing that was cut off, and the newest files are the ones kept. A listing is not a probe: a file here can still turn out to carry no H.264 track, which the job reports when it runs. ### Upload a source file into the content directory - [POST /vod/files](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/createvodfile.md): The request body is the file itself; the target name rides the query string because names carry subdirectory slashes. The bytes are written beside the target and renamed into place, so a partial upload is never visible under an analyzable name, and the file is in the listing — and submittable — the moment this answers. Uploads never overwrite: 409 when the name is taken. 400 for no name, an absolute or escaping path, a non-analyzable extension, or a path segment a file already occupies. 413 for an upload larger than the VOD settings' max_upload_bytes. ### Remove a source file from the content directory - [DELETE /vod/files](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/deletevodfile.md): The opposite of the upload: the file ?file= names, exactly as the listing spells it, is removed. Nothing else goes with it — no job record, stored rows or thumbnail, an emptied subdirectory stays, and a symbolic link is removed as the link, never what it points at. Files are not owned by jobs. A finished job keeps its record and results without its source; a failed or cancelled job on a removed file can no longer be resumed — the resume answers 409 saying the source cannot be resolved, and automatic resume stands down. 409 while a queued or running job is using the file, naming the job: cancel it first, then delete. 404 when no such file is there. 400 for no name, an absolute or escaping path, or a non-analyzable extension — the content directory is the Engine's playback directory, and this removes only what the listing could show. ### List the jobs this Engine knows about - [GET /vod/jobs](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/listvodjobs.md): Newest first, one page at a time. The tag and state filters are applied before the page is taken, so total is what they selected and ?state=pending&limit=1 is a queue depth. A listing is a reading, not a subscription: a job can leave the state it was selected for before the answer is read. Finished jobs stay listed until the retention settings evict them or a DELETE removes them. ### Submit a file for analysis - [POST /vod/jobs](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/createvodjob.md): Queues an offline analysis of file, a path under the content directory as GET /vod/files lists it, and answers as soon as the job is queued; progress is polled from the job. The job's configuration is resolved when it is submitted, in the same layers a stream's is: default config < stream_group_config < config. stream_group_config names a stream group config (its match rule plays no part — the job uses its config); config is an inline config layered over it, or over the default config alone. At least one of the two is required; both are allowed. The layering rules are the contract's: a detector declared at a layer replaces the whole detector below it, listeners layer per entry, everything else field by field. What the job resolved to is recorded on it (effective_config on the single-job view) and a later edit of the group never touches a job already submitted. 400 for anything wrong with the body: a missing or unsupported file, an unknown stream_group_config, neither configuration member, a configuration that selects no detector or is inactive, no runnable listener together with store_results: false, an unusable lifecycle_webhook URL, a lifecycle_webhook_secret naming no configured secret. 503 when VOD is unavailable on this Engine. ### One job - [GET /vod/jobs/{jobId}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/getvodjob.md): Includes both config (as submitted) and effective_config (as resolved and run). ### Remove a finished job - [DELETE /vod/jobs/{jobId}](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/deletevodjob.md): Removes the job's record, stored rows and thumbnail. Only a job in a terminal state can be removed: 409 for one still queued or running (cancel it first), for one that has just ended and is still writing its record (try again), and for one that was resumed while the removal waited. A removal is never reported that did not happen. ### Stop a queued or running job - [POST /vod/jobs/{jobId}/cancel](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/cancelvodjob.md): The job settles to cancelled off this request and keeps its record and stored rows; removing those is what DELETE is for. 409 for a job already in a terminal state — a cancel that raced completion says the job completed rather than pretend it stopped anything. ### Resume a failed or cancelled job - [POST /vod/jobs/{jobId}/resume](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/resumevodjob.md): The same job id runs again from where its stored results stop, appending to the same results file rather than analyzing the source afresh; resumes counts the runs. A job submitted with a stream_group_config reloads that group by name and re-layers the inline config it was submitted with — the group must still resolve to the same analysis, or the resume is refused. An inline-only job whose credentials were redacted out of its record takes them from the body: config must be the same analysis as submitted, and only its credentials are taken. A body on a group-built job is refused. 400 for a body this API cannot read. 409 for every other refusal, each saying which: a job still queued or running, a completed job with nothing to fill in, a job that kept no results, a source file that changed since the job ran, a configuration that no longer matches, credentials the record does not hold and the body did not supply, a resume point this file cannot open a window on, a job still writing its record (try again). ### The job's stored detections, one page at a time - [GET /vod/jobs/{jobId}/results](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/listvodjobresults.md): Every response the analysis service returned for the job, as the job stored it — media-time stamped and unfiltered by any listener's gating — verbatim rows, never reshaped. A job still running serves what it has answered so far. ?from_ms&to_ms narrow the page to a half-open stretch of the source. 404 when the job is unknown, when it was submitted with store_results: false, or when it has not stored a row yet; the detail says which. ### The job's detections as NDJSON - [GET /vod/jobs/{jobId}/results/file](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/getvodjobresultsfile.md): The results file itself, one JSON object per line, as an attachment named .jsonl. A job whose results were compressed at rest is served verbatim under Content-Encoding: gzip to a client that accepts it, and decompressed to one that does not; either way what arrives is the same lines. 404 as for the paged results. ### The job's frame (in progress or final) - [GET /vod/jobs/{jobId}/thumbnail](https://developer.wowza.com/docs/wowza-video-intelligence-framework/api/openapi/vod/getvodjobthumbnail.md): The job's own decoded frame — the size, scaling and encoding the detector was given — so it shows what was analyzed rather than a re-render of the source. 404 when the job is unknown or has no frame to show, which is every job on the clip path (a synthetic detector relays encoded video and never decodes a picture).