From 2fe6af9f7a14a54e3ac757cb8857bcfdeb00ef7d Mon Sep 17 00:00:00 2001 From: Jake Fineman Date: Thu, 6 Aug 2026 13:41:20 -0400 Subject: [PATCH 1/7] Add /enhance path (AI video super-resolution, wave-gateway#799) The live-bound POST /v1/enhance route (ESPCN super-resolution, v1 ships exactly one model) had no OpenAPI entry. Documents auth (bearer key, enhance:write scope), the x402 402 challenge for unauthenticated callers (mirrors the Render/MoQ PaymentRequired idiom), the raw-body-or-?url= input contract, the per-job receipt headers (x-enhance-*), the wave_enhance_minutes duration-billing note, and the 400/413/422/501/503 error shapes. Co-Authored-By: Claude Fable 5 --- openapi.yaml | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/openapi.yaml b/openapi.yaml index dc10210..20f6f94 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -72,6 +72,12 @@ tags: Synchronous container-transform for local IP video + Dante/AES67 audio: mux separate RTP H.264 video and Dante audio into one sync-locked MPEG-TS or fMP4 stream, and demux the inverse. x402 metered per minute (`wave_av_minutes`) plus per transform-second (`wave_av_transform_seconds`). + - name: Enhance + description: >- + AI video super-resolution. v1 ships one model, `espcn` (ESPCN, a fixed exact 3x upscale) — + unrecognized `model` values 400. Requires the `enhance:write` entitlement; x402-payable + per-output-minute (`wave_enhance_minutes`, rounded up to the nearest minute of rendered + output duration). - name: Braided Audio description: >- On-demand WAVE Braided Audio publish — mixes multiple named audio sources into one @@ -1590,6 +1596,143 @@ paths: schema: $ref: '#/components/schemas/Error' + # Enhance API + /enhance: + post: + tags: [Enhance] + summary: Super-resolve a video with an AI model (x402-payable) + operationId: enhanceVideo + description: >- + Upscales a video with an on-graph AI model. v1 ships exactly one model, `espcn` (ESPCN + super-resolution, a fixed exact 3x factor baked into the trained weights — not a runtime + parameter). Internally every input frame is letterboxed to a fixed working canvas before + inference, so the v1 output resolution is fixed regardless of input resolution; a future + model may support other output shapes. Send either the raw video bytes as the request body + or a `url` query parameter pointing at an `https` source (the source is fetched + server-side; redirects are not followed and non-public/loopback/private hosts are + rejected). Requires the `enhance:write` entitlement. Billed against the + `wave_enhance_minutes` meter: the OUTPUT artifact's rendered duration in minutes, rounded + up to the next whole minute. + parameters: + - name: model + in: query + required: false + schema: + type: string + enum: [espcn] + default: espcn + description: AI model to apply. v1 supports only `espcn`; any other value 400s. + - name: url + in: query + required: false + schema: + type: string + format: uri + description: >- + Fetch the source video from this `https` URL instead of sending it as the request + body. Must be publicly reachable — loopback, private, link-local, and `.local`/ + `.internal` hosts are rejected before any fetch. + requestBody: + required: false + description: >- + The source video, raw bytes. Omit the body (and use `?url=` instead) to have the source + fetched server-side. Max 200 MiB either way. + content: + video/*: + schema: + type: string + format: binary + application/octet-stream: + schema: + type: string + format: binary + responses: + '200': + description: >- + The enhanced video, binary, streamed back with the same content-type as the source. + Per-job receipt and billing metadata are carried on response headers, not a JSON body. + headers: + x-enhance-model: + schema: + type: string + description: The model that ran, e.g. `espcn`. + x-enhance-scale-factor: + schema: + type: number + description: Upscale factor actually applied. + x-enhance-input-dimensions: + schema: + type: string + description: Input frame dimensions as `WIDTHxHEIGHT`, e.g. `1280x720`. + x-enhance-output-dimensions: + schema: + type: string + description: Output frame dimensions as `WIDTHxHEIGHT`, e.g. `672x672`. + x-wave-meter: + schema: + type: string + description: The meter this job billed against — `wave_enhance_minutes`. + x-wave-usage-minutes: + schema: + type: integer + description: Output-duration minutes billed for this job (rounded up). + content: + video/*: + schema: + type: string + format: binary + '400': + description: >- + Invalid request — unrecognized `model`, an unsafe or invalid `url`, or no video + supplied (neither a request body nor `?url=`). + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + $ref: '#/components/responses/Unauthorized' + '402': + $ref: '#/components/responses/PaymentRequired' + '403': + $ref: '#/components/responses/Forbidden' + '413': + description: The source video (request body, or the resolved `?url=` source) exceeds the 200 MiB limit. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '422': + description: >- + `INPUT_TOO_LARGE` — the source video's frame dimensions exceed the v1 input-size cap. + Downscale the source and retry. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/RateLimitError' + '501': + description: Enhance is not yet available in this environment (the spoke is not provisioned). + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '502': + $ref: '#/components/responses/UpstreamError' + '503': + description: >- + The enhance backend is temporarily unavailable (e.g. cold-start / pool exhaustion). + Retryable; honor the `Retry-After` header. + headers: + Retry-After: + schema: + type: integer + description: Seconds to wait before retrying. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + # AV Mux/Demux API — synchronous container-transform path (RTP H.264 video + Dante/AES67 audio). /av/remux: post: From 84af0f34665290a6880709b90b395766dbb3ba82 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Thu, 6 Aug 2026 17:45:22 +0000 Subject: [PATCH 2/7] docs: cross-reference /enhance and Studio AI enhancement surfaces Co-authored-by: Codesmith --- openapi.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openapi.yaml b/openapi.yaml index 20f6f94..f2be29c 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1059,6 +1059,11 @@ paths: tags: [Studio AI] summary: Create an enhancement job operationId: createEnhancement + description: >- + Creates an asynchronous, credit-billed enhancement job (including `upscale` and + `super_resolution`) against a library video, returning a job envelope to poll. For + synchronous, pay-per-call super-resolution of ad-hoc video bytes (billed against + `wave_enhance_minutes`), use `POST /enhance` instead. requestBody: required: true content: @@ -1613,6 +1618,14 @@ paths: rejected). Requires the `enhance:write` entitlement. Billed against the `wave_enhance_minutes` meter: the OUTPUT artifact's rendered duration in minutes, rounded up to the next whole minute. + + + Distinct from the Studio AI enhancement surface (`POST /studio-ai/enhancements`, which + also offers `upscale`/`super_resolution` job types): Studio AI runs asynchronous, + credit-billed jobs against library videos and returns a job envelope, whereas this + endpoint synchronously super-resolves ad-hoc video bytes (or an `https` source) and + streams the result back, billing `wave_enhance_minutes`. Use Studio AI for library + workflows; use this endpoint for direct, pay-per-call enhancement. parameters: - name: model in: query From e69babf22e187c80803c4dcaaa96d3aef196015a Mon Sep 17 00:00:00 2001 From: yakimoto Date: Thu, 6 Aug 2026 17:47:49 +0000 Subject: [PATCH 3/7] docs: record the new /enhance surface in the changelog (Unreleased) Co-authored-by: Codesmith --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f79ab..2e05d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,22 @@ All notable changes to this project are documented here. The format is based on ### Added +- **Enhance AI video super-resolution surface** (`openapi.yaml`) — the live-routed + `POST /v1/enhance` route (wave-gateway#799) had no spec entry, so no SDK or CLI method could be + generated for it. Adds the `Enhance` tag and the `enhanceVideo` operation: + - `POST /enhance` (scope `enhance:write`), x402-payable via the reusable `PaymentRequired` + 402 response. v1 ships exactly one model, `espcn` (ESPCN super-resolution, fixed 3x factor + baked into the trained weights); any other `model` value 400s. + - Input as raw request body (`video/*` / `application/octet-stream`) or a server-side `?url=` + fetch (`https` only, non-public hosts rejected), capped at 200 MiB either way. + - Binary streaming output with per-job receipt headers: `x-enhance-model`, + `x-enhance-scale-factor`, `x-enhance-input-dimensions`, `x-enhance-output-dimensions`, + `x-wave-meter`, `x-wave-usage-minutes`. Billed against `wave_enhance_minutes` (output + duration in minutes, rounded up). + - Failure modes specified alongside the happy path: 400, 401, the 402 x402 challenge, 403, + 413, `422 INPUT_TOO_LARGE`, 429, 501 (spoke not provisioned), 502, and 503 with + `Retry-After`. Cross-referenced with the async Studio AI enhancement surface + (`POST /studio-ai/enhancements`). - **MoQ join-token mint surface** (`openapi.yaml`) — the Media over QUIC product had no spec at all, so no SDK or CLI could be generated for it. Adds the `MoQ` tag and both mint operations: - `POST /moq/publish/{ns}/{track}` (`mintMoqPublishToken`, scope `moq:write`) and From 8e3bbea7520d81931479cf83cc54880bbf11e272 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Thu, 6 Aug 2026 17:49:25 +0000 Subject: [PATCH 4/7] docs: declare application/octet-stream on the /enhance 200 response Co-authored-by: Codesmith --- openapi.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openapi.yaml b/openapi.yaml index f2be29c..33bccb6 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1694,6 +1694,10 @@ paths: schema: type: string format: binary + application/octet-stream: + schema: + type: string + format: binary '400': description: >- Invalid request — unrecognized `model`, an unsafe or invalid `url`, or no video From e8ce3b0053563a69928957489ca77d7a75a473d7 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Thu, 6 Aug 2026 17:52:33 +0000 Subject: [PATCH 5/7] docs: mark bearer auth optional on /enhance (x402 pay-per-call lane) Co-authored-by: Codesmith --- openapi.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openapi.yaml b/openapi.yaml index 33bccb6..3a44441 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1615,7 +1615,9 @@ paths: model may support other output shapes. Send either the raw video bytes as the request body or a `url` query parameter pointing at an `https` source (the source is fetched server-side; redirects are not followed and non-public/loopback/private hosts are - rejected). Requires the `enhance:write` entitlement. Billed against the + rejected). Auth is either lane: a bearer API key carrying the `enhance:write` + entitlement, or no key at all — an unauthenticated call receives the 402 x402 challenge + and is served once paid (pay-per-call). Billed against the `wave_enhance_minutes` meter: the OUTPUT artifact's rendered duration in minutes, rounded up to the next whole minute. @@ -1626,6 +1628,9 @@ paths: endpoint synchronously super-resolves ad-hoc video bytes (or an `https` source) and streams the result back, billing `wave_enhance_minutes`. Use Studio AI for library workflows; use this endpoint for direct, pay-per-call enhancement. + security: + - {} + - BearerAuth: [] parameters: - name: model in: query From 4833622ec222d4a03eb034d3a1f0fdb4110d5f02 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Thu, 6 Aug 2026 19:09:30 +0000 Subject: [PATCH 6/7] docs: drop private-repo reference from changelog entry Co-authored-by: Codesmith --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e05d74..cb7eb33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ All notable changes to this project are documented here. The format is based on ### Added - **Enhance AI video super-resolution surface** (`openapi.yaml`) — the live-routed - `POST /v1/enhance` route (wave-gateway#799) had no spec entry, so no SDK or CLI method could be + `POST /v1/enhance` route had no spec entry, so no SDK or CLI method could be generated for it. Adds the `Enhance` tag and the `enhanceVideo` operation: - `POST /enhance` (scope `enhance:write`), x402-payable via the reusable `PaymentRequired` 402 response. v1 ships exactly one model, `espcn` (ESPCN super-resolution, fixed 3x factor From bab87555ad2d60f86d10e0721445f6590d96819d Mon Sep 17 00:00:00 2001 From: yakimoto Date: Thu, 6 Aug 2026 19:12:54 +0000 Subject: [PATCH 7/7] docs: document when /enhance returns 401 vs the 402 x402 challenge Co-authored-by: Codesmith --- openapi.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openapi.yaml b/openapi.yaml index 3a44441..48b2f62 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1712,7 +1712,15 @@ paths: schema: $ref: '#/components/schemas/Error' '401': - $ref: '#/components/responses/Unauthorized' + description: >- + Authentication failed AND the x402 pay-per-call lane is not armed in this environment. + When the pay lane is armed (production default), a missing or unrecognized API key + yields the 402 x402 challenge instead — never this 401. A client should treat 401 as + "re-authenticate with a valid bearer key" (pay-per-call is unavailable here). + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '402': $ref: '#/components/responses/PaymentRequired' '403':