{"openapi":"3.1.0","info":{"title":"SlideForge API","description":"Agent-first PowerPoint generation. Send structured slide intents (a `form` plus typed fields) or a brief; get back a real, editable `.pptx`. The draw path is deterministic — no model renders your slide, so supplied numbers and text come out verbatim. Every response carries `status`, `fidelity` and `warnings` so an automated caller can tell whether the output is usable. $0.05 per slide; an unusable slide is not billed. Authenticate with a SlideForge API key (`Authorization: Bearer sf_live_…` or `X-API-Key`) — new accounts include 60 free slides, no credit card: https://slideforge.dev/sign-up","version":"5.345.0"},"paths":{"/v1/tools/pdf-analyze":{"post":{"tags":["tools-free-tier"],"summary":"Inspect a PDF and say what is possible (public, no auth, no conversion)","description":"The public half of /tools/pdf-to-pptx: returns producer, page count, source classification and `can_extract` so a visitor learns whether their PDF converts to editable shapes — before signing up. No job, no persistence, no conversion, no charge. Converting is authenticated: POST /v1/deck/from-pdf.","operationId":"public_pdf_analyze_v1_tools_pdf_analyze_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_public_pdf_analyze_v1_tools_pdf_analyze_post"}}},"required":true},"responses":{"200":{"description":"Analysis returned","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid PDF (not a PDF, corrupted, empty)"},"413":{"description":"File too large"},"429":{"description":"Rate limit exceeded (30/hour/IP)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tools/pdf-to-pptx":{"post":{"tags":["tools-free-tier"],"summary":"Convert the first 3 pages of a PDF to editable .pptx (public, no auth)","description":"The free taste: a real, editable, unwatermarked .pptx of the first 3 pages, on a 1-hour download link. Only PDFs the analyzer classifies as `slide-vector` convert here — that path is pure compute. 25 MB max, 5 requests per hour per IP. The full document is authenticated: POST /v1/deck/from-pdf.","operationId":"free_pdf_to_pptx_v1_tools_pdf_to_pptx_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_free_pdf_to_pptx_v1_tools_pdf_to_pptx_post"}}},"required":true},"responses":{"200":{"description":"Converted — response includes a signed download_url","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid PDF (not a PDF, corrupted, empty)"},"413":{"description":"File exceeds the 25 MB free-tier cap"},"422":{"description":"This PDF's bucket does not convert on the free path"},"429":{"description":"Rate limit exceeded (5/hour/IP)"},"503":{"description":"Conversion timed out or storage unavailable"}}}},"/v1/tools/combine-powerpoint":{"post":{"tags":["tools-free-tier"],"summary":"Merge the first 2 uploaded .pptx files into one deck (public, no auth)","description":"The free merge: your first 2 uploaded decks combined in full, on a 1-hour download link. Pure compute — no LLM, no watermark, and no AI-disclosure stamp, because these are your slides and we only concatenated them. 20 MB per file. 5 merges per hour per IP.","operationId":"free_combine_powerpoint_v1_tools_combine_powerpoint_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_free_combine_powerpoint_v1_tools_combine_powerpoint_post"}}},"required":true},"responses":{"200":{"description":"Merged — response includes a signed download_url","content":{"application/json":{"schema":{}}}},"400":{"description":"Fewer than 2 .pptx files, or a file that is not a readable .pptx"},"413":{"description":"A file exceeds 20 MB, or the pair exceeds 40 MB"},"429":{"description":"Rate limit exceeded (5/hour/IP)"},"503":{"description":"Busy, merge failed, or storage unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tools/preview-variant":{"post":{"tags":["tools-free-tier"],"summary":"Render a catalog variant to a preview PNG (public, no auth)","description":"Renders one pinned catalog variant to an image so a visitor can see real output before signing up. No .pptx is produced and nothing is downloadable. Takes a `prior_id` from the catalog — NOT a free-text brief: matching a brief to a layout needs the router, which spends model tokens, so it happens behind a free account.","operationId":"preview_catalog_variant_v1_tools_preview_variant_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariantPreviewRequest"}}},"required":true},"responses":{"200":{"description":"Rendered — response includes a short-lived preview_url","content":{"application/json":{"schema":{}}}},"400":{"description":"Unknown prior_id"},"429":{"description":"Rate limit exceeded (30/hour/IP)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tools/preview-code":{"post":{"tags":["tools-free-tier"],"summary":"Run python-pptx code and return a preview image (public, no auth)","description":"Executes python-pptx code in the sandbox and returns slide 1 as an inline PNG (768px) plus static lint findings (overlaps, off-canvas shapes, tiny fonts, chart semantics) in `lint[]`. Send a python-pptx script as it is (the presentation it saves is the one shown), or a `build(prs)` function. No .pptx is produced and nothing is downloadable — the file, PDF export, and full-resolution renders live in the playground and /v1/render/code. 16KB code cap, 10s timeout, 1 slide.","operationId":"preview_code_v1_tools_preview_code_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodePreviewRequest"}}},"required":true},"responses":{"200":{"description":"Preview rendered, or code diagnostics with status invalid/failed","content":{"application/json":{"schema":{}}}},"429":{"description":"Rate limit exceeded (3/minute, 20/hour per IP)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tools/attempt":{"post":{"tags":["tools-free-tier"],"summary":"Record an anonymous free-tool attempt (public, no auth)","description":"Fire-and-forget beacon so an anonymous attempt on a free-tool page is countable. Accepts shape only — file count, total bytes, mime types. Never filenames or content.","operationId":"record_tool_attempt_v1_tools_attempt_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolAttempt"}}},"required":true},"responses":{"200":{"description":"Recorded (or silently dropped)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolAttemptAck"}}}},"429":{"description":"Rate limit exceeded (60/hour/IP)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/.well-known/agent-card.json":{"get":{"tags":["oauth"],"summary":"Agent Card","description":"A2A Protocol — Agent Card for discovery by other agents.","operationId":"agent_card__well_known_agent_card_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"tags":["oauth"],"summary":"Oauth Protected Resource","description":"RFC 9728 - OAuth Protected Resource Metadata (root path).","operationId":"oauth_protected_resource__well_known_oauth_protected_resource_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource/{path}":{"get":{"tags":["oauth"],"summary":"Oauth Protected Resource Subpath","description":"RFC 9728 - OAuth Protected Resource Metadata (subpath, e.g. /mcp, /mcp-connectors).","operationId":"oauth_protected_resource_subpath__well_known_oauth_protected_resource__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"tags":["oauth"],"summary":"Oauth Authorization Server","description":"RFC 8414 - Authorization Server Metadata.","operationId":"oauth_authorization_server__well_known_oauth_authorization_server_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/register":{"get":{"tags":["oauth"],"summary":"Oauth Register Info","description":"RFC 7591 - Dynamic Client Registration endpoint info (GET).","operationId":"oauth_register_info_oauth_register_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["oauth"],"summary":"Oauth Register","description":"RFC 7591 - Dynamic Client Registration.\n\nRedirect URIs are validated to prevent open-redirect attacks:\n- HTTPS, loopback HTTP, or a private-use native-app scheme (RFC 8252 §7.1:\n  cursor://, windsurf://, ...). Plain http:// to a real host stays rejected\n  (cleartext), as do browser-executable schemes. PKCE — S256, enforced\n  unconditionally at /oauth/authorize — is what makes custom schemes safe:\n  an app that hijacks the scheme catches the code but cannot exchange it\n  without the verifier. ADR-2026-08-16-dcr-custom-scheme-redirects.\n\nCap of 100 registered clients enforced via LRU eviction (oldest by\nlast_used_at) — friendlier than 503 for legitimate clients while\nbounding storage growth.","operationId":"oauth_register_oauth_register_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/authorize":{"get":{"tags":["oauth"],"summary":"Oauth Authorize","description":"OAuth 2.1 Authorization endpoint - redirects to Auth0 with PKCE.","operationId":"oauth_authorize_oauth_authorize_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/callback":{"get":{"tags":["oauth"],"summary":"Oauth Callback","description":"OAuth callback - receives Auth0 code, redirects back to MCP client.","operationId":"oauth_callback_oauth_callback_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/revoke":{"post":{"tags":["oauth"],"summary":"Oauth Revoke","description":"RFC 7009 token revocation — ADR-2026-08-01 clause 5.\n\nProxies revocation to Auth0 and drops our matching grant rows. This is what lets a client's\n\"disconnect\" actually kill the long-lived credential rather than merely forgetting it.\n\nBounded on purpose: revoking a refresh token does NOT end the session. The outstanding access\ntoken stays valid until `exp` (24h in prod) plus up to 30 min of middleware token cache. That\nbound exists today for every access token; this endpoint does not widen it and does not claim\nto close it.\n\nPer RFC 7009 §2.2 an unknown or already-revoked token is a SUCCESS, not an error — otherwise\nthe endpoint becomes a token-validity oracle.","operationId":"oauth_revoke_oauth_revoke_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/oauth/token":{"post":{"tags":["oauth"],"summary":"Oauth Token","description":"OAuth 2.1 Token endpoint - exchanges code for access token.","operationId":"oauth_token_oauth_token_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/auth/keys":{"get":{"tags":["auth"],"summary":"List your API keys","description":"List your API keys (prefixes only, never full keys).","operationId":"list_keys_v1_auth_keys_get","responses":{"200":{"description":"List of API key metadata","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"}}},"post":{"tags":["auth"],"summary":"Create a new API key","description":"Create a new API key. The full key is shown once only -- store it securely.","operationId":"create_key_v1_auth_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}},"required":true},"responses":{"200":{"description":"API key created (full key returned once)","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/keys/default":{"get":{"tags":["auth"],"summary":"Get default API key","description":"Returns the stored default API key for the current user. Only available for the default key.","operationId":"get_default_key_v1_auth_keys_default_get","responses":{"200":{"description":"Default API key","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"},"404":{"description":"No default key stored"}}}},"/v1/auth/keys/{key_id}":{"delete":{"tags":["auth"],"summary":"Revoke an API key","description":"Revoke an API key (soft delete). The key will no longer authenticate.","operationId":"revoke_key_v1_auth_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer","title":"Key Id"}}],"responses":{"200":{"description":"Key revoked","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"},"404":{"description":"Key not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/provision":{"post":{"tags":["auth"],"summary":"Provision a new user","description":"Create a SlideForge user, wallet, and API key from an OAuth user. Called by the web frontend server-side route, authenticated via X-Provision-Secret header.","operationId":"provision_user_v1_auth_provision_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionRequest"}}},"required":true},"responses":{"200":{"description":"User provisioned with API key","content":{"application/json":{"schema":{}}}},"403":{"description":"Invalid provision secret"},"409":{"description":"User already exists (returns existing API key prefix)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/profile":{"get":{"tags":["billing"],"summary":"Get billing profile","description":"Company/tax details used on invoices.","operationId":"get_billing_profile_v1_billing_profile_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"put":{"tags":["billing"],"summary":"Update billing profile","description":"Create or update the company/tax details used on invoices.","operationId":"put_billing_profile_v1_billing_profile_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingProfile"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/invoices":{"get":{"tags":["billing"],"summary":"List invoices","description":"Invoiceable wallet top-ups for the caller, each with its invoice (if issued).","operationId":"list_invoices_v1_billing_invoices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["billing"],"summary":"Issue an invoice","description":"Issue (or return the existing) invoice for one paid top-up. Requires a billing profile.","operationId":"post_invoice_v1_billing_invoices_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueInvoiceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/invoices/{invoice_number}":{"get":{"tags":["billing"],"summary":"Get an invoice","description":"Invoice metadata + a fresh signed download URL.","operationId":"get_invoice_v1_billing_invoices__invoice_number__get","parameters":[{"name":"invoice_number","in":"path","required":true,"schema":{"type":"string","title":"Invoice Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/checkout":{"post":{"tags":["billing"],"summary":"Create a Stripe Checkout session","description":"Create a Stripe Checkout session for wallet top-up. Volume discounts: $50→+10%, $100→+15%, $200→+20%. Returns checkout_url to redirect the user to Stripe.","operationId":"create_checkout_v1_billing_checkout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}},"required":true},"responses":{"200":{"description":"Checkout session created","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid amount"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/topup/{session_id}":{"get":{"tags":["billing"],"summary":"Open-tracked redirect into a Stripe Checkout session","description":"Records that a minted top-up link was actually opened, then 302s to Stripe. Public: the unguessable session id is the credential; only sessions this API minted (metadata.source=slideforge_wallet) are redirected.","operationId":"topup_redirect_v1_topup__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"302":{"description":"Redirect to Stripe Checkout (or the outcome page)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/charge-saved":{"post":{"tags":["billing"],"summary":"Charge saved card for instant top-up","description":"Charge the user's saved card (from auto top-up setup) for an instant wallet top-up. No Stripe Checkout redirect needed. Volume discounts apply.","operationId":"charge_saved_card_v1_billing_charge_saved_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}},"required":true},"responses":{"200":{"description":"Card charged, wallet credited","content":{"application/json":{"schema":{}}}},"400":{"description":"No saved card or invalid amount"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/setup-intent":{"post":{"tags":["billing"],"summary":"Create Stripe SetupIntent for card saving","description":"Creates a Stripe Customer (if needed) and SetupIntent. Returns client_secret for Stripe Elements card setup.","operationId":"create_setup_intent_v1_billing_setup_intent_post","responses":{"200":{"description":"SetupIntent created","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"}}}},"/v1/billing/autotopup":{"get":{"tags":["billing"],"summary":"Get auto top-up settings","description":"Returns current auto top-up configuration including card info.","operationId":"get_autotopup_v1_billing_autotopup_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["billing"],"summary":"Save auto top-up settings","description":"Save auto top-up settings after card is confirmed via SetupIntent. Validates threshold/amount constraints and verifies payment method with Stripe.","operationId":"save_autotopup_v1_billing_autotopup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoTopupSettings"}}},"required":true},"responses":{"200":{"description":"Settings saved","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid settings"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["billing"],"summary":"Disable auto top-up","description":"Disable auto top-up. Keeps card on file for re-enabling later.","operationId":"delete_autotopup_v1_billing_autotopup_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/billing/plan":{"get":{"tags":["billing"],"summary":"Current self-serve plan","description":"The account's plan (monthly/yearly), its ceiling and slides used this period, or null; plus the catalogue of plans and the default offer.","operationId":"get_plan_v1_billing_plan_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"delete":{"tags":["billing"],"summary":"Cancel the plan at period end","description":"The paid period stays usable; nothing is refunded.","operationId":"cancel_plan_v1_billing_plan_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/billing/plans/checkout":{"post":{"tags":["billing"],"summary":"Start a plan","description":"Mints a Stripe subscription checkout for the named plan. 409 while a plan is open.","operationId":"plan_checkout_v1_billing_plans_checkout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanCheckoutRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/plan/overflow":{"post":{"tags":["billing"],"summary":"Continue from wallet credit past the plan","description":"On (the default): past the plan's slides, renders continue at $0.05 a slide from wallet credit.","operationId":"plan_overflow_v1_billing_plan_overflow_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanOverflowRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/plan/reactivate":{"post":{"tags":["billing"],"summary":"Keep a cancelled plan","description":"Undoes cancel-at-period-end while the paid period is still running; the plan renews again.","operationId":"plan_reactivate_v1_billing_plan_reactivate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/billing/plan/upgrade":{"post":{"tags":["billing"],"summary":"Upgrade monthly to yearly now","description":"Stripe invoices the yearly price less the unused part of the month; the ceiling resets.","operationId":"plan_upgrade_v1_billing_plan_upgrade_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/billing/portal":{"post":{"tags":["billing"],"summary":"Open the Stripe billing portal","description":"Mints a Stripe Billing Portal session for this account's saved card: change the card behind the plan, cancel. 404 when no card is on file in this Stripe mode.","operationId":"billing_portal_v1_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/email/unsubscribe":{"post":{"tags":["email"],"summary":"Unsubscribe One Click","description":"RFC 8058 one-click. The mail client posts here with no human present.\n\nThe token travels in the query string — that is what the List-Unsubscribe header carries.\nThe POST body is the fixed `List-Unsubscribe=One-Click` and is not read.","operationId":"unsubscribe_one_click_v1_email_unsubscribe_post","parameters":[{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["email"],"summary":"Unsubscribe Link","description":"The link in the body, clicked by a person.","operationId":"unsubscribe_link_v1_email_unsubscribe_get","parameters":[{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/email/clicked":{"post":{"tags":["email"],"summary":"Record Click","description":"Stamp the first click on a campaign mail. Called by the web middleware, not a person.\n\nCampaign links are ordinary slideforge.dev URLs carrying `?src=&sid=`; Resend's own click\ntracking, which would rewrite every link through a tracking subdomain, stays off. So the\nclick reaches us the same way any other visit does and this endpoint is what turns a\nlanding into an attributed click.\n\nUnauthenticated for the same reason unsubscribe is: the reader has usually not logged in\nyet — /console/billing, where the top-up mails point, bounces them through Auth0 first.\nA `sid` grants nothing; the only effect it can have is setting one timestamp on the row\nit names, once.\n\nSilent on an unknown id. The middleware fires this without waiting for the answer, so a\n404 would be read by nobody and would only tell a prober which ids exist.","operationId":"record_click_v1_email_clicked_post","parameters":[{"name":"sid","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tools/{tool_name}":{"post":{"tags":["mcp"],"summary":"Rest Tool Dispatch","description":"REST alias over the MCP tool dispatch — same tools, same handlers, API-key auth.\n\nExists so the local stdio MCP wrapper (smartdatabrokers/slideforge-mcp) forwards as a\nplain REST client, not an MCP proxy: the wrapper bakes the tool schemas in locally and\nPOSTs each call's `arguments` object here. Body shape == the MCP tool's inputSchema.\nReturns the tool-result envelope (MCP content blocks). Cost events + wallet accounting\nflow exactly as the /mcp/ tools/call path — this reuses the same dispatch.","operationId":"rest_tool_dispatch_v1_tools__tool_name__post","parameters":[{"name":"tool_name","in":"path","required":true,"schema":{"type":"string","title":"Tool Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/_widget_debug":{"post":{"tags":["mcp"],"summary":"Widget Debug Ping","description":"No-auth debug ping for live ChatGPT widget diagnostics.\n\nIframe runtime POSTs JSON state here; we log it. No auth on purpose —\nlog-only, no side effects. Temporary debugging surface; remove after\nthe ChatGPT widget render is stable.","operationId":"widget_debug_ping__widget_debug_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp/canonical/":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_canonical__post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp/canonical":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_canonical_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp-connectors/":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_connectors__post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp-connectors":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_connectors_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp/copilot/":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_copilot__post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp/copilot":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_copilot_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp/openai/":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_openai__post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp/openai":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_openai_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/mcp/":{"post":{"tags":["mcp"],"summary":"Mcp Endpoint","description":"MCP Streamable HTTP endpoint — handles JSON-RPC 2.0 messages.","operationId":"mcp_endpoint_mcp__post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/assets/logo":{"post":{"tags":["assets"],"summary":"Upload Logo Endpoint","description":"Upload a logo image for slide chrome injection.\n\nAccepts PNG, JPG, SVG, GIF, WebP. Max 5MB.\nReturns logo_id for use in generate requests.","operationId":"upload_logo_endpoint_v1_assets_logo_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_logo_endpoint_v1_assets_logo_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/assets/image":{"post":{"tags":["assets"],"summary":"Upload Image Endpoint","description":"Upload an image for use in the Image spec component.\n\nAccepts PNG, JPG, GIF, WebP, SVG. Max 10MB.\nReturns asset_id; place it on a slide as `image_src: \"asset:<id>\"`.","operationId":"upload_image_endpoint_v1_assets_image_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_image_endpoint_v1_assets_image_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/assets/{asset_id}":{"delete":{"tags":["assets"],"summary":"Delete an uploaded image asset","description":"Delete a user-owned uploaded/generated image asset. Logos are content-addressed and shared by hash, so logo deletion is not exposed here.","operationId":"delete_asset_endpoint_v1_assets__asset_id__delete","parameters":[{"name":"asset_id","in":"path","required":true,"schema":{"type":"string","title":"Asset Id"}}],"responses":{"200":{"description":"Asset deleted","content":{"application/json":{"schema":{}}}},"404":{"description":"Asset not found or not owned by you"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/assets/image/generate":{"post":{"tags":["assets"],"summary":"Generate Image Endpoint","description":"Generate an image via AI and store as a persistent asset.\n\nWraps the existing generate_image() pipeline (used internally by creative mode)\nand returns an asset_id that the Image spec component can consume.\n\nModel selection precedence:\n  1. Explicit `model` (gateway model ID) wins.\n  2. Else `quality` ('draft' | 'balanced' | 'premium') maps via QUALITY_TIER_MODELS.\n  3. Else defaults to 'balanced' (QUALITY_TIER_MODELS['balanced']).\n\n$0.05/generation (flat — independent of model tier for now).\nSize options: 1024x1024 (square), 1792x1024 (wide), 1024x1792 (tall).","operationId":"generate_image_endpoint_v1_assets_image_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/compile":{"post":{"tags":["compile"],"summary":"Compile slides into a deck","description":"Merge multiple slides into a single PPTX deck. Returns 202 with job_id -- poll GET /v1/jobs/{id} for the deck URL.","operationId":"compile_deck_v1_compile_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompileRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"202":{"description":"Compile job queued"},"400":{"description":"No job_ids provided or too many (max 50)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/files/{path}":{"get":{"tags":["files"],"summary":"Download a file","description":"Stream a file from storage. Requires a valid HMAC signature (sig) and expiry (exp) in query params. Signatures are generated by the API when returning job results.","operationId":"get_file_v1_files__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"sig","in":"query","required":true,"schema":{"type":"string","description":"HMAC-SHA256 signature","title":"Sig"},"description":"HMAC-SHA256 signature"},{"name":"exp","in":"query","required":true,"schema":{"type":"integer","description":"Unix timestamp expiry","title":"Exp"},"description":"Unix timestamp expiry"},{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable filename for download","title":"Name"},"description":"Human-readable filename for download"}],"responses":{"200":{"description":"File stream","content":{"application/json":{"schema":{}}}},"403":{"description":"Invalid or expired signature"},"404":{"description":"File not found"},"503":{"description":"Storage not configured"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/previews/ephemeral/{path}":{"get":{"tags":["files"],"summary":"Download a free ephemeral preview PNG","description":"Stream a playground preview image. Requires a valid HMAC signature (sig) and expiry (exp). Serves only ephemeral preview blobs — never invoices or deliverables.","operationId":"get_ephemeral_preview_v1_previews_ephemeral__path__get","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}},{"name":"sig","in":"query","required":true,"schema":{"type":"string","description":"HMAC-SHA256 signature","title":"Sig"},"description":"HMAC-SHA256 signature"},{"name":"exp","in":"query","required":true,"schema":{"type":"integer","description":"Unix timestamp expiry","title":"Exp"},"description":"Unix timestamp expiry"}],"responses":{"200":{"description":"PNG image","content":{"application/json":{"schema":{}},"image/png":{}}},"403":{"description":"Invalid or expired signature"},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deck":{"post":{"tags":["deck"],"summary":"Removed — the legacy convergence deck engine is gone (ADR 2026-06-14)","description":"Gone (410). Render a deck of structured intents via POST /v1/render/intent/deck (or the MCP create_deck tool); assemble existing slides via POST /v1/deck/from-jobs.","operationId":"generate_deck_v1_deck_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Legacy deck engine removed"}}}},"/v1/deck/outline":{"post":{"tags":["deck"],"summary":"Predict the deck plan (engines + cost) without rendering","description":"Free preview of a deck render. Returns per-slide engine and cost: every slide goes through the SSG intent engine at a flat $0.05/slide. Thin briefs carry a `brief_hint` coaching nudge. No render, no wallet charge.","operationId":"deck_outline_v1_deck_outline_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeckRequest"}}},"required":true},"responses":{"200":{"description":"Outline + cost estimate","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deck/{deck_id}":{"patch":{"tags":["deck"],"summary":"Replace slides in an existing deck and recompile","description":"Swap one or more slides in a previously compiled deck. Pass position (1-based) and any completed job_id. Returns 202 — poll GET /v1/jobs/{deck_id} for recompile progress.","operationId":"update_deck_v1_deck__deck_id__patch","parameters":[{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeckUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"202":{"description":"Recompile started"},"400":{"description":"Invalid request"},"404":{"description":"Deck not found"},"409":{"description":"Deck not in a recompilable state"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deck/from-jobs":{"post":{"tags":["deck"],"summary":"Assemble a deck from existing slide job_ids","description":"Compose an existing set of completed slide jobs into a real deck. Cheaper than POST /v1/deck (no LLM, no generation), more polished than POST /v1/compile (creates a real `job_type=deck` row that PATCH /v1/deck/{id} can later edit).\n\nUse this when you have a set of slides from prior /v1/generate or /v1/render calls and want to bundle them into an editable deck.","operationId":"deck_from_jobs_v1_deck_from_jobs_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeckFromJobsRequest"}}},"required":true},"responses":{"202":{"description":"Deck creation queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeckQueued"}}}},"400":{"description":"No job_ids provided or invalid"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deck/{deck_id}/fork":{"post":{"tags":["deck"],"summary":"Fork a deck — create a new editable copy with the same children","description":"Create an A/B variant of a deck. The new deck has the same slide content as the source (sharing blob references — no duplication) but its own metadata, so PATCH /v1/deck/{new_id} edits don't affect the source. Free.","operationId":"fork_deck_v1_deck__deck_id__fork_post","parameters":[{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeckForkRequest"}}}},"responses":{"202":{"description":"Fork queued","content":{"application/json":{"schema":{}}}},"404":{"description":"Source deck not found"},"400":{"description":"Source job is not a deck"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deck/combine":{"post":{"tags":["deck"],"summary":"Merge uploaded .pptx files into one deck","description":"Merge up to 10 uploaded .pptx files into a single deck, in the order given. Free — merging spends no model tokens, so nothing is charged and no balance is touched. 20 MB per file, 100 MB total. Anonymous callers get the first two files via POST /v1/tools/combine-powerpoint instead.","operationId":"deck_combine_v1_deck_combine_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_deck_combine_v1_deck_combine_post"}}},"required":true},"responses":{"200":{"description":"Merged — response includes a signed download_url","content":{"application/json":{"schema":{}}}},"400":{"description":"Fewer than 2 files, or a file that is not a readable .pptx"},"401":{"description":"Not authenticated"},"413":{"description":"A file exceeds 20 MB, or the set exceeds 100 MB"},"429":{"description":"Rate limit exceeded"},"503":{"description":"Merge failed or storage unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/feedback":{"post":{"tags":["feedback"],"summary":"Submit feedback","description":"Submit structured feedback — quality issues, bug reports, feature requests, testimonials, or general comments. Link to a specific job with job_id for slide-quality feedback.\n\n**Categories:** layout, typography, contrast, content, icons, images, theme, chrome, consistency, bug, feature_request, testimonial, general, other\n\n**Severities:** bug, quality, suggestion\n\nApproved feature requests earn $5 credit, testimonials earn $2 credit.","operationId":"submit_feedback_v1_feedback_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackSubmit"}}}},"responses":{"200":{"description":"Feedback saved","content":{"application/json":{"schema":{},"example":{"feedback_id":42,"status":"open"}}}},"400":{"description":"Invalid category or severity"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["feedback"],"summary":"List your feedback","description":"List your submitted feedback with optional filters. Shows status, the resolution written when it was acted on, award status and credits.","operationId":"list_feedback_v1_feedback_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/generate":{"post":{"tags":["generate"],"summary":"Removed — the Generate (creative codegen) engine is gone","description":"Gone (410). The Generate engine was removed per ADR 2026-06-12. Send a structured intent via the MCP create_slide tool or POST /v1/render/auto, or run your own python-pptx code via POST /v1/render (mode=code). Every render is $0.05.","operationId":"generate_slide_v1_generate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Generate engine removed"}}}},"/v1/iterate":{"post":{"tags":["generate"],"summary":"Removed — the iterate SKU is gone","description":"Gone (410). Iterating is re-sending the modified intent: every render is $0.05, and an identical input is free.","operationId":"iterate_slide_v1_iterate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Iterate removed"}}}},"/v1/themes":{"get":{"tags":["generate"],"summary":"Removed — the legacy theme layer is gone, use /v1/brands","description":"Gone (410). The legacy theme layer (`/v1/themes*`, `theme_json`) was replaced by brand kits per spec 37. List and manage kits via /v1/brands; `theme_id` on every render call keeps its name and accepts a built-in id or a brand slug (`slug@N` pins a version).","operationId":"themes_gone","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Legacy theme layer removed"}}},"put":{"tags":["generate"],"summary":"Removed — the legacy theme layer is gone, use /v1/brands","description":"Gone (410). The legacy theme layer (`/v1/themes*`, `theme_json`) was replaced by brand kits per spec 37. List and manage kits via /v1/brands; `theme_id` on every render call keeps its name and accepts a built-in id or a brand slug (`slug@N` pins a version).","operationId":"themes_gone","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Legacy theme layer removed"}}},"post":{"tags":["generate"],"summary":"Removed — the legacy theme layer is gone, use /v1/brands","description":"Gone (410). The legacy theme layer (`/v1/themes*`, `theme_json`) was replaced by brand kits per spec 37. List and manage kits via /v1/brands; `theme_id` on every render call keeps its name and accepts a built-in id or a brand slug (`slug@N` pins a version).","operationId":"themes_gone","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Legacy theme layer removed"}}},"delete":{"tags":["generate"],"summary":"Removed — the legacy theme layer is gone, use /v1/brands","description":"Gone (410). The legacy theme layer (`/v1/themes*`, `theme_json`) was replaced by brand kits per spec 37. List and manage kits via /v1/brands; `theme_id` on every render call keeps its name and accepts a built-in id or a brand slug (`slug@N` pins a version).","operationId":"themes_gone","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Legacy theme layer removed"}}},"patch":{"tags":["generate"],"summary":"Removed — the legacy theme layer is gone, use /v1/brands","description":"Gone (410). The legacy theme layer (`/v1/themes*`, `theme_json`) was replaced by brand kits per spec 37. List and manage kits via /v1/brands; `theme_id` on every render call keeps its name and accepts a built-in id or a brand slug (`slug@N` pins a version).","operationId":"themes_gone","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Legacy theme layer removed"}}}},"/v1/render":{"post":{"tags":["render"],"summary":"Removed — the template render engine is gone","description":"Gone (410). The template render engine was removed. Discover slide forms via the MCP browse_catalog tool (form/variant catalog; type=schema + family for the machine-readable contract), then render via POST /v1/render/auto with a brief or a structured intent via the MCP create_slide tool. Caller-supplied python-pptx code renders via POST /v1/render/code.","operationId":"render_slide_removed_v1_render_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Templates removed"}}}},"/v1/render/auto":{"post":{"tags":["render"],"summary":"Render a slide from a brief via the SSG intent engine ($0.05)","description":"Brief → SSG intent route → deterministic render. The brief path used by MCP `create_slide` when only a prose brief is supplied. $0.05 per slide. Unroutable briefs return a 422 `intent_unroutable` error with no charge. Explicit style asks in the brief (dark/light, corporate/technical/... looks) map onto built-in themes automatically unless `theme_id` is set; the response names the mapping and any unsupported styling.","operationId":"render_auto_endpoint_v1_render_auto_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoRenderRequest"}}},"required":true},"responses":{"200":{"description":"Render complete","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing or empty brief"},"402":{"description":"Insufficient balance"},"422":{"description":"Intent could not be routed — no charge"},"429":{"description":"Rate limit exceeded"}}}},"/v1/render/intent":{"post":{"tags":["render"],"summary":"Render a slide from a structured SSG intent (mirrors MCP create_slide)","description":"Deterministic SSG render from a structured intent — the REST mirror of the MCP create_slide tool. Provide ONE of: `form` (+ typed content fields) for a typed intent, `prior_id` (+`content`) to render an exact catalog prior (no routing), `author_ssg` for a full AuthorSSG document, or a prose `brief`. $0.05 per slide.\n\n`preview:true` → a FREE ephemeral PNG preview (no job, no charge, nothing downloadable). Eligible only for verbatim inputs (`prior_id`/`author_ssg`); briefs/images return 400 (use Generate).\n\nBrief-mode style asks (dark/light, look adjectives) auto-map onto built-in themes unless `theme_id` is set — pick one explicitly via GET /v1/brands or browse_catalog(type=themes).","operationId":"render_intent_endpoint_v1_render_intent_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentRenderRequest"}}},"required":true},"responses":{"200":{"description":"Render complete (or free PNG preview)","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing input, or preview not eligible (use Generate)"},"402":{"description":"Insufficient balance"},"422":{"description":"Intent could not be rendered — no charge"}}}},"/v1/render/intent/deck":{"post":{"tags":["render"],"summary":"Render a deck from N structured SSG intents (mirrors MCP create_deck)","description":"Deterministic SSG deck render — the REST mirror of the MCP create_deck tool. `slides` is a list of 1-30 intent objects (each the same shape as /v1/render/intent: form + typed fields, or a brief). Slides fill in parallel and merge into one themed PPTX. $0.05 per usable slide.","operationId":"render_intent_deck_endpoint_v1_render_intent_deck_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentDeckRequest"}}},"required":true},"responses":{"200":{"description":"Deck render complete","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing/invalid slides"},"402":{"description":"Insufficient balance"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/render/from-image":{"post":{"tags":["render"],"summary":"Ingest: image → editable slide(s) (alias of /v1/perception/proceed)","description":"Canonical ingest name for image → slide(s); thin delegate to the planner_v2 from-image pipeline.","operationId":"render_from_image_endpoint_v1_render_from_image_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProceedRequest"}}},"required":true},"responses":{"200":{"description":"Render complete","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing image or invalid choices"},"402":{"description":"Insufficient balance"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/render/recipe":{"post":{"tags":["render"],"summary":"Removed — the semantic recipe engine is gone","description":"Gone (410). The semantic recipe engine was removed in v5. Send a structured intent via the MCP create_slide tool, or use POST /v1/render/auto.","operationId":"render_recipe_endpoint_v1_render_recipe_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Recipe engine removed"}}}},"/v1/suggest-layout":{"post":{"tags":["render"],"summary":"Removed — layout suggestion is gone (ADR 2026-06-13)","description":"Gone (410). The brief→spec suggestion helper was retired with the spec endpoint. Use POST /v1/render/auto (brief) or POST /v1/render/intent (structured intent).","operationId":"suggest_layout_endpoint_v1_suggest_layout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"suggest-layout removed"}}}},"/v1/render/preflight":{"post":{"tags":["render"],"summary":"Removed — template preflight is gone","description":"Gone (410). The template render engine was removed. Discover slide forms via the MCP browse_catalog tool (form/variant catalog; type=schema + family for the machine-readable contract), then render via POST /v1/render/auto with a brief or a structured intent via the MCP create_slide tool. Caller-supplied python-pptx code renders via POST /v1/render/code.","operationId":"render_preflight_removed_v1_render_preflight_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Templates removed"}}}},"/v1/render/intent/deck/{deck_id}":{"patch":{"tags":["render"],"summary":"Edit slides of a rendered deck (free, no model)","operationId":"edit_intent_deck_v1_render_intent_deck__deck_id__patch","parameters":[{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeckEditRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/render/intent/{job_id}":{"patch":{"tags":["render"],"summary":"Edit a rendered slide (free, no model)","operationId":"edit_intent_slide_v1_render_intent__job_id__patch","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideEditRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analyze/image/perceive":{"post":{"tags":["analyze"],"summary":"Vision perception → SlideIntentIR (v0.1, prototype)","description":"Run full vision perception (4 parallel vision-model specialists) and\nreturn a SlideIntentIR structured spec. Phase 2 of the\nPerception → Planner v2 plan.\n\nBills `image_analysis_v2` ($0.20) on success only. The returned\n`SlideIntentIR` is the input the Planner v2 will consume (P4).\nToday, callers can also drive it through the legacy auto-router by\nextracting `brief = perception.brief()` (helper TBD).\n\nINTERNAL ENDPOINT — perception schema is IP-load-bearing and not\nadvertised in the public MCP surface. Keep on this REST path until\nwe decide otherwise.","operationId":"perceive_image_endpoint_v1_analyze_image_perceive_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing or invalid image"},"402":{"description":"Insufficient balance"},"500":{"description":"Vision perception failed"}}}},"/v1/analyze/image/preflight":{"post":{"tags":["analyze"],"summary":"Estimate cost+duration before running full image analysis","description":"Run a single cheap vision pass to estimate the cost+duration of the\nfull from-image flow. Free to the user; we eat ~$0.02/call internally.\n\nThe frontend uses this output to render a \"Will cost $X-Y, balance $Z.\nProceed?\" confirmation before committing to the expensive perception\npath. Auto-mode can skip the confirmation; customize-mode shows\nper-asset choices once we have asset-level data (P2).","operationId":"preflight_image_endpoint_v1_analyze_image_preflight_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing or invalid image"},"500":{"description":"Vision-model preflight failed"}}}},"/v1/analyze/image":{"post":{"tags":["analyze"],"summary":"Analyze Image Endpoint","description":"Analyze an uploaded image and return a synthesized slide brief.\n\nSync. Bills `image_analysis` ($0.05) on success only. Feed the returned\n`brief` into POST /v1/render/auto to produce a slide.\n\nFor multi-slide breakdowns (`slide_count > 1`) returns\n`AnalyzeImageMultiResponse` with one entry per slide; the agent then\ncalls /v1/render/auto once per slide and assembles a deck.","operationId":"analyze_image_endpoint_v1_analyze_image_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeImageRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing or invalid image"},"402":{"description":"Insufficient balance"},"500":{"description":"Vision-model analysis failed"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/perception/proceed":{"post":{"tags":["perception"],"summary":"Full from-image pipeline: perception → assets → planner v2 → render","description":"Drive the full from-image pipeline: preflight → option branch.\n\nreconstruct/explain render via the SSG ladder (ADR 2026-07-02);\npreserve/teach_deck keep their deterministic asset paths.","operationId":"perception_proceed_endpoint_v1_perception_proceed_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProceedRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Missing image or invalid choices"},"402":{"description":"Insufficient balance"},"500":{"description":"Pipeline failure"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/plan_slide":{"post":{"tags":["plan"],"summary":"Removed â€” the semantic recipe engine is gone","description":"Gone (410). The semantic recipe planner was removed in v5. Use POST /v1/render with a structured intent, or the MCP plan_slide tool.","operationId":"plan_slide_endpoint_v1_plan_slide_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"410":{"description":"Recipe engine removed"}}}},"/v1/plan_slide/shapes":{"get":{"tags":["plan"],"summary":"List all known shapes","description":"Reference endpoint â€” returns the 10 shapes from spec 00b.","operationId":"list_shapes_v1_plan_slide_shapes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/admin/candidate-components":{"get":{"tags":["admin"],"summary":"List candidate components awaiting triage","operationId":"list_candidates_v1_admin_candidate_components_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","description":"Filter: pending | promoted | rejected | duplicate","default":"pending","title":"Status"},"description":"Filter: pending | promoted | rejected | duplicate"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":25,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/candidate-components/{candidate_id}":{"get":{"tags":["admin"],"summary":"Get one candidate (full python_pptx_code + metadata)","operationId":"get_candidate_v1_admin_candidate_components__candidate_id__get","parameters":[{"name":"candidate_id","in":"path","required":true,"schema":{"type":"string","title":"Candidate Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/candidate-components/{candidate_id}/triage":{"post":{"tags":["admin"],"summary":"Triage a candidate (promote / reject / mark duplicate)","operationId":"triage_v1_admin_candidate_components__candidate_id__triage_post","parameters":[{"name":"candidate_id","in":"path","required":true,"schema":{"type":"string","title":"Candidate Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/translate":{"post":{"tags":["translate"],"summary":"Translate an uploaded PPTX file","description":"Upload a .pptx file and receive a translated version with all formatting preserved. Runs asynchronously — the response contains a job_id to poll via GET /v1/jobs/{job_id}.\n\n**Max file size:** 50 MB.  **Max slides:** 100.  **Macros rejected.**\n\n**Pricing:** $0.02 per slide, flat. The wallet is checked for every slide before any work starts (402 with a checkout link if it cannot cover the deck); slides whose translation still overflows at the minimum font size are not billed.\n\n**Fidelity-preserving:** text runs are translated in place via python-pptx, preserving fonts, colors, sizes, positions, charts, tables, and embedded images.","operationId":"translate_upload_v1_translate_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_translate_upload_v1_translate_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"202":{"description":"Translation queued"},"400":{"description":"Invalid file (size, format, macros, or structure)"},"402":{"description":"Insufficient balance"},"413":{"description":"File too large"},"422":{"description":"Validation error"},"429":{"description":"Rate limit exceeded"}}}},"/v1/translate/from-job":{"post":{"tags":["translate"],"summary":"Translate an existing job's PPTX (JSON body)","description":"JSON-body alternative to POST /v1/translate for the no-file case. Cleaner DX for agents that don't want to construct multipart just to say 'translate this existing job into German'.\n\nIdentical pricing and identical background pipeline as /v1/translate; the only difference is the request shape.","operationId":"translate_from_job_v1_translate_from_job_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslateFromJobRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"202":{"description":"Translation queued"},"400":{"description":"Invalid input"},"402":{"description":"Insufficient balance"},"404":{"description":"Source job not found"},"429":{"description":"Rate limit exceeded"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/inspect":{"post":{"tags":["inspect"],"summary":"Inspect any PPTX — read-only Deck Quality Report (free)","description":"Structural quality report for ANY deck — a prior SlideForge job, an https URL, or uploaded bytes. Deterministic checks (no LLM): clipped/overflowing text via real font metrics, tiny fonts, off-canvas objects, text collisions, WCAG contrast, image-only slides — with honest coverage for what inherited theme formatting hides. Free; ≤25 MB; first 50 slides.","operationId":"inspect_deck_route_v1_inspect_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InspectRequest"}}},"required":true},"responses":{"200":{"description":"The Deck Quality Report (status: clean | review | failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InspectResponse"}}}},"400":{"description":"Bad source (none/multiple provided, invalid base64, non-https URL)"},"413":{"description":"File exceeds the 25 MB inspect cap"},"422":{"description":"Unreadable file (corrupt, encrypted, or legacy .ppt)"},"429":{"description":"Rate limit exceeded"}}}},"/v1/repair":{"post":{"tags":["repair"],"summary":"Repair a PPTX deterministically — sizes, colors, invisible junk; never your words ($0.02/repaired slide)","description":"Applies deterministic fixes to inspect findings: shrink_to_fit (real font metrics, floor 9pt), raise_min_font (only when the box still fits), remove_off_canvas (fully-invisible parked shapes), fix_contrast (WCAG, hue-preserving). No characters are ever altered — the response carries a computed visible_text_untouched proof. Ambiguous defects are flagged, never guessed. $0.02 per repaired slide (a slide with ≥1 fix); dry_run plans the exact fixes for FREE; no_changes never bills. ≤25 MB; first 50 slides.","operationId":"repair_deck_route_v1_repair_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepairRequest"}}},"required":true},"responses":{"200":{"description":"complete (repaired job — download via GET /v1/jobs/{id}/pptx) | no_changes | planned (dry_run)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepairResponse"}}}},"400":{"description":"Bad source or unknown action"},"402":{"description":"Insufficient wallet balance"},"413":{"description":"File exceeds the 25 MB cap"},"422":{"description":"Unreadable file (corrupt, encrypted, or legacy .ppt)"},"429":{"description":"Rate limit exceeded"},"503":{"description":"Storage unavailable"}}}},"/v1/render/from-pdf/analyze":{"post":{"tags":["pdf"],"summary":"Ingest: inspect a PDF, no job, no charge","description":"Free preview endpoint — no charge, no persistence.\n\nLogs each call's producer + page count + source classification to\nslideforge.telemetry so we can see what real users upload and prioritize\nwhich producers to support next. Fire-and-forget — never fails the\nrequest.","operationId":"analyze_pdf_endpoint_v1_render_from_pdf_analyze_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_analyze_pdf_endpoint_v1_render_from_pdf_analyze_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PdfAnalysis"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deck/from-pdf/analyze":{"post":{"tags":["pdf"],"summary":"Inspect a PDF and propose an extraction strategy (no job, no charge)","description":"Upload a PDF and receive metadata, per-page stats, source classification, and an estimated cost. Use this to show users what they're about to spend before calling `/v1/deck/from-pdf`. No job is created and the file is not persisted — re-upload the same file to `/v1/deck/from-pdf` when ready.","operationId":"analyze_pdf_endpoint_v1_deck_from_pdf_analyze_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_analyze_pdf_endpoint_v1_deck_from_pdf_analyze_post"}}},"required":true},"responses":{"200":{"description":"Analysis returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PdfAnalysis"}}}},"400":{"description":"Invalid PDF (size, format, corrupted)"},"413":{"description":"File too large"},"429":{"description":"Rate limit exceeded"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/render/from-pdf":{"post":{"tags":["pdf"],"summary":"Ingest: PDF (PowerPoint export) → editable PPTX deck","description":"POST /v1/deck/from-pdf — convert a PowerPoint-exported PDF into an editable PPTX.","operationId":"deck_from_pdf_v1_render_from_pdf_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_deck_from_pdf_v1_render_from_pdf_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"202":{"description":"Extraction queued"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deck/from-pdf":{"post":{"tags":["pdf"],"summary":"Convert a PDF (PowerPoint export) into an editable PPTX deck","description":"Upload a PDF and receive an editable .pptx with the original text, layout, shapes, and images preserved.\n\nCurrently supports PDFs exported from slide tools (PowerPoint, Keynote, Google Slides, Canva, LaTeX Beamer, etc.). Text documents are not converted: to turn a document into slides, let the calling assistant read it and send the slides as intents.\n\n**Max file size:** 50 MB.","operationId":"deck_from_pdf_v1_deck_from_pdf_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_deck_from_pdf_v1_deck_from_pdf_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"202":{"description":"Extraction queued"},"400":{"description":"Invalid PDF (size, format, corrupted)"},"402":{"description":"Insufficient balance"},"413":{"description":"File too large"},"422":{"description":"Unsupported PDF source (not a PowerPoint export)"},"429":{"description":"Rate limit exceeded"}}}},"/v1/ui-upload/pptx":{"post":{"tags":["ui-upload"],"summary":"Upload Pptx For Translate","description":"Accept a PPTX from the MCP Apps iframe, kick off translation.\n\nToken is minted by `upload_asset(purpose=\"translate\", target_language=...)`.\nTarget language is carried in the token's signed payload — the iframe\ndoesn't need to pass it on the upload. Reuses the existing\n`translate_upload` worker (same endpoint `/v1/translate` serves web form).\n\nReturns `{job_id, poll_url}` once the job is created; iframe polls\n`/v1/jobs/{id}` for completion. No server-side chain (translate is\nthe final step).","operationId":"upload_pptx_for_translate_v1_ui_upload_pptx_post","parameters":[{"name":"X-Upload-Token","in":"header","required":true,"schema":{"type":"string","title":"X-Upload-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_pptx_for_translate_v1_ui_upload_pptx_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ui-upload/theme":{"post":{"tags":["ui-upload"],"summary":"Upload Theme Template","description":"Accept a brand template PPTX from the MCP Apps drop-zone, extract the theme.\n\nToken is minted by `upload_asset(purpose=\"theme\")` with no inline data. Unlike pdf/translate\nthere is NO job to poll — extraction is synchronous and the response carries `theme_id`; the\nresult card renders a success panel (U4 ADR §7.4: renders on this theme are NATIVE by default,\nbuilt on the uploaded file).","operationId":"upload_theme_template_v1_ui_upload_theme_post","parameters":[{"name":"X-Upload-Token","in":"header","required":true,"schema":{"type":"string","title":"X-Upload-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_theme_template_v1_ui_upload_theme_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ui-upload/pdf":{"post":{"tags":["ui-upload"],"summary":"Upload Pdf","description":"Accept a PDF from the MCP Apps iframe, kick off conversion.\n\nReturns `{job_id, status: \"queued\", poll_url}` immediately. The\niframe polls the signed poll_url against `/v1/jobs/{job_id}` for\ncompletion, then transitions the result card to the populated state.\n\nAuth: `X-Upload-Token` header (minted by `upload_asset(purpose=\"pdf\")`).\nNo Bearer token; the upload token carries the user identity.","operationId":"upload_pdf_v1_ui_upload_pdf_post","parameters":[{"name":"X-Upload-Token","in":"header","required":true,"schema":{"type":"string","title":"X-Upload-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_pdf_v1_ui_upload_pdf_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ui-upload/recreate":{"post":{"tags":["ui-upload"],"summary":"Upload Recreate","description":"Accept a slide image from the MCP Apps iframe, recreate it as an\neditable slide (ADR 2026-08-05 recreate). Returns `{job_id, poll_url}`\nimmediately; the pipeline runs as a background task and the iframe polls\nthe signed route — the pdf async shape.","operationId":"upload_recreate_v1_ui_upload_recreate_post","parameters":[{"name":"X-Upload-Token","in":"header","required":true,"schema":{"type":"string","title":"X-Upload-Token"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_recreate_v1_ui_upload_recreate_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ui-telemetry/{job_id}":{"post":{"tags":["ui-telemetry"],"summary":"Record Event","description":"Record a UI interaction event tied to a specific job.\n\nAuth by signed URL (`sig` + `exp`). No Bearer token required — the\nsandboxed iframe has no credentials; the signature is its grant.","operationId":"record_event_v1_ui_telemetry__job_id__post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","description":"HMAC signature","default":"","title":"Sig"},"description":"HMAC signature"},{"name":"exp","in":"query","required":false,"schema":{"type":"string","description":"Signature expiry (unix epoch)","default":"","title":"Exp"},"description":"Signature expiry (unix epoch)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ui-feedback/{job_id}":{"post":{"tags":["ui-feedback"],"summary":"Submit Widget Rating","description":"Record a star rating (+ optional comment) for a rendered job.","operationId":"submit_widget_rating_v1_ui_feedback__job_id__post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","description":"HMAC signature","default":"","title":"Sig"},"description":"HMAC signature"},{"name":"exp","in":"query","required":false,"schema":{"type":"string","description":"Signature expiry (unix epoch)","default":"","title":"Exp"},"description":"Signature expiry (unix epoch)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs":{"get":{"tags":["jobs"],"summary":"List your jobs","description":"List your recent generation jobs with optional status filter. Paginated via `limit` and `offset` parameters. Returns newest first.","operationId":"list_jobs_v1_jobs_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status: queued, generating, executing, rendering, complete, failed","title":"Status"},"description":"Filter by status: queued, generating, executing, rendering, complete, failed"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max jobs to return (default 10)","default":10,"title":"Limit"},"description":"Max jobs to return (default 10)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of jobs to skip","default":0,"title":"Offset"},"description":"Number of jobs to skip"},{"name":"detail","in":"query","required":false,"schema":{"type":"string","description":"'compact' (default: id, name, status, type, cost, date) or 'full' (adds brief, signed URLs, score)","default":"compact","title":"Detail"},"description":"'compact' (default: id, name, status, type, cost, date) or 'full' (adds brief, signed URLs, score)"},{"name":"include_children","in":"query","required":false,"schema":{"type":"boolean","description":"Include deck child slides (each carries parent_deck_id). Default false shows parents only.","default":false,"title":"Include Children"},"description":"Include deck child slides (each carries parent_deck_id). Default false shows parents only."}],"responses":{"200":{"description":"Paginated list of jobs","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}":{"get":{"tags":["jobs"],"summary":"Get job status","description":"Poll a generation job's status. Returns pptx_url and preview_url when complete. For deck jobs, includes per-slide progress. Includes `cost_charged` — the retail price you paid.","operationId":"get_job_status_v1_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"wait","in":"query","required":false,"schema":{"type":"integer","maximum":60,"minimum":0,"description":"Long-poll: hold the connection up to N seconds (max 60) waiting for terminal status. 0 = immediate response (default).","default":0,"title":"Wait"},"description":"Long-poll: hold the connection up to N seconds (max 60) waiting for terminal status. 0 = immediate response (default)."},{"name":"fields","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated field names to include (e.g. 'status,cost_charged'). Returns only those fields + job_id. Default: all fields.","title":"Fields"},"description":"Comma-separated field names to include (e.g. 'status,cost_charged'). Returns only those fields + job_id. Default: all fields."},{"name":"sig","in":"query","required":false,"schema":{"type":"string","description":"HMAC signature for signed-URL polling (used by MCP Apps iframe which has no Bearer token).","default":"","title":"Sig"},"description":"HMAC signature for signed-URL polling (used by MCP Apps iframe which has no Bearer token)."},{"name":"exp","in":"query","required":false,"schema":{"type":"string","description":"Signature expiry timestamp (unix epoch).","default":"","title":"Exp"},"description":"Signature expiry timestamp (unix epoch)."}],"responses":{"200":{"description":"Job status with results when complete","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStatus"}}}},"404":{"description":"Job not found or not owned by you"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["jobs"],"summary":"Delete a job","description":"Delete a job and its entire iteration chain (all versions). Also deletes associated blobs (PPTX, previews) from storage. This action is irreversible.","operationId":"delete_job_endpoint_v1_jobs__job_id__delete","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Job deleted successfully","content":{"application/json":{"schema":{}}}},"404":{"description":"Job not found or not owned by you"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/versions":{"get":{"tags":["jobs"],"summary":"Get all versions of a slide","description":"Returns the full iteration chain for a job — walks up to the root, then finds all descendants. Works from any job in the chain.","operationId":"get_job_versions_v1_jobs__job_id__versions_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/download-url":{"post":{"tags":["jobs"],"summary":"Mint a fresh short-lived, single-use download URL for an owned job","description":"Artifact-security (#73): exchange an owned job for a click-time download URL — short TTL, single-use, key-versioned (instantly revocable). Ownership-gated. Prefer this over holding a long-lived render-time link, so a leaked transcript/log leaks an inert pointer, not access.","operationId":"mint_job_download_url_v1_jobs__job_id__download_url_post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/DownloadUrlRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/pptx":{"get":{"tags":["jobs"],"summary":"Download PPTX for a completed job","description":"Stream the PowerPoint file for a completed job. This URL IS the PPTX file — give it directly to users as a download link.","operationId":"get_job_pptx_v1_jobs__job_id__pptx_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sig"}},{"name":"exp","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exp"}},{"name":"kv","in":"query","required":false,"schema":{"type":"string","default":"","title":"Kv"}},{"name":"n","in":"query","required":false,"schema":{"type":"string","default":"","title":"N"}}],"responses":{"200":{"description":"PPTX file","content":{"application/json":{"schema":{}},"application/vnd.openxmlformats-officedocument.presentationml.presentation":{}}},"404":{"description":"Job not found or no PPTX available"},"409":{"description":"Job not complete"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/preview":{"get":{"tags":["jobs"],"summary":"Download PNG preview for a completed job","description":"Stream the full-size slide preview PNG. Use /preview/md for 640px or /preview/sm for 256px thumbnails.","operationId":"get_job_preview_v1_jobs__job_id__preview_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sig"}},{"name":"exp","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exp"}},{"name":"kv","in":"query","required":false,"schema":{"type":"string","default":"","title":"Kv"}},{"name":"n","in":"query","required":false,"schema":{"type":"string","default":"","title":"N"}}],"responses":{"200":{"description":"PNG image","content":{"application/json":{"schema":{}},"image/png":{}}},"404":{"description":"Job not found or no preview"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/preview/{size}":{"get":{"tags":["jobs"],"summary":"Download sized preview (sm=256px, md=640px)","description":"Stream a sized preview PNG (sm/md) — via a signed link OR (Phase 2) an authenticated owner's token.","operationId":"get_job_preview_sized_v1_jobs__job_id__preview__size__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"size","in":"path","required":true,"schema":{"type":"string","title":"Size"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sig"}},{"name":"exp","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exp"}}],"responses":{"200":{"description":"PNG image","content":{"application/json":{"schema":{}},"image/png":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/contact_sheet":{"get":{"tags":["jobs"],"summary":"Download the whole-deck contact-sheet PNG","description":"Stream a single image tiling ALL slides of a deck (failed slides shown as placeholders). Deck jobs only. Use /contact_sheet/md or /sm for smaller.","operationId":"get_deck_contact_sheet_v1_jobs__job_id__contact_sheet_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sig"}},{"name":"exp","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exp"}},{"name":"kv","in":"query","required":false,"schema":{"type":"string","default":"","title":"Kv"}},{"name":"n","in":"query","required":false,"schema":{"type":"string","default":"","title":"N"}}],"responses":{"200":{"description":"PNG image","content":{"application/json":{"schema":{}},"image/png":{}}},"404":{"description":"Not a deck / no contact sheet"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/contact_sheet/{size}":{"get":{"tags":["jobs"],"summary":"Download sized deck contact sheet (sm/md)","operationId":"get_deck_contact_sheet_sized_v1_jobs__job_id__contact_sheet__size__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"size","in":"path","required":true,"schema":{"type":"string","title":"Size"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sig"}},{"name":"exp","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exp"}}],"responses":{"200":{"description":"PNG image","content":{"application/json":{"schema":{}},"image/png":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/slides/{slide_number}/preview":{"get":{"tags":["jobs"],"summary":"Download per-slide preview PNG","description":"Stream a 640px preview PNG for a specific slide (0-based index). Available for translate jobs that generated all-slide previews.","operationId":"get_slide_preview_v1_jobs__job_id__slides__slide_number__preview_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"slide_number","in":"path","required":true,"schema":{"type":"integer","title":"Slide Number"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sig"}},{"name":"exp","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exp"}}],"responses":{"200":{"description":"PNG image","content":{"application/json":{"schema":{}},"image/png":{}}},"404":{"description":"Slide preview not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}/pdf":{"get":{"tags":["jobs"],"summary":"Download PDF for a completed job","description":"On-demand PDF download. First request renders the PDF from the PPTX via PrismPPTX (~200ms/slide), caches it, and streams the file. Subsequent requests serve from cache. This URL IS the PDF file — give it directly to users as a download link.","operationId":"get_job_pdf_v1_jobs__job_id__pdf_get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string","default":"","title":"Sig"}},{"name":"exp","in":"query","required":false,"schema":{"type":"string","default":"","title":"Exp"}},{"name":"kv","in":"query","required":false,"schema":{"type":"string","default":"","title":"Kv"}},{"name":"n","in":"query","required":false,"schema":{"type":"string","default":"","title":"N"}}],"responses":{"200":{"description":"PDF file (application/pdf)","content":{"application/json":{"schema":{}},"application/pdf":{}}},"404":{"description":"Job not found"},"409":{"description":"Job not complete"},"503":{"description":"Storage not configured"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/languages":{"get":{"tags":["languages"],"summary":"List supported translation languages","description":"Returns the list of languages SlideForge can natively generate slides in. Pass any of the returned `code` values in the `language` field of `/v1/generate`, `/v1/render`, `/v1/deck`, or `/v1/iterate` to get a slide in that language. English (`en`) is the default and has no translation surcharge.","operationId":"get_languages_v1_languages_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/referral":{"get":{"tags":["referral"],"summary":"Get your referral info","description":"Get your referral code, shareable link, and referral stats. Auto-generates a code if you don't have one yet.","operationId":"get_referral_v1_referral_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/referral/code":{"put":{"tags":["referral"],"summary":"Set custom referral code","description":"Set a custom referral code. 3-20 characters, alphanumeric and hyphens only. Case-insensitive (stored uppercase).","operationId":"set_referral_code_v1_referral_code_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCodeRequest"}}},"required":true},"responses":{"200":{"description":"Code set successfully","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid code format"},"409":{"description":"Code already taken"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/recipes":{"get":{"tags":["recipes"],"summary":"List or semantically search composition recipes","description":"Returns the catalog of vetted composition patterns. Each recipe is a multi-component pattern (e.g. SplitView{Quadrant, List}) with audit-vetted ratios and a spec_json template ready to fill.\n\nDefault: discovery-mode metadata. Pass `?detail=full` for the complete entry including spec_template + example_params + anti_patterns. Pass `?brief=<text>` for hybrid (BM25 + vector + semantic) search against the slideforge-recipes-v1 index — returns ranked top-N with full recipe payloads.","operationId":"list_recipes_endpoint_v1_recipes_get","parameters":[{"name":"detail","in":"query","required":false,"schema":{"type":"string","description":"'compact' (default) or 'full'","default":"compact","title":"Detail"},"description":"'compact' (default) or 'full'"},{"name":"shape","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by 00b shape","title":"Shape"},"description":"Filter by 00b shape"},{"name":"component","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to recipes that use this component","title":"Component"},"description":"Filter to recipes that use this component"},{"name":"brief","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Natural language brief — runs hybrid semantic search","title":"Brief"},"description":"Natural language brief — runs hybrid semantic search"},{"name":"top","in":"query","required":false,"schema":{"type":"integer","description":"Result count for brief mode (max 20)","default":5,"title":"Top"},"description":"Result count for brief mode (max 20)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/recipes/{slug}":{"get":{"tags":["recipes"],"summary":"Get a single recipe with spec template + example","description":"Returns the complete recipe: metadata + spec_template (JSON spec to copy and fill) + example_params (concrete values demonstrating the pattern) + anti_patterns (when NOT to use it).","operationId":"get_recipe_endpoint_v1_recipes__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report-recipes":{"get":{"tags":["report-workflows"],"summary":"List built-in report workflow seeds","operationId":"list_report_recipes_v1_report_recipes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/report-workflows":{"get":{"tags":["report-workflows"],"summary":"List saved customer report workflows","operationId":"list_report_workflows_v1_report_workflows_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["report-workflows"],"summary":"Save a customer report workflow","operationId":"save_report_workflow_v1_report_workflows_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportWorkflowSaveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report-workflows/from-deck":{"post":{"tags":["report-workflows"],"summary":"Draft a report workflow from a create_deck payload","operationId":"create_report_workflow_from_deck_v1_report_workflows_from_deck_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportWorkflowFromDeckRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report-workflows/{workflow_id}":{"get":{"tags":["report-workflows"],"summary":"Get a saved customer report workflow","operationId":"get_report_workflow_v1_report_workflows__workflow_id__get","parameters":[{"name":"workflow_id","in":"path","required":true,"schema":{"type":"string","title":"Workflow Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report-workflows/{workflow_id}/run":{"post":{"tags":["report-workflows"],"summary":"Run a saved customer report workflow","operationId":"run_report_workflow_v1_report_workflows__workflow_id__run_post","parameters":[{"name":"workflow_id","in":"path","required":true,"schema":{"type":"string","title":"Workflow Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportWorkflowRunRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/catalog":{"get":{"tags":["catalog"],"summary":"Browse the slide-template catalog (patterns + segment showcase)","description":"The public gallery + agent catalog. Returns slide templates with segment/use-case/family facets. Filter with `?segment=`, `?use_case=`, `?family=`, `?layer=pattern|showcase`, and free-text `?q=`. No auth. Layer `pattern` = structural variants; `showcase` = authored segment-specific example slides.","operationId":"list_catalog_v1_catalog_get","parameters":[{"name":"segment","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Segment"}},{"name":"use_case","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case"}},{"name":"family","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Family"}},{"name":"layer","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"pattern | showcase","title":"Layer"},"description":"pattern | showcase"},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"free-text over title + blurb","title":"Q"},"description":"free-text over title + blurb"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/catalog/forms":{"get":{"tags":["catalog"],"summary":"The SSG form menu — families, when-to-use, bound fields, variants","description":"The structured-intent contract for agents. No `?family=` -> the form overview (each form: when-to-use, bound fields, variant count). `?family=` -> that form's variants (paginate with `?limit=` / `?offset=`). REST mirror of MCP `browse_catalog`. Free, no auth. Per-form JSON Schema: GET /v1/catalog/forms/{form}.","operationId":"list_forms_v1_catalog_forms_get","parameters":[{"name":"family","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Scope to one form/family","title":"Family"},"description":"Scope to one form/family"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Max variants (with family=)","title":"Limit"},"description":"Max variants (with family=)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Pagination offset (with family=)","default":0,"title":"Offset"},"description":"Pagination offset (with family=)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/catalog/forms/{form}":{"get":{"tags":["catalog"],"summary":"One form's machine-readable contract (JSON Schema + capacity + examples)","description":"The generated contract for a single form: JSON Schema for the `data` payload, capacity limits, intent-field mapping, variants, and worked examples. REST mirror of MCP `browse_catalog(type=schema, family=<form>)`. `variant=`/`prior_id=` pins a variant so the contract matches the slotmap the binder uses for it. 404 if the form is unknown.","operationId":"get_form_schema_v1_catalog_forms__form__get","parameters":[{"name":"form","in":"path","required":true,"schema":{"type":"string","title":"Form"}},{"name":"variant","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant"}},{"name":"prior_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prior Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/widgets":{"get":{"tags":["catalog","discovery"],"summary":"Code-mode widget + chart-primitive catalog","description":"The board-grade widgets and chart primitives drawable via `add_widget()` in `mode=code` (`/v1/render/code`). Grid by default (every name + a one-liner); `?name=<widget>` returns the full contract — docstring, param/content schema, and a worked example. REST mirror of MCP `browse_catalog(type=widgets)`. Free, no auth.","operationId":"list_widgets_v1_widgets_get","parameters":[{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A widget/primitive name for its full contract","title":"Name"},"description":"A widget/primitive name for its full contract"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/widgets/{name}/preview":{"get":{"tags":["catalog","discovery"],"summary":"Widget thumbnail (PNG) from blob storage","description":"Pre-rendered thumbnail of the widget/primitive drawn from its worked example on a neutral 16:9 slide (generated by scripts/ssg/publish_widget_thumbnails.py). Sizes: full (~1920px), sm (~480px). No auth.","operationId":"get_widget_preview_v1_widgets__name__preview_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"size","in":"query","required":false,"schema":{"type":"string","description":"full | sm","default":"full","title":"Size"},"description":"full | sm"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}},"image/png":{}}},"404":{"description":"Preview not available"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/helpers":{"get":{"tags":["catalog","discovery"],"summary":"python-pptx helpers available in mode=code","description":"Signatures + categories of the python-pptx helpers injected into the `mode=code` sandbox (`add_text_box`, `add_box`, `add_icon`, `add_widget`, …) plus the canvas notes. REST mirror of MCP `browse_catalog(type=helpers)`. Free, no auth.","operationId":"list_helpers_v1_helpers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/catalog/{slug}":{"get":{"tags":["catalog"],"summary":"Get one catalog entry (with its editable structured intent)","description":"Full record for a pattern or showcase slug, including the brief and the structured intent (`content`) that deterministically renders it.","operationId":"get_catalog_item_v1_catalog__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/catalog/{slug}/preview":{"get":{"tags":["catalog"],"summary":"Slide thumbnail (PNG) from blob storage","description":"Serves the pre-rendered thumbnail. Sizes: full (~1600px), sm (~480px). No auth.","operationId":"get_catalog_preview_v1_catalog__slug__preview_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"size","in":"query","required":false,"schema":{"type":"string","description":"full | sm","default":"full","title":"Size"},"description":"full | sm"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}},"image/png":{}}},"404":{"description":"Preview not available"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands":{"get":{"tags":["brands"],"summary":"List My Brands","operationId":"list_my_brands_v1_brands_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["brands"],"summary":"Create Brand From Tokens","operationId":"create_brand_from_tokens_v1_brands_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBrandRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands/upload":{"post":{"tags":["brands"],"summary":"Upload Brand Template","operationId":"upload_brand_template_v1_brands_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_brand_template_v1_brands_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands/from-url":{"post":{"tags":["brands"],"summary":"Create Brand From Url","operationId":"create_brand_from_url_v1_brands_from_url_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FromUrlRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands/{slug}/tokens.json":{"get":{"tags":["brands"],"summary":"Export Tokens Json","description":"The kit as its DTCG document — the open-play half of §2.3: the same file Figma, Tokens\nStudio and Style Dictionary read. Built-in kits are public product info (W5): fetch one,\ntweak it, send it back as the inline `brand` document on a render.","operationId":"export_tokens_json_v1_brands__slug__tokens_json_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands/{slug}/potx":{"get":{"tags":["brands"],"summary":"Export Brand Potx","operationId":"export_brand_potx_v1_brands__slug__potx_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands/{slug}/thmx":{"get":{"tags":["brands"],"summary":"Export Brand Thmx","operationId":"export_brand_thmx_v1_brands__slug__thmx_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands/{slug}":{"get":{"tags":["brands"],"summary":"Get Brand","operationId":"get_brand_v1_brands__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["brands"],"summary":"Delete My Brand","description":"Delete a kit you own — the entitlement remedy for the one-kit cap ('delete it first').","operationId":"delete_my_brand_v1_brands__slug__delete","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/brands/{slug}/versions":{"post":{"tags":["brands"],"summary":"Publish Version","operationId":"publish_version_v1_brands__slug__versions_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewVersionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/usage":{"get":{"tags":["usage"],"summary":"Usage & cost summary","description":"Aggregate usage statistics and cost breakdown for the authenticated user. Includes balance, total slides generated, spend by tier, daily breakdown, and a reconciliation block comparing wallet charges, job billed prices, and internal cost events. Use `?days=7` for the last week or `?days=90` for a quarter.","operationId":"get_usage_v1_usage_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Lookback period in days (default 30, max 365)","default":30,"title":"Days"},"description":"Lookback period in days (default 30, max 365)"}],"responses":{"200":{"description":"Usage summary","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/wallet":{"get":{"tags":["wallet"],"summary":"Get wallet balance","description":"Get your wallet balance and last 20 transactions.","operationId":"wallet_info_v1_wallet_get","responses":{"200":{"description":"Balance and transaction history","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"}}}},"/v1/me":{"get":{"tags":["wallet"],"summary":"Get authenticated user identity + wallet snapshot","description":"Returns the authenticated user's identity and current wallet balance in a single call. Useful for new integrators wanting a one-shot 'this key works AND maps to <email>' check before making any other call.","operationId":"get_me_v1_me_get","responses":{"200":{"description":"User identity + wallet","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"}}}},"/v1/account/deletion":{"get":{"tags":["account"],"summary":"Check for a pending erasure","description":"Returns the open erasure request on this account, if there is one.","operationId":"deletion_status_v1_account_deletion_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletionStatusResponse"}}}}}},"post":{"tags":["account"],"summary":"Request erasure of your account (GDPR Art. 17)","description":"Starts an account erasure. Nothing is deleted by this call: it sends a confirmation link to the account's email address, valid for 24 hours. Confirming schedules the erasure 24 hours out, and it can be cancelled at any point before then.","operationId":"request_deletion_v1_account_deletion_post","responses":{"200":{"description":"Confirmation email sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletionRequestResponse"}}}},"401":{"description":"Authentication required"},"409":{"description":"No email address on the account — contact support instead"}}},"delete":{"tags":["account"],"summary":"Cancel a pending erasure","description":"Withdraws an open erasure request. Only works before the erasure has run.","operationId":"cancel_deletion_v1_account_deletion_delete","responses":{"200":{"description":"Request cancelled","content":{"application/json":{"schema":{}}}},"404":{"description":"No open request on this account"}}}},"/v1/agent-onboarding":{"get":{"tags":["discovery"],"summary":"Agent-first onboarding playbook","description":"Structured playbook for an agent evaluating SlideForge. Free; no auth required, but balance is only shown with auth.","operationId":"agent_onboarding_v1_agent_onboarding_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/capabilities":{"get":{"tags":["discovery"],"summary":"Capability overview","description":"REST bootstrap for agents: render paths, discovery, costs, and constraints.","operationId":"capabilities_v1_capabilities_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/pricing":{"get":{"tags":["discovery"],"summary":"Machine-readable rate card","description":"Returns the canonical pricing table as structured JSON.","operationId":"pricing_v1_pricing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/render/code":{"post":{"tags":["sandbox"],"summary":"Execute python-pptx code in sandbox","description":"Run arbitrary python-pptx code in a secure sandbox and get a PPTX file + PNG preview. The code is a python-pptx script (the presentation it saves is the output), or a `build(prs)` function (`build(prs, slide)` when chrome is provided). All python-pptx helpers (add_text_box, add_box, add_icon, etc.) are available. Dangerous imports are blocked. 30-second timeout.\n\nUse this for custom layouts, novel visualizations, or any slide that needs full layout control. $0.05 per render. ~2 seconds.","operationId":"render_code_v1_render_code_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxRenderRequest"}}},"required":true},"responses":{"200":{"description":"Render complete with signed URLs","content":{"application/json":{"schema":{}}}},"400":{"description":"Code validation failed (syntax error, blocked import, script made no presentation)"},"408":{"description":"Execution timed out (30s limit)"},"402":{"description":"Insufficient balance"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks":{"get":{"tags":["webhooks"],"summary":"List webhook subscriptions","description":"List your active webhook subscriptions. Secret is masked (first 8 chars only).","operationId":"list_webhooks_v1_webhooks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["webhooks"],"summary":"Create webhook subscription","description":"Register a URL to receive event notifications. Returns the signing secret (shown only once).","operationId":"create_webhook_v1_webhooks_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreate"}}},"required":true},"responses":{"200":{"description":"Webhook created with signing secret","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid URL or event types"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}":{"get":{"tags":["webhooks"],"summary":"Get webhook details + recent deliveries","description":"Returns webhook subscription details and the last 20 delivery attempts.","operationId":"get_webhook_v1_webhooks__webhook_id__get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["webhooks"],"summary":"Delete webhook subscription","description":"Deactivate a webhook (soft-delete for audit trail).","operationId":"delete_webhook_v1_webhooks__webhook_id__delete","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Webhook deactivated","content":{"application/json":{"schema":{}}}},"404":{"description":"Not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}/test":{"post":{"tags":["webhooks"],"summary":"Send a test event","description":"Fires a synthetic job.completed event to verify your endpoint is reachable.","operationId":"test_webhook_v1_webhooks__webhook_id__test_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}/deliveries":{"get":{"tags":["webhooks"],"summary":"View delivery log","description":"Paginated delivery history for a webhook. For debugging and SOC2 audit evidence.","operationId":"list_deliveries_v1_webhooks__webhook_id__deliveries_get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workflows/consulting-deck":{"post":{"tags":["workflows"],"summary":"Generate a consulting-quality slide deck from a topic","description":"One call → finished consulting deck. Provide a topic and audience; the API generates a narrative outline, routes each slide through the intent engine, renders in parallel, and compiles into a single PPTX.\n\nInternally uses the same pipeline as POST /v1/deck (auto-brief mode) with a consulting-optimized prompt that structures each slide with a narrative role (hook, evidence, framework, recommendation, close).\n\n**Pricing:** $0.05 per slide, typically $0.30-$1.20 for a 6-slide deck.\n\n**Duration:** ~15-60s depending on slide count.","operationId":"consulting_deck_v1_workflows_consulting_deck_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsultingDeckRequest"}}},"required":true},"responses":{"202":{"description":"Deck generation started — poll GET /v1/jobs/{deck_id}","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid input"},"402":{"description":"Insufficient balance"},"503":{"description":"Service not configured"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workflows/":{"get":{"tags":["workflows"],"summary":"List registered workflows","description":"Catalogue of all available workflow archetypes (filterable by family).","operationId":"list_workflows_v1_workflows__get","parameters":[{"name":"family","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Family"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workflows/agile/sprint-retrospective":{"post":{"tags":["workflows"],"summary":"Generate a Sprint Retrospective deck (team-internal per-sprint reflection)","description":"Team-internal per-sprint reflection deck. 6 slides: Executive Dashboard, Project & Status Breakdown, Sprint Trends, Epic Progress, Development Summary, Highlights & Risks.\n\n**Audience:** Scrum Master, team, PO.\n**Cadence:** end of every sprint.\n**Scope:** project (with project_id) or workspace-wide (project_id omitted).\n\n**Modes:** `connection_id` (we fetch from Zoho), `prepared_data` (you give us a SprintMetrics-shaped dict). Pricing $0.18 per report (6 × $0.03).","operationId":"agile_sprint_retrospective_v1_workflows_agile_sprint_retrospective_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgileWorkflowRequest"}}},"required":true},"responses":{"202":{"description":"Report queued — poll GET /v1/jobs/{deck_id}","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid input"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workflows/agile/executive-snapshot":{"post":{"tags":["workflows"],"summary":"Generate an Executive Snapshot (1-2 slide leadership-facing RAG report)","description":"Smallest agile archetype — designed to read in <30 seconds.\n\nSlide 1: Big RAG status banner (ON TRACK / AT RISK / OFF TRACK) + 4 KPI tiles (completion, velocity, cycle time, defect rate) + key updates list.\nSlide 2 (optional): Velocity & throughput trend chart + 3-5 explanatory bullets.\n\n**Audience:** executives, sponsors, steering committees.\n**Cadence:** weekly or per-sprint.\n**Scope:** project (with project_id) or workspace-wide.\n\n**Modes:** `connection_id` (we fetch from Zoho) or `prepared_data` (SprintMetrics-shaped dict). Pricing $0.06 per report (2 × $0.03).","operationId":"agile_executive_snapshot_v1_workflows_agile_executive_snapshot_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgileWorkflowRequest"}}},"required":true},"responses":{"202":{"description":"Report queued — poll GET /v1/jobs/{deck_id}","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid input"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workflows/agile/sprint-review":{"post":{"tags":["workflows"],"summary":"Generate a Sprint Review deck (outward-facing per-sprint communication)","description":"Outward-facing per-sprint deck for stakeholders. 6 slides:\n1. Executive Dashboard (6 KPIs)\n2. Sprint Burndown (ideal-line dashed + actual line — research convention)\n3. Velocity History (last N sprints + 3-sprint rolling average overlay)\n4. Completed Work (closed items, epic-grouped)\n5. Rollover (items still in flight at sprint end)\n6. Highlights & Risks (auto-detected signals)\n\n**Audience:** PO, team, stakeholders.\n**Cadence:** end of every sprint.\n**Scope:** project (with project_id) or workspace-wide.\n\n**Modes:** `connection_id` or `prepared_data`. Pricing $0.18 (6 × $0.03).","operationId":"agile_sprint_review_v1_workflows_agile_sprint_review_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgileWorkflowRequest"}}},"required":true},"responses":{"202":{"description":"Report queued — poll GET /v1/jobs/{deck_id}","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid input"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workflows/agile/pi-progress":{"post":{"tags":["workflows"],"summary":"Generate a PI Progress deck (SAFe-grade quarterly progress report)","description":"SAFe Program Increment / quarterly progress. 4 slides:\n1. Executive Dashboard (6 KPIs)\n2. PI Predictability (achievement % per sprint, 80-100% target band)\n3. Feature Burnup (per-epic progress)\n4. Highlights & Risks\n\n**Audience:** RTE, Product Mgmt, executives.\n**Cadence:** per PI (8-12 weeks) or quarterly.\n\n**Deferred** until source data exists: PI Objectives (BV scoring), Program Board (team-per-feature), Next-PI Preview (PI plan).","operationId":"agile_pi_progress_v1_workflows_agile_pi_progress_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgileWorkflowRequest"}}},"required":true},"responses":{"202":{"description":"Report queued","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid input"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/workflows/agile/portfolio-health":{"post":{"tags":["workflows"],"summary":"Generate a Portfolio Health deck (cross-portfolio executive view)","description":"Cross-portfolio executive view. 4 slides:\n1. Executive Dashboard (workspace KPIs)\n2. Project RAG Grid (per-project independent health status)\n3. Portfolio Epic Burnup (all epics across all projects)\n4. Highlights & Risks\n\n**Audience:** portfolio mgmt, executives, steering committees.\n**Cadence:** monthly or quarterly.\n**Scope:** workspace-only (single-project portfolio view doesn't apply — project_id is silently ignored if provided).\n\n**Deferred** until source data exists: Portfolio Kanban with WIP limits, Capacity Allocation by value stream.","operationId":"agile_portfolio_health_v1_workflows_agile_portfolio_health_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgileWorkflowRequest"}}},"required":true},"responses":{"202":{"description":"Report queued","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid input"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/oauth/{vendor_id}/authorize":{"post":{"tags":["oauth-connect"],"summary":"Start OAuth authorization for an integration","description":"Returns an authorize URL. The frontend opens it in a popup; the user approves on the vendor's site; the vendor redirects back to /callback which stores the connection and messages the opener window.","operationId":"authorize_v1_oauth__vendor_id__authorize_post","parameters":[{"name":"vendor_id","in":"path","required":true,"schema":{"type":"string","title":"Vendor Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations":{"get":{"tags":["connections"],"summary":"List available vendors and integrations","description":"List vendors with their integrations. No auth required — this is a catalog.","operationId":"list_integrations_v1_integrations_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/connections":{"get":{"tags":["connections"],"summary":"List my connections","operationId":"list_connections_v1_connections_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/connections/{connection_id}":{"get":{"tags":["connections"],"summary":"Get connection details","operationId":"get_connection_v1_connections__connection_id__get","parameters":[{"name":"connection_id","in":"path","required":true,"schema":{"type":"string","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["connections"],"summary":"Update connection settings","operationId":"update_connection_v1_connections__connection_id__patch","parameters":[{"name":"connection_id","in":"path","required":true,"schema":{"type":"string","title":"Connection Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["connections"],"summary":"Revoke a connection","operationId":"revoke_connection_v1_connections__connection_id__delete","parameters":[{"name":"connection_id","in":"path","required":true,"schema":{"type":"string","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/connections/{connection_id}/test":{"post":{"tags":["connections"],"summary":"Verify a connection works","description":"Lightweight validation — refreshes token if needed and calls the connector's\ncheapest endpoint (e.g. list projects).","operationId":"test_connection_v1_connections__connection_id__test_post","parameters":[{"name":"connection_id","in":"path","required":true,"schema":{"type":"string","title":"Connection Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/events":{"post":{"tags":["events"],"summary":"Track a user event","description":"Fire-and-forget event tracking for frontend analytics. Returns 202 immediately. No billing charge.","operationId":"track_event_v1_events_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventRequest"}}},"required":true},"responses":{"202":{"description":"Event accepted","content":{"application/json":{"schema":{}}}},"400":{"description":"Unknown event type"},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/attribution":{"post":{"tags":["events"],"summary":"Record where a web signup came from (first touch only)","description":"Writes the landing path, referrer host and UTM values captured in the browser on the visitor's first page view. First-touch and immutable: it fills `users.acquisition_meta` only while that column is still NULL, so a later visit can never rewrite where someone originally came from. No billing charge.","operationId":"record_attribution_v1_attribution_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttributionRequest"}}},"required":true},"responses":{"202":{"description":"Accepted (written, or ignored because attribution already exists)","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/openapi-agent.json":{"get":{"tags":["discovery"],"summary":"Agent-facing OpenAPI projection","description":"Curated subset of the API for GPT Actions, custom connectors and other OpenAPI-driven agents: the 7-tool MCP surface plus the job lifecycle, with operationIds written to be read as function names. Derived from the live spec, so it cannot drift from what the server actually serves.","operationId":"openapi_agent_openapi_agent_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/live":{"get":{"tags":["health"],"summary":"Liveness probe","operationId":"health_live_health_live_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/ready":{"get":{"tags":["health"],"summary":"Readiness probe","operationId":"health_ready_health_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AgileWorkflowRequest":{"properties":{"connection_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connection Id","description":"OAuth connection (from /v1/oauth/{vendor}/authorize flow). Workspace-wide aggregation when project_id is omitted."},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Project ID (single-project mode); omit for workspace-wide aggregation."},"sprint_history":{"type":"integer","maximum":8.0,"minimum":0.0,"title":"Sprint History","description":"Number of past sprints for trending","default":4},"staging_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Staging Data","description":"Caller-supplied canonical entities (SprintData, etc). NOT YET implemented."},"prepared_data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Prepared Data","description":"Pre-computed SprintMetrics-shaped dict — render only. Keys match SprintMetrics dataclass fields."},"theme_id":{"type":"string","title":"Theme Id","default":"slideforge_standard"},"options":{"type":"object","title":"Options","description":"Workflow-specific options, e.g. {exclude_slides: [...]}"}},"type":"object","title":"AgileWorkflowRequest","description":"Shared request body for /v1/workflows/agile/* endpoints.\n\nThree input modes (mutually exclusive):\n  Mode A (stored OAuth): connection_id [+ project_id for single-project, omit for workspace-wide]\n  Mode B-staging: staging_data dict — NOT YET IMPLEMENTED in Phase A\n  Mode B-prepared: prepared_data dict (a SprintMetrics-shaped dict) → render only"},"AnalyzeImageRequest":{"properties":{"image":{"type":"string","title":"Image"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instructions"},"slide_count":{"type":"integer","title":"Slide Count","default":1}},"type":"object","required":["image"],"title":"AnalyzeImageRequest","description":"POST /v1/analyze/image request body — vision LLM analyzes an image and\nsynthesizes a slide brief.\n\nSync. Bills against the wallet on success ($0.05 per call). Feed the\nreturned `brief` to POST /v1/render/auto (or any other endpoint that\ntakes a brief). Decoupled from rendering so agents can analyze without\ncommitting to a render — and so the from-image UX is two visible stages\n(Analyzing → Rendering) instead of one opaque \"generate\" call."},"AttributionRequest":{"properties":{"landing_path":{"type":"string","maxLength":200,"title":"Landing Path"},"referrer_host":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Referrer Host"},"utm_source":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Utm Source"},"utm_medium":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Utm Medium"},"utm_campaign":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Utm Campaign"},"utm_term":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Utm Term"},"utm_content":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Utm Content"},"gclid":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Gclid"}},"type":"object","required":["landing_path"],"title":"AttributionRequest","description":"Where a web signup came from, captured in the browser on first touch.\n\nDeliberately not a URL and not a query string: `landing_path` is the path only and\n`referrer_host` the hostname only, so nothing a search engine or a user put in a query\ncan arrive here. UTM values are the five standard ones, length-capped."},"AuthorizeRequest":{"properties":{"integration_id":{"type":"string","title":"Integration Id","description":"Integration being connected (e.g. 'zoho_sprints')"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region","description":"Provider region (e.g. 'in', 'com', 'eu')"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"User-chosen label for this connection"},"return_to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To","description":"Frontend URL to redirect to after success"},"extra_scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Extra Scopes","description":"Additional scopes to request (for scope-upgrade flows)"}},"type":"object","required":["integration_id"],"title":"AuthorizeRequest"},"AutoRenderRequest":{"properties":{"brief":{"type":"string","title":"Brief"},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"expand_brief":{"type":"boolean","title":"Expand Brief","default":false},"data_hint":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Data Hint"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"chrome":{"anyOf":[{"$ref":"#/components/schemas/Chrome"},{"type":"null"}]},"include_preview":{"type":"boolean","title":"Include Preview","default":true},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"allow_fabrication":{"type":"boolean","title":"Allow Fabrication","default":false},"allow_low_confidence":{"type":"boolean","title":"Allow Low Confidence","default":false},"allow_truncation":{"type":"boolean","title":"Allow Truncation","default":false},"dry_run":{"type":"boolean","title":"Dry Run","default":false},"styling":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Styling"}},"type":"object","required":["brief"],"title":"AutoRenderRequest","description":"POST /v1/render/auto request body — brief → SSG intent render.\n\nThe brief path: SSG intent route → deterministic render. $0.05 per slide;\nunroutable briefs return 422 with no charge."},"AutoTopupSettings":{"properties":{"enabled":{"type":"boolean","title":"Enabled","default":true},"threshold":{"type":"number","title":"Threshold"},"amount":{"type":"number","title":"Amount"},"payment_method_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Method Id"}},"type":"object","required":["threshold","amount"],"title":"AutoTopupSettings"},"BillingProfile":{"properties":{"legal_name":{"type":"string","title":"Legal Name"},"address_line1":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line1"},"address_line2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line2"},"postcode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postcode"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"country":{"type":"string","title":"Country"},"tax_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tax Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["legal_name","country"],"title":"BillingProfile"},"Body_analyze_pdf_endpoint_v1_deck_from_pdf_analyze_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pdf file to analyze"}},"type":"object","required":["file"],"title":"Body_analyze_pdf_endpoint_v1_deck_from_pdf_analyze_post"},"Body_analyze_pdf_endpoint_v1_render_from_pdf_analyze_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pdf file to analyze"}},"type":"object","required":["file"],"title":"Body_analyze_pdf_endpoint_v1_render_from_pdf_analyze_post"},"Body_deck_combine_v1_deck_combine_post":{"properties":{"files":{"items":{"type":"string","format":"binary"},"type":"array","title":"Files","description":".pptx files, in merge order"}},"type":"object","required":["files"],"title":"Body_deck_combine_v1_deck_combine_post"},"Body_deck_from_pdf_v1_deck_from_pdf_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pdf file to convert"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Human-readable job name"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Entry tag persisted on jobs.source (attribution)"}},"type":"object","required":["file"],"title":"Body_deck_from_pdf_v1_deck_from_pdf_post"},"Body_deck_from_pdf_v1_render_from_pdf_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pdf file to convert"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Human-readable job name"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"Entry tag persisted on jobs.source (attribution)"}},"type":"object","required":["file"],"title":"Body_deck_from_pdf_v1_render_from_pdf_post"},"Body_free_combine_powerpoint_v1_tools_combine_powerpoint_post":{"properties":{"files":{"items":{"type":"string","format":"binary"},"type":"array","title":"Files","description":"Two or more .pptx files, in merge order"}},"type":"object","required":["files"],"title":"Body_free_combine_powerpoint_v1_tools_combine_powerpoint_post"},"Body_free_pdf_to_pptx_v1_tools_pdf_to_pptx_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pdf file to convert"}},"type":"object","required":["file"],"title":"Body_free_pdf_to_pptx_v1_tools_pdf_to_pptx_post"},"Body_public_pdf_analyze_v1_tools_pdf_analyze_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pdf file to analyse"}},"type":"object","required":["file"],"title":"Body_public_pdf_analyze_v1_tools_pdf_analyze_post"},"Body_translate_upload_v1_translate_post":{"properties":{"file":{"anyOf":[{"type":"string","format":"binary"},{"type":"null"}],"title":"File","description":"The .pptx file to translate (omit when source_job_id is provided)"},"source_job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Job Id","description":"ID of an existing slideforge job whose pptx_url should be translated (alternative to uploading a file). Useful for translating decks just produced by /v1/generate, /v1/deck, or /v1/deck/from-pdf without downloading and re-uploading the .pptx."},"target_language":{"type":"string","title":"Target Language","description":"ISO 639-1 target language code"},"source_language":{"type":"string","title":"Source Language","description":"ISO 639-1 source language or 'auto' to detect","default":"auto"},"mode":{"type":"string","title":"Mode","description":"Translation model tier, 'draft' or 'pro'. No price effect.","default":"draft"},"include_notes":{"type":"boolean","title":"Include Notes","description":"Translate speaker notes","default":false},"include_tables":{"type":"boolean","title":"Include Tables","description":"Translate table cells","default":true},"concise_mode":{"type":"boolean","title":"Concise Mode","description":"Hint LLM to prefer shorter translations","default":false},"delete_after_download":{"type":"boolean","title":"Delete After Download","description":"Auto-delete source + output on first download","default":false},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Human-readable job name"},"dry_run":{"type":"boolean","title":"Dry Run","description":"Return cost + duration estimate without creating a job.","default":false}},"type":"object","required":["target_language"],"title":"Body_translate_upload_v1_translate_post"},"Body_upload_brand_template_v1_brands_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"slug":{"type":"string","title":"Slug"},"name":{"type":"string","title":"Name"}},"type":"object","required":["file"],"title":"Body_upload_brand_template_v1_brands_upload_post"},"Body_upload_image_endpoint_v1_assets_image_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_image_endpoint_v1_assets_image_post"},"Body_upload_logo_endpoint_v1_assets_logo_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_logo_endpoint_v1_assets_logo_post"},"Body_upload_pdf_v1_ui_upload_pdf_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pdf file to convert"}},"type":"object","required":["file"],"title":"Body_upload_pdf_v1_ui_upload_pdf_post"},"Body_upload_pptx_for_translate_v1_ui_upload_pptx_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The .pptx file to translate"}},"type":"object","required":["file"],"title":"Body_upload_pptx_for_translate_v1_ui_upload_pptx_post"},"Body_upload_recreate_v1_ui_upload_recreate_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"Slide image (PNG/JPEG/WebP) to recreate"}},"type":"object","required":["file"],"title":"Body_upload_recreate_v1_ui_upload_recreate_post"},"Body_upload_theme_template_v1_ui_upload_theme_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"The company .pptx template"}},"type":"object","required":["file"],"title":"Body_upload_theme_template_v1_ui_upload_theme_post"},"CatalogDetail":{"properties":{"slug":{"type":"string","title":"Slug"},"layer":{"type":"string","title":"Layer"},"title":{"type":"string","title":"Title"},"family":{"type":"string","title":"Family"},"family_label":{"type":"string","title":"Family Label"},"variant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant"},"variant_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant Label"},"segments":{"items":{"type":"string"},"type":"array","title":"Segments"},"use_cases":{"items":{"type":"string"},"type":"array","title":"Use Cases"},"blurb":{"type":"string","title":"Blurb"},"preview_url":{"type":"string","title":"Preview Url"},"render_ready":{"type":"boolean","title":"Render Ready","default":true},"brief":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief"},"intent":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Intent"},"content":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Content"}},"type":"object","required":["slug","layer","title","family","family_label","segments","use_cases","blurb","preview_url"],"title":"CatalogDetail"},"CatalogItem":{"properties":{"slug":{"type":"string","title":"Slug"},"layer":{"type":"string","title":"Layer"},"title":{"type":"string","title":"Title"},"family":{"type":"string","title":"Family"},"family_label":{"type":"string","title":"Family Label"},"variant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant"},"variant_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant Label"},"segments":{"items":{"type":"string"},"type":"array","title":"Segments"},"use_cases":{"items":{"type":"string"},"type":"array","title":"Use Cases"},"blurb":{"type":"string","title":"Blurb"},"preview_url":{"type":"string","title":"Preview Url"},"render_ready":{"type":"boolean","title":"Render Ready","default":true}},"type":"object","required":["slug","layer","title","family","family_label","segments","use_cases","blurb","preview_url"],"title":"CatalogItem"},"CatalogResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CatalogItem"},"type":"array","title":"Items"},"count":{"type":"integer","title":"Count"},"segments":{"items":{"$ref":"#/components/schemas/Facet"},"type":"array","title":"Segments"},"use_cases":{"items":{"$ref":"#/components/schemas/Facet"},"type":"array","title":"Use Cases"},"families":{"items":{"$ref":"#/components/schemas/Facet"},"type":"array","title":"Families"}},"type":"object","required":["items","count","segments","use_cases","families"],"title":"CatalogResponse"},"CheckoutRequest":{"properties":{"amount":{"type":"number","title":"Amount"}},"type":"object","required":["amount"],"title":"CheckoutRequest"},"Chrome":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"subtitle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtitle"},"kicker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kicker"},"footer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Footer"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"page_number":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page Number"},"classification":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Classification"}},"type":"object","title":"Chrome","description":"Slide chrome — the frame rendered by render_chrome().\n\nAll fields optional. Resolution order:\n1. Explicit chrome on request (highest priority)\n2. LLM-extracted chrome from brief\n3. Deck-level chrome defaults\n4. Theme chrome defaults\n5. Not rendered (if still None)"},"CodePreviewRequest":{"properties":{"code":{"type":"string","maxLength":16384,"minLength":1,"title":"Code"},"theme_id":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Theme Id"}},"type":"object","required":["code"],"title":"CodePreviewRequest","description":"python-pptx source to preview. Code only — no chrome, no job refs, no uploads."},"CompileRequest":{"properties":{"job_ids":{"items":{"type":"string"},"type":"array","title":"Job Ids"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"slide_numbers":{"type":"boolean","title":"Slide Numbers","default":true}},"type":"object","required":["job_ids"],"title":"CompileRequest","description":"POST /v1/compile request body."},"ConnectionUpdate":{"properties":{"workspace_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Id","description":"Vendor workspace/team/org ID (shorthand for config.workspace_id)"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Human-friendly label"},"config":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Config","description":"Vendor-specific configuration (merged into existing config)"}},"type":"object","title":"ConnectionUpdate"},"ConsultingDeckRequest":{"properties":{"topic":{"type":"string","title":"Topic"},"audience":{"type":"string","title":"Audience","default":"executive leadership"},"slide_count":{"type":"integer","title":"Slide Count","default":6},"context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context"},"tone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tone"},"theme_id":{"type":"string","title":"Theme Id","default":"slideforge_standard"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"include_pdf":{"type":"boolean","title":"Include Pdf","default":false}},"type":"object","required":["topic"],"title":"ConsultingDeckRequest","description":"POST /v1/workflows/consulting-deck request body."},"CreateBrandRequest":{"properties":{"slug":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$","title":"Slug","description":"The wire theme_id. Lowercase; unique within your account."},"name":{"type":"string","maxLength":256,"minLength":1,"title":"Name"},"document":{"type":"object","title":"Document","description":"A W3C Design Tokens (DTCG 2025.10) document; Tokens-Studio dialect accepted."}},"type":"object","required":["slug","name","document"],"title":"CreateBrandRequest"},"CreateKeyRequest":{"properties":{"name":{"type":"string","title":"Name","default":"Default"}},"type":"object","title":"CreateKeyRequest"},"DeckEditRequest":{"properties":{"slides":{"items":{"$ref":"#/components/schemas/DeckSlideEdit"},"type":"array","minItems":1,"title":"Slides"}},"type":"object","required":["slides"],"title":"DeckEditRequest"},"DeckForkRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"}},"type":"object","title":"DeckForkRequest","description":"POST /v1/deck/{id}/fork request body — all fields optional."},"DeckFromJobsRequest":{"properties":{"job_ids":{"items":{"type":"string"},"type":"array","title":"Job Ids"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id"},"slide_numbers":{"type":"boolean","title":"Slide Numbers","default":true}},"type":"object","required":["job_ids"],"title":"DeckFromJobsRequest","description":"POST /v1/deck/from-jobs request body.\n\nLike /v1/compile, but produces a real `job_type=deck` row with child slides\nlinked via parent_job_id, so PATCH /v1/deck/{id} works on the result.\nNo LLM, no generation — just merge + chrome."},"DeckQueued":{"properties":{"deck_id":{"type":"string","title":"Deck Id"},"status":{"type":"string","title":"Status","default":"queued"},"slide_count":{"type":"integer","title":"Slide Count"}},"type":"object","required":["deck_id","slide_count"],"title":"DeckQueued","description":"202 response for deck creation."},"DeckRequest":{"properties":{"slides":{"items":{"$ref":"#/components/schemas/SlideSpec"},"type":"array","title":"Slides","default":[]},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt"},"slide_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Slide Count"},"audience":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audience"},"focus_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Focus Area"},"visual_style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visual Style"},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode"},"enrich":{"type":"boolean","title":"Enrich","default":false},"chrome":{"anyOf":[{"$ref":"#/components/schemas/Chrome"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"max_parallel":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Parallel"}},"type":"object","title":"DeckRequest","description":"POST /v1/deck request body."},"DeckSlideEdit":{"properties":{"slide":{"type":"integer","minimum":0.0,"title":"Slide","description":"0-based index, as slides[].i in the deck response"},"patch":{"type":"object","title":"Patch","description":"the slide fields to change (same fields as create_slide). A field left out keeps its value, a field sent as null is removed, a list replaces the whole list, `data` merges key by key. Send only `notes` to add or replace speaker notes without a re-render."}},"type":"object","required":["slide","patch"],"title":"DeckSlideEdit"},"DeckUpdateRequest":{"properties":{"replace":{"items":{"$ref":"#/components/schemas/SlideReplacement"},"type":"array","title":"Replace"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"slide_numbers":{"type":"boolean","title":"Slide Numbers","default":true}},"type":"object","required":["replace"],"title":"DeckUpdateRequest","description":"PATCH /v1/deck/{deck_id} request body."},"DeletionRequestResponse":{"properties":{"request_id":{"type":"string","title":"Request Id","description":"Reference for this request; quoted in every email about it"},"state":{"type":"string","title":"State","description":"pending_confirmation until the emailed link is clicked"},"message":{"type":"string","title":"Message","description":"What the requester must do next"},"email_sent_to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Sent To","description":"Where the confirmation was sent"}},"type":"object","required":["request_id","state","message"],"title":"DeletionRequestResponse","description":"What the caller gets back when an erasure is requested."},"DeletionStatusResponse":{"properties":{"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id"},"scheduled_for":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled For"}},"type":"object","title":"DeletionStatusResponse","description":"The open erasure request on this account, if any."},"DownloadUrlRequest":{"properties":{"artifact":{"type":"string","title":"Artifact","default":"pptx"}},"type":"object","title":"DownloadUrlRequest"},"EventRequest":{"properties":{"event":{"type":"string","title":"Event"},"metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metadata"},"resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Id"}},"type":"object","required":["event"],"title":"EventRequest"},"Facet":{"properties":{"id":{"type":"string","title":"Id"},"label":{"type":"string","title":"Label"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["id","label","count"],"title":"Facet"},"FeedbackSubmit":{"properties":{"subject":{"type":"string","maxLength":200,"minLength":1,"title":"Subject","description":"Short title"},"category":{"type":"string","title":"Category","description":"Feedback category"},"severity":{"type":"string","title":"Severity","description":"bug | quality | suggestion"},"description":{"type":"string","minLength":1,"title":"Description","description":"Detailed feedback"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Link to specific job (recommended for bugs/quality)"},"slide_number":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Slide Number","description":"Slide number in deck"},"metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metadata","description":"Extra context (JSON)"}},"type":"object","required":["subject","category","severity","description"],"title":"FeedbackSubmit","description":"POST /v1/feedback body."},"FromUrlRequest":{"properties":{"url":{"type":"string","maxLength":512,"minLength":3,"title":"Url","description":"The brand's website — a domain or any URL on it."},"slug":{"anyOf":[{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{1,63}$"},{"type":"null"}],"title":"Slug"},"name":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Name"}},"type":"object","required":["url"],"title":"FromUrlRequest"},"GenerateSpec":{"properties":{"brief":{"type":"string","title":"Brief"},"chrome":{"anyOf":[{"$ref":"#/components/schemas/Chrome"},{"type":"null"}]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["brief"],"title":"GenerateSpec","description":"Per-slide brief config within a deck (new explicit format)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ImageGenerateRequest":{"properties":{"prompt":{"type":"string","title":"Prompt"},"size":{"type":"string","title":"Size","default":"1024x1024"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"quality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quality"}},"type":"object","required":["prompt"],"title":"ImageGenerateRequest"},"InspectRequest":{"properties":{"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"A prior SlideForge slide/deck job to inspect"},"pptx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pptx Url","description":"HTTPS URL to a .pptx"},"pptx_base64":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pptx Base64","description":"Base64-encoded .pptx (≤25 MB decoded)"}},"type":"object","title":"InspectRequest","description":"Exactly ONE source."},"InspectResponse":{"properties":{"status":{"type":"string","title":"Status"},"slides_total":{"type":"integer","title":"Slides Total"},"canvas":{"type":"object","title":"Canvas"},"summary":{"type":"object","title":"Summary"},"slides":{"items":{"type":"object"},"type":"array","title":"Slides"},"truncated":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Truncated"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"source":{"type":"string","title":"Source"},"latency_ms":{"type":"integer","title":"Latency Ms"},"cost":{"type":"number","title":"Cost","default":0.0},"billed":{"type":"boolean","title":"Billed","default":false}},"type":"object","required":["status","slides_total","canvas","summary","slides","source","latency_ms"],"title":"InspectResponse"},"IntentDeckRequest":{"properties":{"slides":{"items":{"type":"object"},"type":"array","title":"Slides","default":[]},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id"},"brand":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Brand"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"dry_run":{"type":"boolean","title":"Dry Run","default":false},"force_render":{"type":"boolean","title":"Force Render","default":false},"allow_partial":{"type":"boolean","title":"Allow Partial","default":false},"logo_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Id"},"styling":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Styling"},"direction":{"anyOf":[{"type":"string","enum":["ltr","rtl"]},{"type":"null"}],"title":"Direction"},"imagery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Imagery"},"imagery_tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Imagery Tag"},"strict_policy":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Strict Policy"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"}},"type":"object","title":"IntentDeckRequest","description":"POST /v1/render/intent/deck — N structured SSG intents → one merged PPTX.\n\nREST mirror of the MCP create_deck intent path. `slides` is a list of intent objects\n(same shape as /v1/render/intent: form + typed fields, or a brief). Slides fill in\nparallel and merge into one themed deck. $0.05 per usable slide."},"IntentRenderRequest":{"properties":{"brief":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief"},"form":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Form"},"variant":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant"},"prior_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prior Id"},"content":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Content"},"author_ssg":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Author Ssg"},"headline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headline"},"context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"takeaway":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Takeaway"},"caution":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Caution"},"highlight":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight"},"source_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Note"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"form_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Form Description"},"density":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Density"},"direction":{"anyOf":[{"type":"string","enum":["ltr","rtl"]},{"type":"null"}],"title":"Direction"},"blocks":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Blocks"},"metric":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metric"},"data":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Data"},"image_src":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Src"},"image_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Prompt"},"auto_decompose":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Auto Decompose"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id"},"brand":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Brand"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"preview":{"type":"boolean","title":"Preview","default":false},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"},"allow_fabrication":{"type":"boolean","title":"Allow Fabrication","default":false},"allow_low_confidence":{"type":"boolean","title":"Allow Low Confidence","default":false},"allow_truncation":{"type":"boolean","title":"Allow Truncation","default":false},"dry_run":{"type":"boolean","title":"Dry Run","default":false},"quality_profile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quality Profile"},"variant_policy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant Policy"},"allow_variant_fallback":{"type":"boolean","title":"Allow Variant Fallback","default":false},"min_font_pt":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Font Pt"},"imagery":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Imagery"},"imagery_tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Imagery Tag"},"styling":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Styling"},"verify":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verify"}},"additionalProperties":true,"type":"object","title":"IntentRenderRequest","description":"POST /v1/render/intent — structured SSG intent → deterministic render.\n\nThe REST mirror of the MCP create_slide intent surface (the `_INTENT_FIELDS`\nallowlist in api/app/routers/ssg_v2.py). Provide a prose `brief`, a typed\nstructured intent (`form` + the content slots below), an exact catalog prior\n(`prior_id` + `content`), or a full `author_ssg` document. $0.05 per slide.\n(A free PNG preview path is a follow-on per ADR-2026-06-13, built with the web\nthemes/playground work.)"},"IssueInvoiceRequest":{"properties":{"transaction_id":{"type":"integer","title":"Transaction Id"}},"type":"object","required":["transaction_id"],"title":"IssueInvoiceRequest"},"JobStatus":{"properties":{"job_id":{"type":"string","title":"Job Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"status":{"type":"string","title":"Status"},"current_iteration":{"type":"integer","title":"Current Iteration","default":0},"max_iterations":{"type":"integer","title":"Max Iterations","default":5},"current_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Score"},"target_score":{"type":"number","title":"Target Score","default":8.5},"pptx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pptx Url"},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Url"},"pdf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Url"},"final_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Final Score"},"score_breakdown":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Score Breakdown"},"iterations":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Iterations"},"cost_charged":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cost Charged"},"latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latency Ms"},"strategy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Strategy"},"slides_completed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Slides Completed"},"slides_failed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Slides Failed"},"failed_slides":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Failed Slides"},"quality_warnings":{"anyOf":[{"items":{"$ref":"#/components/schemas/QualityWarning"},"type":"array"},{"type":"null"}],"title":"Quality Warnings"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["job_id","status"],"title":"JobStatus","description":"GET /v1/jobs/{id} response.\n\nStatus is one of: queued, generating, executing, rendering, complete,\npartial, failed. `partial` is deck-only — set when some child slides\nsucceeded and at least one failed. The deck PPTX still contains the\nsuccessful slides; agents that previously trusted `status == \"complete\"`\nmust now also handle `partial` (and read `failed_slides` to decide)."},"NewVersionRequest":{"properties":{"document":{"type":"object","title":"Document"}},"type":"object","required":["document"],"title":"NewVersionRequest"},"PdfAnalysis":{"properties":{"producer":{"type":"string","title":"Producer","description":"PDF metadata producer string (telemetry only — never used for routing). Captured so we can correlate buckets with real-world tools after the fact.","default":""},"page_count":{"type":"integer","title":"Page Count","description":"Number of pages in the PDF"},"file_size_bytes":{"type":"integer","title":"File Size Bytes","description":"Source PDF size in bytes"},"bucket":{"type":"string","enum":["slide-vector","slide-raster","doc-text","doc-image-heavy","scanned","mixed","unknown"],"title":"Bucket","description":"Routing decision derived from structural features. The /v1/deck/from-pdf endpoint accepts a file iff its bucket is currently implemented."},"features":{"$ref":"#/components/schemas/PdfFeatures","description":"Document-level structural features used by the classifier"},"estimated_cost_usd":{"type":"number","title":"Estimated Cost Usd","description":"Per-page price for the bucket × page_count"},"can_extract":{"type":"boolean","title":"Can Extract","description":"True iff /v1/deck/from-pdf will currently accept this file."},"unsupported_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unsupported Reason","description":"Human-readable hint shown to the user when can_extract is False, explaining what's not yet supported."},"pages":{"items":{"$ref":"#/components/schemas/PdfPageInfo"},"type":"array","title":"Pages","description":"Per-page summary, in document order"}},"type":"object","required":["page_count","file_size_bytes","bucket","features","estimated_cost_usd","can_extract"],"title":"PdfAnalysis","description":"Output of analyze_pdf() — what /analyze returns and what /generate uses to route."},"PdfFeatures":{"properties":{"aspect_ratio_first":{"type":"number","title":"Aspect Ratio First","description":"width / height of the first page"},"is_slide_aspect":{"type":"boolean","title":"Is Slide Aspect","description":"True if the first page sits in a slide-shaped aspect band (roughly 1.25–2.45 — covers 4:3, 16:10, 16:9, ultrawide)"},"is_doc_portrait":{"type":"boolean","title":"Is Doc Portrait","description":"True if the first page is portrait-oriented (aspect < 1.0)"},"consistent_dimensions":{"type":"boolean","title":"Consistent Dimensions","description":"True if every page in the document shares the same width/height"},"total_text_chars":{"type":"integer","title":"Total Text Chars","description":"Sum of extractable characters across all pages"},"total_text_spans":{"type":"integer","title":"Total Text Spans","description":"Sum of text spans (formatted runs) across all pages. Zero with non-zero total_text_chars suggests glyph-outlined text — readable but unformatted."},"total_images":{"type":"integer","title":"Total Images","description":"Sum of embedded images across all pages"},"total_drawings":{"type":"integer","title":"Total Drawings","description":"Sum of vector drawing primitives (rects, paths, lines) across all pages — high counts indicate hand-built slide content rather than scanned/raster"},"unique_fonts":{"type":"integer","title":"Unique Fonts","description":"Number of distinct fonts seen across the doc"},"pages_with_text":{"type":"integer","title":"Pages With Text","description":"Pages with at least 50 chars of extractable text"},"pages_image_only":{"type":"integer","title":"Pages Image Only","description":"Pages with images present but fewer than 50 chars of extractable text"},"pages_blank":{"type":"integer","title":"Pages Blank","description":"Pages with no text and no images"}},"type":"object","required":["aspect_ratio_first","is_slide_aspect","is_doc_portrait","consistent_dimensions","total_text_chars","total_text_spans","total_images","total_drawings","unique_fonts","pages_with_text","pages_image_only","pages_blank"],"title":"PdfFeatures","description":"Document-level structural features used by the bucket classifier.\n\nEverything here comes from `fitz` primitives — no LLM calls. The fields\nare deliberately concrete (counts, ratios, booleans) so threshold tuning\nagainst a real corpus is reproducible."},"PdfPageInfo":{"properties":{"page_number":{"type":"integer","title":"Page Number","description":"1-based page number"},"has_text":{"type":"boolean","title":"Has Text","description":"True if the page contains extractable text"},"has_images":{"type":"boolean","title":"Has Images","description":"True if the page contains embedded images"},"text_char_count":{"type":"integer","title":"Text Char Count","description":"Total characters of extractable text"},"text_preview":{"type":"string","title":"Text Preview","description":"First ~80 characters of text content (for the analysis card)","default":""}},"type":"object","required":["page_number","has_text","has_images","text_char_count"],"title":"PdfPageInfo","description":"Per-page summary returned by the analyzer (lean — used by the UI list)."},"PlanCheckoutRequest":{"properties":{"plan":{"type":"string","title":"Plan"}},"type":"object","required":["plan"],"title":"PlanCheckoutRequest"},"PlanOverflowRequest":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"PlanOverflowRequest"},"ProceedRequest":{"properties":{"image":{"type":"string","title":"Image"},"instructions":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instructions"},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id"},"brand":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Brand"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"selected_option_id":{"anyOf":[{"type":"string","enum":["preserve_slide","explain_slide","teach_deck","reconstruct_slide"]},{"type":"null"}],"title":"Selected Option Id"},"mode":{"type":"string","title":"Mode","default":"auto"},"choices":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Choices"},"allow_image_generation":{"type":"boolean","title":"Allow Image Generation","default":true},"verify":{"type":"boolean","title":"Verify","default":true}},"type":"object","required":["image"],"title":"ProceedRequest"},"ProvisionRequest":{"properties":{"user_id":{"type":"string","title":"User Id","default":""},"email":{"type":"string","title":"Email","default":""},"name":{"type":"string","title":"Name","default":""}},"type":"object","title":"ProvisionRequest"},"QualityWarning":{"properties":{"category":{"type":"string","title":"Category"},"severity":{"type":"string","title":"Severity"},"description":{"type":"string","title":"Description"},"source":{"type":"string","title":"Source","default":"static_linter"},"location":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Location"}},"type":"object","required":["category","severity","description"],"title":"QualityWarning","description":"A single static-linter or visual-review finding (P3.2a, 2026-04-24).\n\nSurfaced on JobStatus.quality_warnings. Non-blocking — the job status\nstays `complete`/`partial`; quality_warnings is a separate signal an\nagent can read to decide whether to iterate.\n\nseverity:\n  - 'bug'        — real visual issue (text overlap, off-canvas, contrast\n                   fail, font outside legible range). MCP card surfaces\n                   a banner when any 'bug' entries exist.\n  - 'suggestion' — soft hint (high shape count, missing title zone,\n                   borderline font). Silent in the MCP card.\n\nsource:\n  - 'static_linter' — convergence/linter.py output (3.2a)\n  - 'vlm'           — Pixtral one-shot review (3.2b, future)\n  - 'report_linter' — convergence/quality/report_linter.py output"},"RepairRequest":{"properties":{"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"A prior SlideForge slide/deck job to inspect"},"pptx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pptx Url","description":"HTTPS URL to a .pptx"},"pptx_base64":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pptx Base64","description":"Base64-encoded .pptx (≤25 MB decoded)"},"actions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Actions","description":"Subset of ['shrink_to_fit', 'raise_min_font', 'remove_off_canvas', 'fix_contrast'] (default: all)"},"dry_run":{"type":"boolean","title":"Dry Run","description":"Plan the fixes without mutating anything (free preview)","default":false},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Job name for the repaired deck"}},"type":"object","title":"RepairRequest"},"RepairResponse":{"properties":{"status":{"type":"string","title":"Status"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"pptx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pptx Url"},"preview_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Url"},"repair_report":{"type":"object","title":"Repair Report"},"source":{"type":"string","title":"Source"},"latency_ms":{"type":"integer","title":"Latency Ms"},"cost":{"type":"number","title":"Cost","default":0.0},"billed":{"type":"boolean","title":"Billed","default":false}},"type":"object","required":["status","repair_report","source","latency_ms"],"title":"RepairResponse"},"Replacement":{"properties":{"old":{"type":"string","title":"Old","description":"Exact string to find in the code"},"new":{"type":"string","title":"New","description":"String to replace it with"}},"type":"object","required":["old","new"],"title":"Replacement"},"ReportWorkflow":{"properties":{"workflow_id":{"type":"string","title":"Workflow Id"},"version":{"type":"string","title":"Version"},"name":{"type":"string","title":"Name"},"owner_scope":{"type":"string","enum":["seed","customer","internal"],"title":"Owner Scope","default":"seed"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"payload_schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payload Schema"},"payload_schema_inline":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Payload Schema Inline"},"theme_id":{"type":"string","title":"Theme Id","default":"slideforge_standard"},"strict_policy":{"type":"object","title":"Strict Policy"},"name_template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name Template"},"connector":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Connector"},"slides":{"items":{"$ref":"#/components/schemas/WorkflowSlide"},"type":"array","title":"Slides"}},"type":"object","required":["workflow_id","version","name","slides"],"title":"ReportWorkflow"},"ReportWorkflowFromDeckRequest":{"properties":{"workflow_id":{"type":"string","minLength":1,"title":"Workflow Id"},"deck_request":{"type":"object","title":"Deck Request"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"save":{"type":"boolean","title":"Save","default":false}},"type":"object","required":["workflow_id","deck_request"],"title":"ReportWorkflowFromDeckRequest"},"ReportWorkflowRunRequest":{"properties":{"payload":{"type":"object","title":"Payload"},"connection_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connection Id"},"entity_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Name"},"dry_run":{"type":"boolean","title":"Dry Run","default":true},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id"},"include_compiled_deck":{"type":"boolean","title":"Include Compiled Deck","default":true}},"type":"object","title":"ReportWorkflowRunRequest"},"ReportWorkflowSaveRequest":{"properties":{"workflow":{"$ref":"#/components/schemas/ReportWorkflow"},"validation_payload":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Validation Payload"},"compile_check":{"type":"boolean","title":"Compile Check","default":true}},"type":"object","required":["workflow"],"title":"ReportWorkflowSaveRequest"},"SandboxRenderRequest":{"properties":{"code":{"anyOf":[{"type":"string","maxLength":64000},{"type":"null"}],"title":"Code","description":"Python code defining build(prs) or build(prs, slide). Required if job_id not provided. Max 64 KB — well above any real slide; the Jev screen (A2) screens the whole thing, so an unbounded field would let a padded payload slip past a prefix screen."},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id","description":"Previous render_code job ID — loads its code for patching with replacements."},"replacements":{"anyOf":[{"items":{"$ref":"#/components/schemas/Replacement"},"type":"array"},{"type":"null"}],"title":"Replacements","description":"String replacements to apply to the loaded code. Requires job_id."},"theme_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Theme Id","description":"Theme ID for chrome + colors"},"logo_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Id","description":"Uploaded logo (upload_asset purpose=logo): the account's logo inside the code — add_logo(slide) draws it in the theme's logo box."},"chrome":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Chrome","description":"Chrome overlays: title, subtitle, footer, etc."},"headline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Headline","description":"Slide title (chrome-rendered; use build(prs, slide))"},"context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context","description":"Subtitle line under the title (chrome-rendered)"},"takeaway":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Takeaway","description":"Bottom takeaway band (chrome-rendered)"},"source_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Note","description":"Source citation in the footer zone (chrome-rendered)"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Speaker notes for the presenter; never drawn on the slide. Wins over notes the code wrote."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Human-readable job name"},"include_preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Include Preview","description":"Embed preview inline: none, small (256px), default (768px), full. Default: none."},"verify":{"type":"string","title":"Verify","description":"Verification tier: off | lint (default — static geometry linter + composer content validators, no LLM) | lint+vlm (adds the VLM second-look, ~+8s) | full.","default":"lint"},"dry_run":{"type":"boolean","title":"Dry Run","description":"If true: run the verify tier and return status (validated if clean, invalid if errors) + warnings/errors with cost:0 and NO PPTX — a free pre-commit check. Fix errors, then render for real.","default":false},"quality_profile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quality Profile","description":"Readiness thresholds the layout block is judged against: executive | technical (default) | appendix."},"fail_on_vlm":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Fail On Vlm","description":"VLM finding categories that BLOCK (escalate to errors, render free per usable-or-free): substring match on the finding, e.g. ['contrast', 'clipping']. A high-confidence `overlap` and any `content_missing` already block by default. Requires verify=lint+vlm."},"content":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Content","description":"Typed business facts (ADR 2026-07-14): available read-only as CONTENT inside the code; the renderer verifies every leaf rendered verbatim and emits fidelity + provenance. Keys named 'id' are references, not display content."},"code_profile":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Profile","description":"widgets_only = compose via add_widget + approved helpers + CONTENT/theme only; raw python-pptx (slide.shapes.*, add_text_box/add_box, add_slide) is rejected. Omit or 'native' for full access. Response echoes code_profile.applied + violations."}},"type":"object","title":"SandboxRenderRequest"},"SetCodeRequest":{"properties":{"code":{"type":"string","maxLength":20,"minLength":3,"title":"Code","description":"Custom referral code (alphanumeric + hyphens)"}},"type":"object","required":["code"],"title":"SetCodeRequest","description":"PUT /v1/referral/code body."},"SlideEditRequest":{"properties":{"patch":{"type":"object","title":"Patch","description":"the slide fields to change (same fields as create_slide). A field left out keeps its value, a field sent as null is removed, a list replaces the whole list, `data` merges key by key. Send only `notes` to add or replace speaker notes without a re-render."}},"type":"object","required":["patch"],"title":"SlideEditRequest"},"SlideReplacement":{"properties":{"position":{"type":"integer","title":"Position"},"job_id":{"type":"string","title":"Job Id"}},"type":"object","required":["position","job_id"],"title":"SlideReplacement","description":"One slide to swap in a deck."},"SlideSpec":{"properties":{"generate":{"anyOf":[{"$ref":"#/components/schemas/GenerateSpec"},{"type":"null"}]},"brief":{"type":"string","title":"Brief","default":""},"chrome":{"anyOf":[{"$ref":"#/components/schemas/Chrome"},{"type":"null"}]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"slide_number":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Slide Number"}},"type":"object","title":"SlideSpec","description":"One slide within a deck request."},"ToolAttempt":{"properties":{"tool":{"type":"string","enum":["image-to-pptx","combine-powerpoint","pdf-to-pptx"],"title":"Tool"},"file_count":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"File Count"},"total_bytes":{"type":"integer","minimum":0.0,"title":"Total Bytes"},"mimes":{"items":{"type":"string"},"type":"array","maxItems":100,"title":"Mimes"}},"type":"object","required":["tool","file_count","total_bytes"],"title":"ToolAttempt","description":"A file picked on a free-tool page. Shape only — never a filename or content."},"ToolAttemptAck":{"properties":{"recorded":{"type":"boolean","title":"Recorded"}},"type":"object","required":["recorded"],"title":"ToolAttemptAck"},"TranslateFromJobRequest":{"properties":{"source_job_id":{"type":"string","title":"Source Job Id"},"target_language":{"type":"string","title":"Target Language"},"source_language":{"type":"string","title":"Source Language","default":"auto"},"include_notes":{"type":"boolean","title":"Include Notes","default":false},"include_tables":{"type":"boolean","title":"Include Tables","default":true},"concise_mode":{"type":"boolean","title":"Concise Mode","default":false},"delete_after_download":{"type":"boolean","title":"Delete After Download","default":false},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["source_job_id","target_language"],"title":"TranslateFromJobRequest","description":"POST /v1/translate/from-job request body.\n\nJSON-body alternative to /v1/translate (multipart) for the no-file case.\nCleaner DX for agents that don't want to construct multipart for what is\neffectively a JSON request: 'translate this existing job into German'."},"TriageRequest":{"properties":{"status":{"type":"string","title":"Status"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"promoted_component_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promoted Component Name"}},"type":"object","required":["status"],"title":"TriageRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VariantPreviewRequest":{"properties":{"prior_id":{"type":"string","maxLength":120,"minLength":1,"title":"Prior Id"}},"type":"object","required":["prior_id"],"title":"VariantPreviewRequest","description":"A pinned catalog variant to render. NOT a brief — see the endpoint docstring."},"WebhookCreate":{"properties":{"url":{"type":"string","title":"Url","description":"HTTPS endpoint URL to receive events"},"events":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Events","description":"Event types to subscribe to (default: all)"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Human-readable description"}},"type":"object","required":["url"],"title":"WebhookCreate"},"WorkflowSlide":{"properties":{"id":{"type":"string","title":"Id"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"},"required":{"type":"boolean","title":"Required","default":false},"when":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"When"},"form":{"type":"string","title":"Form"},"variant":{"type":"string","title":"Variant"},"constants":{"type":"object","title":"Constants"},"bindings":{"additionalProperties":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"type":"object","title":"Bindings"},"optional_bindings":{"additionalProperties":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"type":"object","title":"Optional Bindings"}},"type":"object","required":["id","form","variant"],"title":"WorkflowSlide"}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"SlideForge API key: `Authorization: Bearer sf_live_…`. Create one at https://slideforge.dev/console/keys"},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Same key, header form — for clients that cannot set Authorization."}}},"servers":[{"url":"https://api.slideforge.dev"}],"security":[{"bearerAuth":[]},{"apiKeyAuth":[]}]}