{"openapi":"3.1.0","info":{"title":"html2pptx.app Export API","version":"1.0.0","description":"Convert HTML/CSS to fully editable PowerPoint files. Supports commercial plans with rate limiting, usage quotas, and concurrent job control.","contact":{"url":"https://html2pptx.app"}},"servers":[{"url":"https://html2pptx.app","description":"Production"}],"security":[{"bearerAuth":[]}],"paths":{"/api/v1/import/pptx":{"post":{"operationId":"importPptxV1","summary":"Convert an existing .pptx into editable HTML slides (v1)","description":"Versioned alias for POST /api/import/pptx. Parses PPTX OOXML and returns HTML/SVG slides, resolving theme colors (incl. clrMap/clrMapOvr), master txStyles and font families. Synchronous.","tags":["Import"],"requestBody":{"required":true,"description":"Send the .pptx either as multipart/form-data (field \"file\") or as JSON with a base64 payload (handy for JSON-RPC / MCP clients).","content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The .pptx file (max 25 MB)."}}}},"application/json":{"schema":{"type":"object","required":["pptxBase64"],"properties":{"pptxBase64":{"type":"string","description":"Base64-encoded .pptx contents (data URLs accepted)."},"fileName":{"type":"string","description":"Optional original file name. Defaults to presentation.pptx."}}}}}},"responses":{"200":{"description":"Conversion result with html, per-slide HTML, and shared css.","headers":{"x-request-id":{"description":"Unique request trace identifier","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PptxImportResult"}}}},"400":{"description":"Missing/invalid file or invalid OOXML."},"401":{"description":"API key required."},"403":{"description":"API key not mapped to a plan with API access."},"413":{"description":"PPTX exceeds the size limit."}}}},"/api/v1/export/jobs":{"post":{"operationId":"createExportJobV1","summary":"Create an HTML-to-PPTX export job (v1)","description":"Versioned alias for POST /api/export/jobs. Identical behavior.","tags":["Export"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Optional idempotency key. Repeating a request with the same key within 24 hours returns the original response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobRequest"}}}},"responses":{"200":{"description":"Job created (or completed when waitForCompletion was handled upstream)","headers":{"x-request-id":{"description":"Unique request trace identifier","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportJob"}}}}}}},"/api/v1/export/jobs/{jobId}":{"get":{"operationId":"getExportJobV1","summary":"Get export job status (v1)","description":"Versioned alias for GET /api/export/jobs/{jobId}.","tags":["Export"],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"The export job identifier."}],"responses":{"200":{"description":"Job status","headers":{"x-request-id":{"description":"Unique request trace identifier","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportJob"}}}}}}},"/api/v1/export/plans":{"get":{"operationId":"listExportPlansV1","summary":"List available export plans (v1)","description":"Versioned alias for GET /api/export/plans.","tags":["Plans"],"security":[],"responses":{"200":{"description":"Plan catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanCatalog"}}}}}}},"/api/export/jobs":{"post":{"operationId":"createExportJob","summary":"Create an HTML-to-PPTX export job","description":"Submit HTML (and optional CSS) to convert into a PowerPoint file. The job runs asynchronously; poll the returned jobId for completion.","tags":["Export"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Optional idempotency key. Repeating a request with the same key within 24 hours returns the original response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobRequest"}}}},"responses":{"200":{"description":"Job created (or completed when waitForCompletion was handled upstream)","headers":{"x-request-id":{"description":"Unique request trace identifier","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportJob"}}}},"400":{"description":"Invalid request (missing HTML, bad JSON)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Invalid API key or plan does not include API access","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"413":{"description":"Payload too large for plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"422":{"description":"Slide count exceeds plan limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"429":{"description":"Rate limit, daily limit, monthly limit, or concurrent job limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}},"headers":{"retry-after":{"description":"Seconds to wait before retrying","schema":{"type":"string"}}}},"502":{"description":"Upstream worker unreachable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"503":{"description":"Service temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}}}}},"/api/export/jobs/{jobId}":{"get":{"operationId":"getExportJob","summary":"Get export job status","description":"Fetch the current status of a previously created export job.","tags":["Export"],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"The export job identifier returned by POST /api/export/jobs."}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportJob"}}}},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}}}}},"/api/export/plans":{"get":{"operationId":"listExportPlans","summary":"List available export plans","description":"Fetch the public commercial plan catalog, including recommended tier.","tags":["Plans"],"security":[],"responses":{"200":{"description":"Plan catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanCatalog"}}}}}}},"/api/export/usage":{"get":{"operationId":"getUsage","summary":"Get current usage and quota","description":"Fetch daily/monthly usage, fair-use state, and plan limits for the authenticated API key.","tags":["Usage"],"responses":{"200":{"description":"Usage snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageSnapshot"}}}},"401":{"description":"API key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}}}}},"/api/user-templates":{"post":{"operationId":"publishUserTemplate","summary":"Disabled: template drafts are remote-MCP-only","deprecated":true,"description":"This REST endpoint no longer creates marketplace drafts. Template publishing is HTML-only and remote-MCP-only. Use `html2pptx_validate_template_html`, fix every error, then call `html2pptx_publish_template` from an authenticated remote MCP client with a WorkOS-bound user token. Web UI, CLI, local MCP, generic REST API, and PPTX source files are not publishing surfaces.","tags":["Creator Marketplace"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishTemplateRequest"}},"multipart/form-data":{"schema":{"type":"object","description":"Deprecated. Source uploads are rejected; use remote MCP with validated HTML.","properties":{"pptx":{"type":"string","format":"binary","deprecated":true,"description":"Rejected. PPTX is not a template publishing source."},"html":{"type":"string","format":"binary","deprecated":true,"description":"Rejected. Use remote MCP validate -> publish instead."},"cover":{"type":"string","format":"binary","description":"Custom cover image (optional)"},"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"tags":{"type":"string","description":"JSON-encoded array of strings"},"prompt":{"type":"string"},"premiumOnly":{"type":"string","enum":["true","false"]},"allowSourceView":{"type":"string","enum":["true","false"]},"visibility":{"type":"string","enum":["draft"],"description":"Remote MCP creates drafts only."},"slides":{"type":"string"}},"required":["title"]}}}},"parameters":[{"name":"async","in":"query","required":false,"schema":{"type":"string","enum":["1"]},"description":"Deprecated. POST /api/user-templates is disabled; use remote MCP publishing instead."}],"responses":{"200":{"description":"Deprecated success shape from older deployments. Current production returns 403 for POST.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishTemplateResponse"}}}},"400":{"description":"Validation failure — banned term in text/tag, prompt-injection hit, missing fields, or unsupported content-type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"403":{"description":"Template draft creation is remote-MCP-only and HTML-only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}},"413":{"description":"Deprecated legacy response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}}}},"get":{"operationId":"listMyTemplates","summary":"List the caller's own templates","tags":["Creator Marketplace"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Array of the caller's templates across all statuses.","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/UserTemplate"}}}}}}},"401":{"description":"Authentication required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetail"}}}}}}},"/api/user-templates/{id}/publish":{"post":{"operationId":"publishDraftTemplate","summary":"Publish a draft template (make it public)","description":"Transition a remote-MCP-created HTML draft (or rejected draft) to status=published.","tags":["Creator Marketplace"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Template is now published.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"Authentication required."},"403":{"description":"Not the creator."},"404":{"description":"Draft not found."}}},"delete":{"operationId":"unpublishTemplate","summary":"Move a published template back to draft","tags":["Creator Marketplace"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Template status reset to draft."}}}},"/api/user-templates/{id}/metadata":{"post":{"operationId":"updateTemplateMetadata","summary":"Edit a draft template's metadata","description":"Update title / description / category / tags / prompt / coverSlideIndex / premiumOnly without re-uploading the source file.","tags":["Creator Marketplace"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"prompt":{"type":"string"},"premiumOnly":{"type":"boolean"},"coverSlideIndex":{"type":"integer","minimum":0}}}}}},"responses":{"200":{"description":"Metadata updated."},"400":{"description":"Validation failure."},"401":{"description":"Authentication required."},"403":{"description":"Not the creator."}}}},"/api/templates/attribute":{"post":{"operationId":"attributeTemplateUsage","summary":"Attribute template usage for earnings","description":"Credit a template for a generation you just did via REST API, CLI, or any other channel. Safe to call opportunistically — deduping, rate-limiting, and self-exclusion are all enforced server-side. Returns HTTP 200 even when the event is rejected (counted: false + reason).","tags":["Creator Marketplace"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["templateId"],"properties":{"templateId":{"type":"string","description":"Catalog id for built-ins, or slug for user-submitted templates."},"templateSource":{"type":"string","enum":["catalog","user"],"description":"Defaults to catalog."},"eventType":{"type":"string","enum":["view","download","mcp","api","cli"],"description":"Defaults to inferred type: \"cli\" when the UA matches html2pptx-cli, \"api\" otherwise."}}}}}},"responses":{"200":{"description":"Event recorded (see `counted` + `reason` in the response body).","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"counted":{"type":"boolean"},"pointValue":{"type":"number"},"reason":{"type":"string","enum":["self","dedupe","rate_limit","unknown_template","unpublished"]}}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key passed as a Bearer token in the Authorization header."}},"schemas":{"PptxImportResult":{"type":"object","description":"Result of converting a .pptx into HTML slides.","properties":{"fileName":{"type":"string"},"plan":{"type":"string","description":"Commercial plan id used for the request."},"label":{"type":"string","nullable":true},"slideCount":{"type":"integer"},"slides":{"type":"array","description":"Per-slide objects.","items":{"type":"object","properties":{"index":{"type":"integer"},"source":{"type":"string"},"layout":{"type":"string","nullable":true},"html":{"type":"string"}}}},"html":{"type":"string","description":"Full deck HTML document."},"css":{"type":"string","description":"Shared stylesheet for the slides."},"warnings":{"type":"array","items":{"type":"string"}}}},"PublishTemplateRequest":{"type":"object","description":"Deprecated REST shape. Template drafts are HTML-only and remote-MCP-only; this schema is retained for older clients and explains rejected fields.","required":["title"],"properties":{"pptxBase64":{"type":"string","deprecated":true,"description":"Rejected. PPTX is not a template publishing source."},"pptxObjectKey":{"type":"string","deprecated":true,"description":"Rejected. Source upload presigns are disabled."},"htmlBase64":{"type":"string","deprecated":true,"description":"Rejected on REST. Use remote MCP `html2pptx_validate_template_html` -> `html2pptx_publish_template` instead."},"htmlObjectKey":{"type":"string","deprecated":true,"description":"Rejected. Source upload presigns are disabled; use remote MCP validate -> publish instead."},"coverBase64":{"type":"string","description":"Optional custom cover image, base64-encoded. Max 4 MB decoded."},"coverObjectKey":{"type":"string","description":"Object key returned by /api/user-templates/presign after uploading a custom cover image directly to storage."},"coverContentType":{"type":"string","description":"MIME type for coverBase64 (e.g. \"image/png\")."},"title":{"type":"string","minLength":2,"maxLength":120},"description":{"type":"string","maxLength":400},"category":{"type":"string","description":"ビジネス / デザイン / テクノロジー / その他 or a custom label."},"tags":{"type":"array","items":{"type":"string"},"maxItems":20},"prompt":{"type":"string","maxLength":8000,"description":"DESIGN.md — the prompt used to generate the HTML. Must not contain instructions aimed at other AI agents (prompt-injection is blocked)."},"premiumOnly":{"type":"boolean"},"allowSourceView":{"type":"boolean","deprecated":true,"description":"Remote MCP HTML option. REST publishing is rejected."},"visibility":{"type":"string","enum":["draft"],"description":"Remote MCP creates a private creator draft only. Sharing/publishing happens from the dashboard."},"slides":{"type":"integer","minimum":0,"description":"Declared slide count (auto-corrected after the render finishes)."}}},"PublishTemplateResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string","description":"Convex id of the draft row."},"slug":{"type":"string","description":"URL-safe public slug."},"renderStatus":{"type":"string","enum":["ready","pending","failed"]},"slideImageCount":{"type":"integer"},"visibility":{"type":"string","enum":["draft"]},"viewUrl":{"type":"string","format":"uri","description":"Potential share/public page after the creator explicitly shares or publishes from the dashboard."},"draftUrl":{"type":"string","format":"uri","description":"Dashboard link where the creator can pick a cover + publish."},"previewApi":{"type":"string","description":"Internal preview endpoint for slide thumbnails or the static hosted HTML preview."},"htmlRenderMode":{"type":"string","enum":["snapshot","sandbox"]},"hasHtmlSnapshot":{"type":"boolean"},"allowSourceView":{"type":"boolean"},"htmlSecurityStatus":{"type":"string","enum":["passed","flagged","scan_error","skipped"],"description":"Stored HTML source scan status. Sharing/publishing is blocked unless this is passed."},"htmlSecuritySummary":{"type":"string","description":"Human-readable summary of the latest HTML source scan."},"htmlSecurityChecks":{"type":"array","items":{"type":"object","additionalProperties":true}},"error":{"type":"string","description":"Present only when renderStatus === \"failed\"."}}},"UserTemplate":{"type":"object","properties":{"_id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"prompt":{"type":"string"},"status":{"type":"string","enum":["draft","unlisted","pending_review","published","rejected","removed"]},"renderStatus":{"type":"string","enum":["pending","rendering","ready","failed"]},"slideImageCount":{"type":"integer"},"coverSlideIndex":{"type":"integer"},"premiumOnly":{"type":"boolean"},"publishedAt":{"type":"integer","description":"Unix ms timestamp."},"createdAt":{"type":"integer"},"updatedAt":{"type":"integer"}}},"CreateJobRequest":{"type":"object","required":["html"],"properties":{"html":{"type":"string","minLength":1,"description":"Slide-oriented HTML markup. Each slide should be a <section class=\"slide\"> element with explicit pixel dimensions."},"css":{"type":"string","description":"Optional CSS applied alongside the HTML."},"fileName":{"type":"string","description":"Optional output file name. Defaults to export.pptx."},"autoEmbedFonts":{"type":"boolean","description":"Attempt to embed detected fonts into the PPTX."},"slideCount":{"type":"integer","minimum":1,"description":"Optional explicit slide count. If omitted, the API estimates it from .slide roots."},"metadata":{"type":"object","additionalProperties":true,"description":"Optional metadata forwarded to the export worker."},"width":{"type":"number","description":"Optional PPTX slide width in inches."},"height":{"type":"number","description":"Optional PPTX slide height in inches."},"layout":{"type":"string","description":"Optional PPTX layout preset. Examples: LAYOUT_16x9, LAYOUT_16x10, LAYOUT_4x3, LAYOUT_WIDE."},"responseFormat":{"type":"string","enum":["url","base64","both"],"default":"url","description":"Controls how the completed PPTX is delivered. \"url\" returns a presigned download URL, \"base64\" returns the file inline, \"both\" returns both."},"callbackUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive a webhook POST when the job completes or fails. The payload is signed with an HMAC-SHA256 in the x-webhook-signature header."}}},"ExportJob":{"type":"object","properties":{"jobId":{"type":"string","description":"Unique job identifier."},"status":{"type":"string","enum":["pending","processing","completed","failed"],"description":"Current job status."},"fileName":{"type":"string","description":"Output file name."},"downloadUrl":{"type":"string","format":"uri","description":"Presigned URL to download the completed PPTX file."},"statusUrl":{"type":"string","format":"uri","description":"URL to poll for job status updates."},"message":{"type":"string","description":"Human-readable status message."}}},"PlanCatalog":{"type":"object","properties":{"recommendedPlanId":{"type":"string","description":"The recommended plan identifier."},"plans":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}},"note":{"type":"string"}}},"Plan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"priceLabel":{"type":"string"},"limits":{"type":"object","properties":{"requestsPerMinute":{"type":"integer"},"maxSlidesPerJob":{"type":"integer"},"concurrentJobs":{"type":"integer"},"maxPayloadBytes":{"type":"integer"},"dailyRequestLimit":{"type":"integer"},"monthlyRequestLimit":{"type":"integer"}}}}},"UsageSnapshot":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"limits":{"type":"object","properties":{"requestsPerMinute":{"type":"integer"},"dailyRequestLimit":{"type":"integer"},"monthlyRequestLimit":{"type":"integer"},"maxSlidesPerJob":{"type":"integer"},"concurrentJobs":{"type":"integer"},"maxPayloadBytes":{"type":"integer"}}},"usage":{"type":"object","properties":{"dailyUsed":{"type":"integer","nullable":true},"dailyRemaining":{"type":"integer","nullable":true},"dailyLimitAvailable":{"type":"boolean"},"dayStartsAt":{"type":"string","format":"date-time"},"dayResetsAt":{"type":"string","format":"date-time"},"monthlyUsed":{"type":"integer","nullable":true},"monthlyRemaining":{"type":"integer","nullable":true},"monthlyLimitAvailable":{"type":"boolean"},"monthStartsAt":{"type":"string","format":"date-time"},"monthResetsAt":{"type":"string","format":"date-time"},"activeJobs":{"type":"integer","nullable":true},"monthlyFairUseState":{"type":"string","enum":["normal","review","upgrade_recommended","unknown"]}}}}},"ProblemDetail":{"type":"object","description":"RFC 9457 Problem Details error response with backwards-compatible fields.","properties":{"error":{"type":"string","description":"Machine-readable error code (snake_case)."},"message":{"type":"string","description":"Human-readable error description (legacy field)."},"type":{"type":"string","format":"uri","description":"RFC 9457 problem type URI."},"status":{"type":"integer","description":"HTTP status code."},"title":{"type":"string","description":"Short human-readable title (Title Case of error code)."},"detail":{"type":"string","description":"Detailed human-readable explanation (same as message)."},"instance":{"type":"string","description":"Request path that generated this error (when available)."}},"required":["error","message","type","status","title","detail"]}}}}