{"openapi":"3.1.0","info":{"title":"Vixdeo API","summary":"Programmatic access to Vixdeo's content production pipeline.","description":"\nVixdeo public API for content production at scale.\n\n**Authentication.** Every endpoint requires `Authorization: Bearer <credential>`\nwhere the credential is either an API key (`vbp_live_*`, created in\nSettings → Agent access with scopes and an optional expiry) or an OAuth 2.1\naccess token (`vbp_oat_*`) obtained by an MCP host through\n`/.well-known/oauth-authorization-server` (dynamic client registration +\nPKCE). Both resolve to the same principal. JWT tokens from the internal app\nare not accepted on this surface. Revoking a key — or disconnecting an\napplication — takes effect on the next request; a 401 carries\n`WWW-Authenticate` with the RFC 9728 `resource_metadata` URL.\n\n**Errors.** Every non-2xx response uses\n[RFC 7807 Problem Details](https://www.rfc-editor.org/rfc/rfc7807) and\n`Content-Type: application/problem+json`. Match on `type` to\ndetect specific error families (e.g. `unsupported-vbp-version`,\n`payment-required`, `rate-limit-exceeded`).\n\n**Idempotency.** Every state-mutating `POST` requires an\n`Idempotency-Key` header. Retries with the same key and an identical\nbody replay the original response byte-for-byte (with\n`Idempotent-Replay: true`); retries with the same key but a different\nbody return `409 idempotency-conflict`. Records persist for 24h.\n\n**Rate limiting.** Tiered per API key. Every response carries\n`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and\n`X-RateLimit-Bucket` reflecting the most-constrained bucket, plus\nper-bucket scoped triplets like\n`X-RateLimit-Limit-productions_per_minute`. A 429 response carries\n`Retry-After` (seconds).\n\n**Webhooks.** Subscribe to lifecycle events (`production.completed`,\n`production.failed`, `production.cancelled`). Deliveries are signed\nwith HMAC-SHA256; verify the `X-Vixdeo-Signature` header before\ntrusting a payload. Failed deliveries retry on a backoff schedule of\n1m / 5m / 30m / 2h / 12h, then dead-letter.\n\n**Versioning.** Breaking changes ship under `/v2`. The public\ncontract for v1 is the snapshot in `core.vbp_schema_v1` plus the\nschemas in `api.v1.schemas` — additive changes only.\n\n**Quickstart.** See the [API quickstart](https://vixdeo.com/v1/quickstart.md)\nfor cURL, Python and JavaScript walkthroughs of \"create your first\nproduction\".\n","termsOfService":"https://vixdeo.com/terms","contact":{"name":"Vixdeo API support","url":"https://vixdeo.com/support","email":"api@vixdeo.com"},"license":{"name":"Proprietary — Vixdeo Terms of Service","url":"https://vixdeo.com/terms"},"version":"1.0"},"servers":[{"url":"/v1"},{"url":"https://vixdeo.com/v1","description":"Production"},{"url":"http://localhost:8000/v1","description":"Local development"}],"paths":{"/productions":{"post":{"tags":["productions"],"summary":"Submit a Video Blueprint for production","description":"Launch a new production from a Video Blueprint.\n\nSteps performed:\n1. Parse the VBP through the versioned dispatcher (Phase 3.1).\n   Unsupported versions surface as a typed Problem Details\n   response (``unsupported-vbp-version``).\n2. Resolve the target channel (optional, but if the API key is\n   locked to a specific channel it must match).\n3. Check the org's monthly quota; ``402 Payment Required`` if\n   exhausted and overage is disabled.\n4. Look for an active run that matches the\n   ``idempotency_key`` / request fingerprint; if found, return\n   its production_id with ``deduplicated: true``.\n5. Auto-create a Project + ProjectVersion owned by the caller's\n   org, with the VBP-derived script as the version's content.\n6. Persist the Run, consume quota, and dispatch the Celery\n   pipeline. Failures during dispatch roll back the run to\n   ``failed`` and surface a ``503 Service Unavailable``.","operationId":"create_production_productions_post","security":[{"VbpApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","description":"Client-generated unique identifier (e.g. UUID v4). Retries with the same key and an identical body replay the original response. Required for every POST on this endpoint.","title":"Idempotency-Key"},"description":"Client-generated unique identifier (e.g. UUID v4). Retries with the same key and an identical body replay the original response. Required for every POST on this endpoint."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionCreateRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionAcceptedOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"402":{"description":"Organization has exhausted its monthly video quota and overage is not enabled. `extensions.period_resets_at` indicates when the next period starts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency conflict: the same `Idempotency-Key` was used with a different request body. Use a fresh key, or repeat the original body to receive the cached response.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/productions/{production_id}":{"get":{"tags":["productions"],"summary":"Read a production's status, steps, and cost","operationId":"get_production_productions__production_id__get","security":[{"VbpApiKey":[]}],"parameters":[{"name":"production_id","in":"path","required":true,"schema":{"type":"string","title":"Production Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionDetail"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/productions/{production_id}/cancel":{"post":{"tags":["productions"],"summary":"Mark a production cancelled","operationId":"cancel_production_productions__production_id__cancel_post","security":[{"VbpApiKey":[]}],"parameters":[{"name":"production_id","in":"path","required":true,"schema":{"type":"string","title":"Production Id"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","description":"Client-generated unique identifier. Cancel is naturally idempotent at the workflow level, but the header is required by the v1 idempotency contract so retries during a network blip return the cached terminal state instead of re-touching the run.","title":"Idempotency-Key"},"description":"Client-generated unique identifier. Cancel is naturally idempotent at the workflow level, but the header is required by the v1 idempotency contract so retries during a network blip return the cached terminal state instead of re-touching the run."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionDetail"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency conflict: the same `Idempotency-Key` was used with a different request body. Use a fresh key, or repeat the original body to receive the cached response.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/channels":{"get":{"tags":["channels"],"summary":"List channels in the caller's organization","operationId":"list_channels_channels_get","security":[{"VbpApiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelListOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/channels/{channel_id}":{"get":{"tags":["channels"],"summary":"Read a single channel by id","description":"Return the channel detail.\n\nThe ``resolve_channel_for_caller`` dependency already validates\norg ownership and the API key's channel lock; the explicit\n``require_scope(\"read\")`` dependency is kept so the OpenAPI doc\nadvertises the scope requirement to integrators.","operationId":"get_channel_channels__channel_id__get","security":[{"VbpApiKey":[]}],"parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","description":"ChannelProfile.id","title":"Channel Id"},"description":"ChannelProfile.id"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/channels/{channel_id}/radar":{"get":{"tags":["channels"],"summary":"Latest Radar analysis for a channel","description":"Return the most recent ``ready`` Radar analysis for this channel.\n\nReturns ``null`` (HTTP 200 with body ``null``) if the channel has\nnever received a Radar analysis. The ``null`` shape is documented\nin the OpenAPI ``response_model`` and is preferred over a 404 here\nbecause the channel itself exists and the caller has access — the\nabsence of an analysis is data, not an authorization issue.","operationId":"get_channel_radar_channels__channel_id__radar_get","security":[{"VbpApiKey":[]}],"parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","description":"ChannelProfile.id","title":"Channel Id"},"description":"ChannelProfile.id"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RadarAnalysisOut"},{"type":"null"}],"title":"Response Get Channel Radar Channels  Channel Id  Radar Get"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/usage":{"get":{"tags":["usage"],"summary":"Current period quota for the caller's organization","description":"Return the org's quota status for the current calendar month.\n\nThe :func:`core.quota.check_quota` helper reads quota without\nmutating it; we additionally pull the underlying row for\n``period_start`` (which the dataclass status doesn't carry) so\nintegrators can render a \"resets in X days\" indicator without\na second round-trip.","operationId":"get_usage_usage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"VbpApiKey":[]}]}},"/webhooks/events":{"get":{"tags":["webhooks"],"summary":"List the event types you may subscribe to","description":"Discovery endpoint: integrators call this once to dynamically\npopulate their subscription UI without hardcoding the event list.","operationId":"get_supported_events_webhooks_events_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportedEventsOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"VbpApiKey":[]}]}},"/webhooks":{"post":{"tags":["webhooks"],"summary":"Create a webhook subscription","description":"Register a new outbound webhook subscription.\n\nThe plaintext ``secret`` is generated server-side and returned in\nthis response **once** — the DB stores only the hash. Persist it\nimmediately on the integrator side; rotation is the only way to\nrecover from a lost secret.","operationId":"create_webhook_webhooks_post","security":[{"VbpApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","description":"Required client-generated unique identifier. Retries with the same key replay the original response (and the same secret), so subscription creation is safe under retries.","title":"Idempotency-Key"},"description":"Required client-generated unique identifier. Retries with the same key replay the original response (and the same secret), so subscription creation is safe under retries."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreatedOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency conflict: the same `Idempotency-Key` was used with a different request body. Use a fresh key, or repeat the original body to receive the cached response.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"get":{"tags":["webhooks"],"summary":"List webhooks in the caller's organization","operationId":"list_webhooks_webhooks_get","security":[{"VbpApiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookListOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{webhook_id}":{"get":{"tags":["webhooks"],"summary":"Read a webhook by id","operationId":"get_webhook_webhooks__webhook_id__get","security":[{"VbpApiKey":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["webhooks"],"summary":"Update a webhook subscription","operationId":"update_webhook_webhooks__webhook_id__patch","security":[{"VbpApiKey":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["webhooks"],"summary":"Delete a webhook subscription","operationId":"delete_webhook_webhooks__webhook_id__delete","security":[{"VbpApiKey":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{webhook_id}/rotate-secret":{"post":{"tags":["webhooks"],"summary":"Generate a fresh secret for an existing webhook","description":"Rotate the secret. Returns the new plaintext **once**.\n\nFrom the integrator's side this should be paired with a brief\noverlap window where they accept signatures from BOTH the old and\nthe new secret (because in-flight deliveries signed under the old\nsecret may still arrive). The overlap is the integrator's\nresponsibility — we cannot orchestrate it for them.","operationId":"rotate_webhook_secret_webhooks__webhook_id__rotate_secret_post","security":[{"VbpApiKey":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","title":"Idempotency-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreatedOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{webhook_id}/deliveries":{"get":{"tags":["webhooks"],"summary":"List recent delivery attempts for a webhook","description":"Surface the last N deliveries for debugging and replay UIs.","operationId":"list_webhook_deliveries_webhooks__webhook_id__deliveries_get","security":[{"VbpApiKey":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by delivery status: pending | delivered | dead.","title":"Status"},"description":"Filter by delivery status: pending | delivered | dead."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryListOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{webhook_id}/deliveries/{delivery_id}/replay":{"post":{"tags":["webhooks"],"summary":"Manually re-trigger a delivery attempt","description":"Reset the delivery row to ``pending`` and schedule an immediate\nre-attempt.\n\nUse cases:\n\n- The consumer was misconfigured, dropped a delivery, and now wants\n  to re-process it after fixing their handler.\n- A ``signing_unavailable`` failure (worker process restarted\n  between enqueue and dispatch) needs to be retried after a\n  ``rotate-secret`` call has reseeded the secret cache.\n\nCounter-intuitively, replay does NOT reset ``attempts`` to zero —\nwe want the audit log to reflect the total work done. It does\nhowever clear the ``status``/``next_attempt_at`` so the dispatcher\nwill pick the row up on its next sweep.","operationId":"replay_webhook_delivery_webhooks__webhook_id__deliveries__delivery_id__replay_post","security":[{"VbpApiKey":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"delivery_id","in":"path","required":true,"schema":{"type":"string","title":"Delivery Id"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","title":"Idempotency-Key"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryOut"}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Resource does not exist or is not visible to the caller.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Idempotency conflict: the same `Idempotency-Key` was used with a different request body. Use a fresh key, or repeat the original body to receive the cached response.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/mcp":{"post":{"tags":["mcp"],"summary":"MCP JSON-RPC entry point (agent operator surface)","description":"Single MCP JSON-RPC entry point.\n\nAuth = API-key (``Bearer vbp_*``) with at least ``read`` scope, via the same\ndependency the rest of /v1 uses. Notifications (no ``id``) return 202 with no\nbody, per JSON-RPC.","operationId":"mcp_endpoint_mcp_post","security":[{"VbpApiKey":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional client-generated unique identifier for the budget-spending tool calls (`run_op` / `commit` / `generate_script`). Unlike `/v1/productions`, MCP does NOT require it (reads and notifications carry none); when supplied on a write, a retry with the same key and an identical body replays the original response.","title":"Idempotency-Key"},"description":"Optional client-generated unique identifier for the budget-spending tool calls (`run_op` / `commit` / `generate_script`). Unlike `/v1/productions`, MCP does NOT require it (reads and notifications carry none); when supplied on a write, a retry with the same key and an identical body replays the original response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Missing or invalid `Authorization: Bearer vbp_*` header.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Authenticated, but the API key lacks the required scope (`read` < `write` < `admin`) or is locked to a different channel/organization than the resource.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Request body failed schema validation. `extensions.errors` lists per-field violations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded. The `Retry-After` header (seconds) and `extensions.retry_after_seconds` indicate when to retry. `extensions.bucket` names which bucket tripped.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}},"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"schemas":{"ChannelListOut":{"properties":{"channels":{"items":{"$ref":"#/components/schemas/ChannelOut"},"type":"array","title":"Channels"}},"additionalProperties":false,"type":"object","required":["channels"],"title":"ChannelListOut","examples":[{"channels":[{"channel_handle":"@solarinsights","channel_id":"UCx1Y2z3a4B5C6D7E8F9G0H","channel_title":"Solar Insights","created_at":"2026-03-12T10:14:22Z","id":"ch_2g1Z9mB7"}]}]},"ChannelOut":{"properties":{"id":{"type":"string","title":"Id"},"channel_id":{"type":"string","title":"Channel Id","description":"External (YouTube) channel id."},"channel_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Title"},"channel_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Handle"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"additionalProperties":false,"type":"object","required":["id","channel_id"],"title":"ChannelOut","examples":[{"channel_handle":"@solarinsights","channel_id":"UCx1Y2z3a4B5C6D7E8F9G0H","channel_title":"Solar Insights","created_at":"2026-03-12T10:14:22Z","id":"ch_2g1Z9mB7"}]},"JsonRpcRequest":{"properties":{"jsonrpc":{"type":"string","title":"Jsonrpc","default":"2.0"},"id":{"anyOf":[{},{"type":"null"}],"title":"Id"},"method":{"type":"string","title":"Method"},"params":{"type":"object","title":"Params","default":{}}},"type":"object","required":["method"],"title":"JsonRpcRequest","examples":[{"id":1,"jsonrpc":"2.0","method":"tools/call","params":{"arguments":{},"name":"list_projects"}}]},"Problem":{"properties":{"type":{"type":"string","title":"Type","description":"Stable identifier for this class of error. Opaque URI under `https://vixdeo.com/errors/` — match on the slug after the last `/`, e.g. `quota-exceeded`, `unsupported-vbp-version`.","examples":["https://vixdeo.com/errors/payment-required"]},"title":{"type":"string","title":"Title","description":"Short human-readable summary, stable across occurrences.","examples":["Payment Required"]},"status":{"type":"integer","title":"Status","description":"HTTP status code, mirrors the response status line.","examples":[402]},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail","description":"Long-form message specific to this occurrence.","examples":["Plan studio has reached its monthly limit (30/30)."]},"instance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instance","description":"URI path of the request that produced the error.","examples":["/v1/productions"]},"extensions":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Extensions","description":"Machine-readable context relevant to `type`. For example, `payment-required` carries `plan`, `videos_used`, `videos_limit`; `rate-limit-exceeded` carries `bucket`, `limit`, `window_seconds`, `retry_after_seconds`.","examples":[{"plan":"studio","videos_limit":30,"videos_used":30}]}},"additionalProperties":true,"type":"object","required":["type","title","status"],"title":"Problem","description":"RFC 7807 Problem Details document.\n\nMatch on ``type`` (an opaque, stable URI under\n``https://vixdeo.com/errors/``) to detect specific error families\nwithout parsing ``detail``. Machine-readable context lives in\n``extensions``.","examples":[{"detail":"Plan studio has reached its monthly limit (30/30).","extensions":{"period_resets_at":"2026-05-01T00:00:00+00:00","plan":"studio","videos_limit":30,"videos_used":30},"instance":"/v1/productions","status":402,"title":"Payment Required","type":"https://vixdeo.com/errors/payment-required"}]},"ProductionAcceptedOut":{"properties":{"production_id":{"type":"string","title":"Production Id"},"status":{"type":"string","title":"Status"},"deduplicated":{"type":"boolean","title":"Deduplicated","description":"True if an in-flight production matched the idempotency key / request fingerprint and the existing production_id was returned.","default":false}},"additionalProperties":false,"type":"object","required":["production_id","status"],"title":"ProductionAcceptedOut","description":"Response for ``POST /v1/productions``.\n\nAlways returns 202 even when the request is deduplicated against\nan in-flight production — ``deduplicated`` flags whether this is\na fresh launch or a replay.","examples":[{"deduplicated":false,"production_id":"prod_2g1Z9mB7","status":"queued"}]},"ProductionCostOut":{"properties":{"estimated_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Cost Usd"},"actual_cost_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Actual Cost Usd"},"provider_calls":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Provider Calls"}},"additionalProperties":true,"type":"object","title":"ProductionCostOut","description":"Cost summary block. Mirrors the keys returned by\n:func:`core.run_manager.get_run_cost_summary`.","examples":[{"actual_cost_usd":1.18,"estimated_cost_usd":1.42,"provider_calls":7}]},"ProductionCreateRequest":{"properties":{"blueprint":{"type":"object","title":"Blueprint","description":"A Video Blueprint document. Must include `schema_version` (or one of the accepted aliases `$schema` / `vbp_version`) and at least one scene with non-empty `narrator_text`."},"channel_profile_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Profile Id","description":"Target channel for this production. Required unless the API key is locked to a specific channel — in which case this field, if provided, must match the locked channel."},"auto_publish":{"type":"boolean","title":"Auto Publish","description":"If true, the production is published to YouTube on completion. Requires a connected channel with valid OAuth.","default":false}},"additionalProperties":false,"type":"object","required":["blueprint"],"title":"ProductionCreateRequest","description":"Request body for ``POST /v1/productions``.\n\nThe ``blueprint`` field accepts a Video Blueprint payload. The\ndispatcher in :func:`core.vbp_versioning.parse_vbp` validates the\nschema version, so unsupported versions surface as\n``UnsupportedVbpVersionError`` translated into a typed Problem\nDetails response by :mod:`api.v1.errors`.","examples":[{"auto_publish":false,"blueprint":{"scenes":[{"narrator_text":"Rooftop solar pays for itself in under seven years on average.","visual_mode":"stock"},{"narrator_text":"Federal tax credits cover thirty percent of installation costs.","visual_mode":"stock"}],"schema_version":"1.0","title":"How rooftop solar pays for itself"},"channel_profile_id":"ch_2g1Z9mB7"}]},"ProductionDetail":{"properties":{"production_id":{"type":"string","title":"Production Id","description":"Stable identifier for this production."},"status":{"type":"string","title":"Status","description":"`pending` | `running` | `completed` | `failed` | `cancelled`."},"worker_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Worker Status"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"final_video_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Final Video Url"},"youtube_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Url"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"steps":{"items":{"$ref":"#/components/schemas/ProductionStepOut"},"type":"array","title":"Steps"},"cost":{"anyOf":[{"$ref":"#/components/schemas/ProductionCostOut"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["production_id","status"],"title":"ProductionDetail","description":"Full status response for ``GET /v1/productions/{id}``.","examples":[{"completed_at":"2026-04-27T05:33:42Z","cost":{"actual_cost_usd":1.18,"estimated_cost_usd":1.42,"provider_calls":7},"created_at":"2026-04-27T05:29:50Z","final_video_url":"https://cdn.vixdeo.com/renders/prod_2g1Z9mB7.mp4","production_id":"prod_2g1Z9mB7","started_at":"2026-04-27T05:29:51Z","status":"completed","steps":[{"completed_at":"2026-04-27T05:30:18Z","started_at":"2026-04-27T05:30:00Z","status":"completed","step_name":"clip_prompting"}],"worker_status":"finished","youtube_url":"https://youtu.be/dQw4w9WgXcQ"}]},"ProductionStepOut":{"properties":{"step_name":{"type":"string","title":"Step Name"},"status":{"type":"string","title":"Status"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"additionalProperties":false,"type":"object","required":["step_name","status"],"title":"ProductionStepOut","examples":[{"completed_at":"2026-04-27T05:30:18Z","started_at":"2026-04-27T05:30:00Z","status":"completed","step_name":"clip_prompting"}]},"RadarAnalysisOut":{"properties":{"analysis_id":{"type":"string","title":"Analysis Id"},"channel_profile_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Profile Id"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"status":{"type":"string","title":"Status"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"suggestions":{"items":{"$ref":"#/components/schemas/RadarSuggestionOut"},"type":"array","title":"Suggestions"}},"additionalProperties":false,"type":"object","required":["analysis_id","status"],"title":"RadarAnalysisOut","examples":[{"analysis_id":"rad_8K3pQ","channel_profile_id":"ch_2g1Z9mB7","completed_at":"2026-04-26T22:01:34Z","created_at":"2026-04-26T22:00:00Z","region":"US","status":"completed","suggestions":[{"confidence_score":78,"rationale":"3 of your top-5 videos are calculator-style; topic gap vs competitors.","summary":"Walk viewers through inputs and show a live-recalc widget.","title":"5-minute roof solar break-even calculator"}]}]},"RadarSuggestionOut":{"properties":{"title":{"type":"string","title":"Title"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"confidence_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Confidence Score"},"rationale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rationale"}},"additionalProperties":true,"type":"object","required":["title"],"title":"RadarSuggestionOut","description":"A single Radar suggestion. Schema mirrors what\n:func:`core.radar.serialize_radar_analysis` produces, but exposed\nexplicitly so the public contract is independent of the internal\nserializer.","examples":[{"confidence_score":78,"rationale":"3 of your top-5 videos are calculator-style; topic gap vs competitors at the 4-7min length.","summary":"Walk viewers through inputs (kWh, latitude, install cost) and show a live-recalc widget.","title":"5-minute roof solar break-even calculator"}]},"SupportedEventsOut":{"properties":{"events":{"items":{"type":"string"},"type":"array","title":"Events"}},"additionalProperties":false,"type":"object","required":["events"],"title":"SupportedEventsOut","description":"Discovery endpoint for integrators: which event types may be\nsubscribed to.","examples":[{"events":["production.completed","production.failed","production.cancelled"]}]},"UsageOut":{"properties":{"plan_id":{"type":"string","title":"Plan Id"},"videos_used":{"type":"integer","title":"Videos Used"},"videos_limit":{"type":"integer","title":"Videos Limit"},"videos_remaining":{"type":"integer","title":"Videos Remaining"},"overage_allowed":{"type":"boolean","title":"Overage Allowed"},"period_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Period Start"},"period_end":{"type":"string","format":"date-time","title":"Period End"},"organization_id":{"type":"string","title":"Organization Id"}},"additionalProperties":false,"type":"object","required":["plan_id","videos_used","videos_limit","videos_remaining","overage_allowed","period_end","organization_id"],"title":"UsageOut","examples":[{"organization_id":"org_4HpL2xR","overage_allowed":false,"period_end":"2026-05-01T00:00:00Z","period_start":"2026-04-01T00:00:00Z","plan_id":"studio","videos_limit":50,"videos_remaining":36,"videos_used":14}]},"WebhookCreateRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name","description":"Human-readable label for this webhook subscription."},"url":{"type":"string","maxLength":2048,"minLength":1,"title":"Url","description":"Target URL for outbound POST requests. Must be HTTP(S); production deployments only accept HTTPS. Loopback addresses are rejected in production."},"events":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Events","description":"List of event types to subscribe to. See ``GET /v1/webhooks/events`` for the supported set."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"}},"additionalProperties":false,"type":"object","required":["name","url","events"],"title":"WebhookCreateRequest","description":"Request body for ``POST /v1/webhooks``.\n\nThe plain ``secret`` is generated by the server and returned in the\nresponse exactly once; clients cannot supply their own. This\nmatches Stripe and removes a class of \"low-entropy secret\" bugs.","examples":[{"description":"Forwarded to the content ops Slack via Zapier.","events":["production.completed","production.failed"],"name":"Production lifecycle → internal CRM","url":"https://hooks.example.com/vixdeo"}]},"WebhookCreatedOut":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"url":{"type":"string","title":"Url"},"events":{"items":{"type":"string"},"type":"array","title":"Events"},"active":{"type":"boolean","title":"Active"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"secret":{"type":"string","title":"Secret","description":"Plaintext webhook secret. SHOWN ONCE: persist it on your side immediately. Use it to verify the `X-Vixdeo-Signature` header on each delivery."}},"additionalProperties":false,"type":"object","required":["id","name","url","events","active","secret"],"title":"WebhookCreatedOut","description":"``POST /v1/webhooks`` response. The ``secret`` field is the\nplaintext secret and is returned **once** — the server stores only\nits hash, so it cannot be retrieved again. Rotate via\n``POST /v1/webhooks/{id}/rotate-secret``.","examples":[{"active":true,"created_at":"2026-04-20T11:23:00Z","description":"Forwarded to the content ops Slack via Zapier.","events":["production.completed","production.failed"],"id":"whk_3mP9Lq","name":"Production lifecycle → internal CRM","secret":"whsec_7n3K2pQ9rS4tU5vW6xY7zA8bC9dE0fG1hH","updated_at":"2026-04-20T11:23:00Z","url":"https://hooks.example.com/vixdeo"}]},"WebhookDeliveryListOut":{"properties":{"deliveries":{"items":{"$ref":"#/components/schemas/WebhookDeliveryOut"},"type":"array","title":"Deliveries"}},"additionalProperties":false,"type":"object","required":["deliveries"],"title":"WebhookDeliveryListOut","examples":[{"deliveries":[{"attempts":1,"completed_at":"2026-04-27T05:33:43Z","created_at":"2026-04-27T05:33:42Z","event_id":"evt_9rT2xK","event_type":"production.completed","id":"whd_5nP8qR","last_attempt_at":"2026-04-27T05:33:43Z","last_response_status":200,"occurred_at":"2026-04-27T05:33:42Z","status":"succeeded","webhook_id":"whk_3mP9Lq"}]}]},"WebhookDeliveryOut":{"properties":{"id":{"type":"string","title":"Id"},"webhook_id":{"type":"string","title":"Webhook Id"},"event_id":{"type":"string","title":"Event Id"},"event_type":{"type":"string","title":"Event Type"},"status":{"type":"string","title":"Status"},"attempts":{"type":"integer","title":"Attempts"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"},"last_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Attempt At"},"next_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Attempt At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"last_response_status":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Response Status"},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"additionalProperties":false,"type":"object","required":["id","webhook_id","event_id","event_type","status","attempts"],"title":"WebhookDeliveryOut","description":"Read-only delivery record exposed to integrators for debugging\nand replay UIs.","examples":[{"attempts":1,"completed_at":"2026-04-27T05:33:43Z","created_at":"2026-04-27T05:33:42Z","event_id":"evt_9rT2xK","event_type":"production.completed","id":"whd_5nP8qR","last_attempt_at":"2026-04-27T05:33:43Z","last_response_status":200,"occurred_at":"2026-04-27T05:33:42Z","status":"succeeded","webhook_id":"whk_3mP9Lq"}]},"WebhookListOut":{"properties":{"webhooks":{"items":{"$ref":"#/components/schemas/WebhookOut"},"type":"array","title":"Webhooks"}},"additionalProperties":false,"type":"object","required":["webhooks"],"title":"WebhookListOut","examples":[{"webhooks":[{"active":true,"created_at":"2026-04-20T11:23:00Z","events":["production.completed"],"id":"whk_3mP9Lq","name":"Production lifecycle → internal CRM","updated_at":"2026-04-20T11:23:00Z","url":"https://hooks.example.com/vixdeo"}]}]},"WebhookOut":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"url":{"type":"string","title":"Url"},"events":{"items":{"type":"string"},"type":"array","title":"Events"},"active":{"type":"boolean","title":"Active"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"additionalProperties":false,"type":"object","required":["id","name","url","events","active"],"title":"WebhookOut","description":"Public view of a webhook (no secret).","examples":[{"active":true,"created_at":"2026-04-20T11:23:00Z","description":"Forwarded to the content ops Slack via Zapier.","events":["production.completed","production.failed"],"id":"whk_3mP9Lq","name":"Production lifecycle → internal CRM","updated_at":"2026-04-20T11:23:00Z","url":"https://hooks.example.com/vixdeo"}]},"WebhookUpdateRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":120,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"url":{"anyOf":[{"type":"string","maxLength":2048,"minLength":1},{"type":"null"}],"title":"Url"},"events":{"anyOf":[{"items":{"type":"string"},"type":"array","minItems":1},{"type":"null"}],"title":"Events"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"additionalProperties":false,"type":"object","title":"WebhookUpdateRequest","description":"Body for ``PATCH /v1/webhooks/{id}``.\n\nAll fields are optional; omitted fields stay unchanged. ``url`` and\n``events`` revalidate using the same rules as create.","examples":[{"active":true,"events":["production.completed","production.failed","production.cancelled"]}]}},"securitySchemes":{"VbpApiKey":{"type":"http","scheme":"bearer"}}},"tags":[{"name":"productions","description":"Submit a Video Blueprint for production and monitor its progress. The state-mutating endpoint (`POST /productions`) requires an `Idempotency-Key` header.","externalDocs":{"description":"VBP schema reference","url":"https://github.com/draxork/vixdeo/blob/main/core/vbp_schema_v1.py"}},{"name":"channels","description":"Discover and inspect channels in your organization, including the latest Radar analysis. Read-only on `/v1`; channel CRUD lives in the internal admin surface."},{"name":"usage","description":"Current billing-period quota. Useful to surface remaining videos in your own dashboards before you trip a `402 payment-required`."},{"name":"webhooks","description":"Subscribe to lifecycle events, replay failed deliveries, and rotate signing secrets. Deliveries follow a Stripe-compatible signature scheme (`X-Vixdeo-Signature: t=<unix_ts>,v1=<hex_hmac>`)."}]}