From be8688de80bb146eeb85727d2a02deaece4770bf Mon Sep 17 00:00:00 2001 From: Grant Date: Tue, 16 Jun 2026 22:48:09 -0500 Subject: [PATCH] Fix OpenAPI spec inaccuracies found by the onboarding test - GET /v1/admin/licenses requires product_id (uuid), not a slug - add the /v1/admin/licenses/search path (was referenced, never defined) - drop the phantom GET /v1/admin/products (only POST exists; list is the public GET /v1/products) - clarify product price_value (write field) vs legacy price_sats --- licensing-service/src/api/openapi.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/licensing-service/src/api/openapi.rs b/licensing-service/src/api/openapi.rs index deb2b99..82b86df 100644 --- a/licensing-service/src/api/openapi.rs +++ b/licensing-service/src/api/openapi.rs @@ -86,9 +86,9 @@ const SPEC_JSON: &str = r##"{ "slug": { "type": "string" }, "name": { "type": "string" }, "description": { "type": "string" }, - "price_sats": { "type": "integer", "nullable": true }, - "price_currency": { "type": "string", "enum": ["SAT", "USD", "EUR"], "nullable": true }, - "price_value": { "type": "integer", "nullable": true }, + "price_sats": { "type": "integer", "nullable": true, "description": "Legacy SAT price. Still accepted on create for backward compatibility; new callers should send price_value + price_currency instead. Also returned in responses (derived from price_value when that path is used)." }, + "price_currency": { "type": "string", "enum": ["SAT", "USD", "EUR"], "nullable": true, "description": "Currency for price_value. Defaults to SAT." }, + "price_value": { "type": "integer", "nullable": true, "description": "Write field: price in the smallest unit of price_currency (sats for SAT, cents for USD/EUR). Send together with price_currency." }, "active": { "type": "boolean" }, "entitlements_catalog": { "type": "array", @@ -263,7 +263,7 @@ const SPEC_JSON: &str = r##"{ "/v1/admin/licenses": { "get": { "summary": "List licenses", - "description": "Scope required: `licenses:read`. Filter by status, product_slug, buyer_email, expiring soon, etc. via query params.", + "description": "Scope required: `licenses:read`. Requires `product_id=` (the product's UUID, not its slug); returns that product's licenses. Use `GET /v1/admin/licenses/search` to look up by buyer_email or invoice id.", "responses": { "200": { "description": "License list" } } }, "post": { @@ -272,6 +272,13 @@ const SPEC_JSON: &str = r##"{ "responses": { "200": { "description": "Issued license" } } } }, + "/v1/admin/licenses/search": { + "get": { + "summary": "Search licenses", + "description": "Scope required: `licenses:read`. Look up licenses by `buyer_email`, `nostr_npub`, or `invoice_id` (whichever is supplied). With no filter, returns the 100 most-recent licenses. The `license_key` is never returned here (only on issue / recover).", + "responses": { "200": { "description": "Matching licenses" } } + } + }, "/v1/admin/licenses/{id}/revoke": { "post": { "summary": "Revoke a license", @@ -301,11 +308,6 @@ const SPEC_JSON: &str = r##"{ } }, "/v1/admin/products": { - "get": { - "summary": "List products", - "description": "Scope required: `products:read`.", - "responses": { "200": { "description": "Product list" } } - }, "post": { "summary": "Create a product", "description": "Scope required: `products:write`.",