From 740f95630283c044f7db7bf2ccbebd4e753c87a7 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Fri, 7 Aug 2026 14:48:04 +0000 Subject: [PATCH 1/3] docs(x402): document error_detail.payment_rejected on the 402 challenge body Co-authored-by: Codesmith --- CHANGELOG.md | 6 ++++++ openapi.yaml | 24 +++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f79ab..dfe5aa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ All notable changes to this project are documented here. The format is based on ### Added +- **`X402PaymentRequired.error_detail.payment_rejected`** (`openapi.yaml`) — documents the + additive `{ reason, rail }` deny verdict the gateway now publishes on a 402 when a submitted + payment was REJECTED (wave-av/wave-gateway#817 / #818), so generated types and docs can + discover the diagnostic field. Optional and conditional: absent on an ordinary unpaid + challenge. + - **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 diff --git a/openapi.yaml b/openapi.yaml index dc10210..0133718 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3544,7 +3544,29 @@ components: items: $ref: '#/components/schemas/X402Accepts' error_detail: - $ref: '#/components/schemas/Error' + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + payment_rejected: + type: object + description: >- + Present ONLY when a submitted payment was REJECTED (as opposed to no payment + having been made at all): the sanitized deny verdict, so a paying caller can + tell a rejected payment apart from an unpaid challenge and correct the named + field. `reason` is a lowercase snake_case token from the gateway's closed deny + vocabulary (e.g. `invalid_permit_header`, `session_expired`, `duplicate`, + `rate_limited`); internal detail that cannot be published verbatim collapses + to the generic `payment_rejected`. `rail` names the payment rail that denied + (e.g. `base-usdc`, `tempo-pathusd`), or `unknown`. + required: [reason, rail] + properties: + reason: + type: string + description: Sanitized machine-readable rejection reason token. + rail: + type: string + description: The payment rail the rejection occurred on. next_action: type: object additionalProperties: true From 43528a1781ed1fd40e7d262a4ad3649b75ee5704 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Fri, 7 Aug 2026 14:50:26 +0000 Subject: [PATCH 2/3] docs(changelog): drop private repo reference to satisfy public-repo-guard Co-authored-by: Codesmith --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfe5aa3..2736ed3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,8 @@ All notable changes to this project are documented here. The format is based on - **`X402PaymentRequired.error_detail.payment_rejected`** (`openapi.yaml`) — documents the additive `{ reason, rail }` deny verdict the gateway now publishes on a 402 when a submitted - payment was REJECTED (wave-av/wave-gateway#817 / #818), so generated types and docs can - discover the diagnostic field. Optional and conditional: absent on an ordinary unpaid - challenge. + payment was REJECTED, so generated types and docs can discover the diagnostic field. Optional + and conditional: absent on an ordinary unpaid challenge. - **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: From b51c42ec90576363be01d51e98d157bcd6d55cf7 Mon Sep 17 00:00:00 2001 From: yakimoto Date: Fri, 7 Aug 2026 14:54:06 +0000 Subject: [PATCH 3/3] fix(x402): model error_detail as the bare error object, not the Error envelope Co-authored-by: Codesmith --- CHANGELOG.md | 9 +++++++ openapi.yaml | 74 +++++++++++++++++++++++++++++----------------------- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2736ed3..3d058d2 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] +### Fixed + +- **`X402PaymentRequired.error_detail` nesting** (`openapi.yaml`): `error_detail` referenced the + `Error` envelope (`{ error: { code, ... } }`), but the gateway nests the bare error object + directly under `error_detail` (`{ code, message, ... }`), with no inner `error` wrapper. The + inner object is now extracted as the `ErrorBody` component schema (referenced by `Error`, so + every other response is unchanged) and `error_detail` composes `ErrorBody` instead, matching + the wire shape so generated types no longer expect a nonexistent `error_detail.error` member. + ### Added - **`X402PaymentRequired.error_detail.payment_rejected`** (`openapi.yaml`) — documents the diff --git a/openapi.yaml b/openapi.yaml index 0133718..8c2a8ee 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1931,39 +1931,47 @@ components: required: [error] properties: error: + $ref: '#/components/schemas/ErrorBody' + + ErrorBody: + type: object + description: > + The normalized WAVE error object itself — the value of the `Error` envelope's + `error` member. Referenced directly by bodies that carry the error object under + a different key (the 402 x402 challenge nests it under `error_detail` without + the envelope wrapper). + required: [code, message] + properties: + code: + type: string + description: Stable machine-readable error code. + message: + type: string + description: Human-readable explanation, safe to surface to end users. + details: type: object - required: [code, message] - properties: - code: - type: string - description: Stable machine-readable error code. - message: - type: string - description: Human-readable explanation, safe to surface to end users. - details: - type: object - additionalProperties: true - description: > - Optional structured context (e.g. failing field, retry limit, - requested scopes) — never raw upstream payloads. - suggestions: - type: array - description: > - Actionable next steps, ordered most→least likely to resolve the - error. Written to be acted on by a human OR an agent. - items: - type: string - did_you_mean: - type: array - description: > - Closest valid alternatives when the caller likely made a typo or - wrong choice (e.g. an unknown scope, product, or route). - items: - type: string - doc_url: - type: string - format: uri - description: Documentation link for this error. + additionalProperties: true + description: > + Optional structured context (e.g. failing field, retry limit, + requested scopes) — never raw upstream payloads. + suggestions: + type: array + description: > + Actionable next steps, ordered most→least likely to resolve the + error. Written to be acted on by a human OR an agent. + items: + type: string + did_you_mean: + type: array + description: > + Closest valid alternatives when the caller likely made a typo or + wrong choice (e.g. an unknown scope, product, or route). + items: + type: string + doc_url: + type: string + format: uri + description: Documentation link for this error. Pagination: type: object @@ -3545,7 +3553,7 @@ components: $ref: '#/components/schemas/X402Accepts' error_detail: allOf: - - $ref: '#/components/schemas/Error' + - $ref: '#/components/schemas/ErrorBody' - type: object properties: payment_rejected: