From 6650719201ee0c69b361d2c65ae66bf46298cbda Mon Sep 17 00:00:00 2001 From: Jake Fineman Date: Tue, 11 Aug 2026 11:45:39 -0400 Subject: [PATCH 1/4] fix: align Clip + Voice contracts with the live gateway Verified by probing api.wave.online: ClipCreate previously required the rejected numeric videoId/startTime/endTime shape; the gateway accepts { source: "", in: "5s", out: "10s" }. The voice path is POST /voice (not /voice/generate), requiring only text, and the primary 200 returns raw audio/mpeg bytes. - ClipCreate: source (recording id string) + in/out relative time strings. - /voice/generate -> /voice; VoiceGenerateRequest.text required, voiceId optional (also sent as voice_id). --- CHANGELOG.md | 9 +++++++++ openapi.yaml | 33 +++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f79ab..d6d9fd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ All notable changes to this project are documented here. The format is based on ## [Unreleased] +### Changed + +- **Clip + Voice contracts aligned to the LIVE gateway** (verified by probing `api.wave.online`): + - `ClipCreate` now requires `source` (recording id string) + `in`/`out` relative time strings + (`5s`, `2m`), replacing the rejected `videoId`/`startTime`/`endTime` numeric shape. + - `POST /voice/generate` → `POST /voice` (the live path); `VoiceGenerateRequest` requires only + `text` (`voiceId` optional, also sent as `voice_id` by the SDK). The 200 response now documents + that the primary path returns raw `audio/mpeg` bytes directly. + ### Added - **MoQ join-token mint surface** (`openapi.yaml`) — the Media over QUIC product had no spec at diff --git a/openapi.yaml b/openapi.yaml index dc10210..36d5920 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -390,7 +390,7 @@ paths: items: $ref: '#/components/schemas/Voice' - /voice/generate: + /voice: post: tags: [Voice] summary: Generate speech from text @@ -404,10 +404,10 @@ paths: responses: '200': description: > - Speech generated. The gateway returns one of three shapes depending on - the engine path: an inline JSON payload with base64 audio + character - `alignment` (single round-trip, carries word timestamps), an async job - to poll, or raw audio bytes when timestamps were not requested. + Speech generated. Verified against the live gateway: the primary path returns the raw + audio bytes (`audio/mpeg`) in the response body — POST `/v1/voice` with `{ text }` + returns the MP3 directly. Engines that request timestamps may instead return an inline + JSON payload with base64 audio + character `alignment`, or an async job to poll. content: application/json: schema: @@ -2238,14 +2238,17 @@ components: ClipCreate: type: object - required: [videoId, startTime, endTime] + required: [source, in, out] properties: - videoId: + source: type: string - startTime: - type: number - endTime: - type: number + description: Recording id the clip is cut from (e.g. `rec_abc123`). Verified against the live gateway — the older `{ type, id, start_time, end_time }` source object is rejected. + in: + type: string + description: Start offset as a relative time string, e.g. `5s` or `2m`. + out: + type: string + description: End offset as a relative time string, e.g. `10s` or `1m30s`. title: type: string description: @@ -2398,12 +2401,14 @@ components: VoiceGenerateRequest: type: object - required: [voiceId, text] + required: [text] properties: - voiceId: - type: string text: type: string + description: Text to convert to speech. + voiceId: + type: string + description: Voice id to use. Optional — the gateway picks a default when omitted. The SDK also sends this as `voice_id`. stability: type: number minimum: 0 From 26fe02e22824a9dc83c275c416722660cc8f5654 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Tue, 11 Aug 2026 15:48:39 +0000 Subject: [PATCH 2/4] docs: mark live-gateway contract alignment as breaking in changelog Co-authored-by: Codesmith --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6d9fd1..eb449c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,14 @@ All notable changes to this project are documented here. The format is based on ### Changed -- **Clip + Voice contracts aligned to the LIVE gateway** (verified by probing `api.wave.online`): +- **BREAKING: Clip + Voice contracts aligned to the LIVE gateway** (verified by probing + `api.wave.online`). SDK/CLI clients generated from the previous spec will break: - `ClipCreate` now requires `source` (recording id string) + `in`/`out` relative time strings (`5s`, `2m`), replacing the rejected `videoId`/`startTime`/`endTime` numeric shape. - `POST /voice/generate` → `POST /voice` (the live path); `VoiceGenerateRequest` requires only `text` (`voiceId` optional, also sent as `voice_id` by the SDK). The 200 response now documents - that the primary path returns raw `audio/mpeg` bytes directly. + that the primary path returns raw `audio/mpeg` bytes directly. The old `/voice/generate` path + is not kept as a deprecated alias because the live gateway does not serve it. ### Added From d4546786aac6cb99db54c1a057b154ada35f7a48 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Tue, 11 Aug 2026 15:51:46 +0000 Subject: [PATCH 3/4] fix: clarify /voice 200 media-type selection and model voice_id alias Co-authored-by: Codesmith --- openapi.yaml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index 36d5920..d185537 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -404,20 +404,22 @@ paths: responses: '200': description: > - Speech generated. Verified against the live gateway: the primary path returns the raw - audio bytes (`audio/mpeg`) in the response body — POST `/v1/voice` with `{ text }` - returns the MP3 directly. Engines that request timestamps may instead return an inline - JSON payload with base64 audio + character `alignment`, or an async job to poll. + Speech generated. Returns the raw MP3 bytes (`audio/mpeg`) by default (verified + against the live gateway: POST `/voice` with `{ text }` returns the MP3 directly). + The `application/json` shapes are returned only when the request opts out of the + default: requests that ask for character timestamps receive a `VoiceSynthesisInline` + payload (base64 audio + `alignment`), and engines that process asynchronously return + a `VoiceGeneration` job to poll. content: + audio/mpeg: + schema: + type: string + format: binary application/json: schema: oneOf: - $ref: '#/components/schemas/VoiceSynthesisInline' - $ref: '#/components/schemas/VoiceGeneration' - audio/mpeg: - schema: - type: string - format: binary '401': $ref: '#/components/responses/Unauthorized' '403': @@ -2408,7 +2410,10 @@ components: description: Text to convert to speech. voiceId: type: string - description: Voice id to use. Optional — the gateway picks a default when omitted. The SDK also sends this as `voice_id`. + description: Voice id to use. Optional; the gateway picks a default when omitted. Also accepted on the wire as the `voice_id` alias. + voice_id: + type: string + description: Snake_case wire alias for `voiceId` (the form the SDK sends). Provide either `voiceId` or `voice_id`, not both. stability: type: number minimum: 0 From 16af177b5182ef4cf924d587ca743cc766c167c9 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Tue, 11 Aug 2026 15:53:59 +0000 Subject: [PATCH 4/4] fix: forbid supplying both voiceId and voice_id at the schema level Co-authored-by: Codesmith --- openapi.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openapi.yaml b/openapi.yaml index d185537..08c608e 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2404,6 +2404,9 @@ components: VoiceGenerateRequest: type: object required: [text] + # `voiceId` and `voice_id` are wire aliases for the same setting; supplying both is invalid. + not: + required: [voiceId, voice_id] properties: text: type: string