diff --git a/CHANGELOG.md b/CHANGELOG.md index 74f79ab..3d058d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,22 @@ 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 + additive `{ reason, rail }` deny verdict the gateway now publishes on a 402 when a submitted + 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: - `POST /moq/publish/{ns}/{track}` (`mintMoqPublishToken`, scope `moq:write`) and diff --git a/openapi.yaml b/openapi.yaml index dc10210..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 @@ -3544,7 +3552,29 @@ components: items: $ref: '#/components/schemas/X402Accepts' error_detail: - $ref: '#/components/schemas/Error' + allOf: + - $ref: '#/components/schemas/ErrorBody' + - 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