{"openapi":"3.1.0","info":{"title":"Ktavly PDF API","version":"1.0.0","summary":"Polished PDFs from JSON templates or HTML. Nothing is stored after rendering.","description":"Send a template id and your data (or raw HTML) and get a print-ready PDF back in the same response.\nHebrew and Arabic are first-class: fonts are embedded and every built-in template works right-to-left.\n\n## Authentication\nRendering and usage endpoints take an API key, created in the dashboard. Send it as\n`Authorization: Bearer pdfk_live_…` (preferred) or `X-API-Key: pdfk_live_…`. Keys are shown once and stored\nonly as HMAC-SHA256 hashes.\n\nDashboard endpoints (`/v1/auth/*`, `/v1/me`, `/v1/account/*`) use the `sid` session cookie set by\n`POST /v1/auth/login`. They only accept `application/json` bodies and reject cross-site `Origin` headers.\n\n## Errors\nEvery error has the same shape: `{\"error\": {\"code\", \"message\", \"details\"?, \"requestId\"}}`. Branch on `code`;\nmessages may change. The full list is at `GET /v1/errors`.\n\n## Privacy\nRequest bodies, HTML and PDFs only exist in memory for the duration of the request. Logs carry metadata\n(request id, route, status, timing, page count, template id, key prefix), never document content.\n\n## Limits\nBodies up to 2 MB. Render time limit 30 s. Requests per minute and documents per month depend on the plan\n(Free 200/mo at 10/min, Starter 5,000 at 60/min, Growth 20,000 at 120/min, Scale 60,000 at 300/min).\n","contact":{"name":"Ktavly support","email":"support@ktavly.com","url":"https://ktavly.com"},"license":{"name":"Proprietary, see the Terms of Service","url":"https://ktavly.com/legal/terms"},"termsOfService":"https://ktavly.com/legal/terms"},"servers":[{"url":"https://api.ktavly.com","description":"Production (planned; ktavly.com is a placeholder and not live yet)"},{"url":"http://localhost:8787","description":"Local development server"}],"tags":[{"name":"Rendering","description":"Turn a template plus data, or raw HTML, into a PDF."},{"name":"Templates","description":"Built-in templates with JSON Schemas and sample data."},{"name":"Usage","description":"Plan, quota and per-key usage for the calling API key."},{"name":"Playground","description":"Unmetered previews of built-in templates for the editor and the public gallery."},{"name":"Account","description":"Sign-up, sign-in and API key management for the dashboard (session cookie)."},{"name":"Webhooks","description":"Inbound billing events."},{"name":"Meta","description":"Health, error reference, brand and this specification."}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"paths":{"/v1/render":{"post":{"tags":["Rendering"],"operationId":"renderPdf","summary":"Render a PDF","description":"Renders a built-in template (`template` + `data`), an inline template exported from the editor, or raw\n`html`. Returns the PDF bytes, or JSON with base64 when `Accept: application/json` is sent.\nOnly successful renders count toward your monthly quota.\n","parameters":[{"name":"download","in":"query","required":false,"description":"Set to `1` to send `Content-Disposition: attachment` instead of `inline`.","schema":{"type":"string","enum":["1"]}},{"name":"Accept","in":"header","required":false,"description":"`application/pdf` (default) or `application/json` for a base64 envelope.","schema":{"type":"string","examples":["application/pdf","application/json"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderRequest"},"examples":{"invoice":{"summary":"Built-in invoice template","value":{"template":"invoice","data":{"number":"INV-1001","issueDate":"2026-10-18","currency":"USD","from":{"name":"Northwind Studio"},"to":{"name":"Lumen Analytics"},"items":[{"description":"Design sprint","quantity":1,"unitPrice":6400}],"taxRate":8.5}}},"hebrewInvoice":{"summary":"Hebrew tax invoice (RTL)","value":{"template":"invoice-he","data":{"documentType":"tax_invoice","number":"20261042","issueDate":"2026-10-18","seller":{"name":"סטודיו גפן בע״מ","businessId":"514238765"},"customer":{"name":"אורן מערכות מידע בע״מ"},"items":[{"description":"עיצוב ממשק משתמש","quantity":1,"unitPrice":18500}]}}},"rawHtml":{"summary":"Raw HTML with page numbers and a footer","value":{"html":"<h1>Hello</h1><p>שלום עולם</p>","options":{"format":"A4","margin":"15mm","pageNumbers":true,"footer":"Acme Ltd · confidential"}}}}}}},"responses":{"200":{"description":"The PDF (or a base64 envelope when JSON was requested).","headers":{"X-Pages":{"$ref":"#/components/headers/X-Pages"},"X-Render-Ms":{"description":"Render time in milliseconds.","schema":{"type":"integer"}},"X-Usage-Used":{"$ref":"#/components/headers/X-Usage-Used"},"X-Usage-Limit":{"$ref":"#/components/headers/X-Usage-Limit"},"X-Usage-Remaining":{"$ref":"#/components/headers/X-Usage-Remaining"},"X-Usage-Overage":{"description":"`true` when this document was billed as overage.","schema":{"type":"string","enum":["true"]}},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Content-Disposition":{"description":"`inline` or `attachment` with the file name.","schema":{"type":"string"}}},"content":{"application/pdf":{"schema":{"type":"string","contentMediaType":"application/pdf"}},"application/json":{"schema":{"$ref":"#/components/schemas/RenderJsonResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/QuotaExceeded"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"$ref":"#/components/responses/ServerBusy"},"504":{"$ref":"#/components/responses/RenderTimeout"}}}},"/v1/templates":{"get":{"tags":["Templates"],"operationId":"listTemplates","summary":"List templates, fonts and page formats","security":[],"responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"}}}},"405":{"$ref":"#/components/responses/MethodNotAllowed"}}}},"/v1/templates/{id}":{"parameters":[{"$ref":"#/components/parameters/TemplateId"}],"get":{"tags":["Templates"],"operationId":"getTemplate","summary":"Get a template with its schema, labels, styles and samples","security":[],"responses":{"200":{"description":"The template definition (without its HTML source).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateDetail"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/templates/{id}/schema":{"parameters":[{"$ref":"#/components/parameters/TemplateId"}],"get":{"tags":["Templates"],"operationId":"getTemplateSchema","summary":"Get the JSON Schema for a template's data","security":[],"responses":{"200":{"description":"A JSON Schema (draft-07) document.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/templates/{id}/sample":{"parameters":[{"$ref":"#/components/parameters/TemplateId"},{"name":"lang","in":"query","required":false,"description":"Sample language, e.g. `he`. Falls back to the English sample.","schema":{"type":"string","pattern":"^[a-z]{2}$"}}],"get":{"tags":["Templates"],"operationId":"getTemplateSample","summary":"Get sample data for a template","security":[],"responses":{"200":{"description":"Sample `data` that passes the template's schema.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/usage":{"get":{"tags":["Usage"],"operationId":"getUsage","summary":"Usage for the calling key's account","responses":{"200":{"description":"Plan, this month's usage, per-key usage and 12-month history.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/UsageSummary"},{"type":"object","required":["currentKey"],"properties":{"currentKey":{"type":"object","required":["id","prefix","name"],"properties":{"id":{"type":"string"},"prefix":{"type":"string"},"name":{"type":"string"}}}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/playground/preview":{"post":{"tags":["Playground"],"operationId":"playgroundPreview","summary":"Preview a built-in template (unmetered)","description":"Used by the template editor and the public gallery. Renders built-in templates only (optionally restyled);\nraw HTML and custom template code are refused. Rate-limited per IP; not counted against any quota.\n","security":[],"parameters":[{"name":"format","in":"query","required":false,"description":"`pdf` for a PDF; omit for a sandboxed HTML preview.","schema":{"type":"string","enum":["pdf"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaygroundRequest"}}}},"responses":{"200":{"description":"HTML preview or PDF.","headers":{"X-Pages":{"$ref":"#/components/headers/X-Pages"}},"content":{"text/html":{"schema":{"type":"string"}},"application/pdf":{"schema":{"type":"string","contentMediaType":"application/pdf"}}}},"404":{"$ref":"#/components/responses/NotFound"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/auth/signup":{"post":{"tags":["Account"],"operationId":"signup","summary":"Create an account","description":"Always answers 202 for a well-formed request so it cannot be used to discover registered emails. A confirmation link is emailed; the Free plan starts after confirmation.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credentials"}}}},"responses":{"202":{"$ref":"#/components/responses/Accepted"},"403":{"$ref":"#/components/responses/Forbidden"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/auth/resend":{"post":{"tags":["Account"],"operationId":"resendVerification","summary":"Resend the confirmation email","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailOnly"}}}},"responses":{"202":{"$ref":"#/components/responses/Accepted"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/auth/verify":{"post":{"tags":["Account"],"operationId":"verifyEmail","summary":"Confirm an email address","description":"The token comes from the emailed link (it travels in the URL fragment, so it never reaches server logs). Single use, valid for 24 hours.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenOnly"}}}},"responses":{"200":{"description":"Confirmed; the account is on the Free plan.","content":{"application/json":{"schema":{"type":"object","required":["ok","email","plan"],"properties":{"ok":{"const":true},"email":{"type":"string","format":"email"},"plan":{"const":"free"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/auth/login":{"post":{"tags":["Account"],"operationId":"login","summary":"Sign in","description":"Sets the `sid` session cookie (HttpOnly, SameSite=Strict, 14 days). Five failed attempts lock the account for 15 minutes.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credentials"}}}},"responses":{"200":{"description":"Signed in.","headers":{"Set-Cookie":{"description":"`sid=…; Path=/; HttpOnly; SameSite=Strict; Max-Age=1209600`","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["ok","email"],"properties":{"ok":{"const":true},"email":{"type":"string","format":"email"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/auth/logout":{"post":{"tags":["Account"],"operationId":"logout","summary":"Sign out","security":[{"sessionCookie":[]}],"responses":{"200":{"$ref":"#/components/responses/Ok"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/auth/forgot":{"post":{"tags":["Account"],"operationId":"forgotPassword","summary":"Request a password reset link","description":"Always answers 202 for a well-formed request. The link is single use and valid for 1 hour.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailOnly"}}}},"responses":{"202":{"$ref":"#/components/responses/Accepted"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/auth/reset":{"post":{"tags":["Account"],"operationId":"resetPassword","summary":"Set a new password with a reset token","description":"Signs out every session for the account.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","password"],"properties":{"token":{"type":"string","maxLength":100},"password":{"$ref":"#/components/schemas/Password"}}}}}},"responses":{"200":{"description":"Password changed.","content":{"application/json":{"schema":{"type":"object","required":["ok","email"],"properties":{"ok":{"const":true},"email":{"type":"string","format":"email"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/me":{"get":{"tags":["Account"],"operationId":"getMe","summary":"The signed-in user, plan and usage","security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Current user and account summary.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/UsageSummary"},{"type":"object","required":["user","maxActiveKeys","billing"],"properties":{"user":{"type":"object","required":["email","createdAt"],"properties":{"email":{"type":"string","format":"email"},"verifiedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"maxActiveKeys":{"type":"integer"},"billing":{"$ref":"#/components/schemas/Billing"}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/account/keys":{"get":{"tags":["Account"],"operationId":"listKeys","summary":"List API keys","security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Keys (active and revoked) with this month's usage.","content":{"application/json":{"schema":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Account"],"operationId":"createKey","summary":"Create an API key","description":"The full key is returned once. Up to 10 active keys per account.","security":[{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":60,"examples":["Production"]}}}}}},"responses":{"201":{"$ref":"#/components/responses/KeyCreated"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"}}}},"/v1/account/keys/{keyId}":{"parameters":[{"$ref":"#/components/parameters/KeyId"}],"delete":{"tags":["Account"],"operationId":"revokeKey","summary":"Revoke an API key","security":[{"sessionCookie":[]}],"responses":{"200":{"description":"Revoked. Requests with this key now fail with `key_revoked`.","content":{"application/json":{"schema":{"type":"object","required":["record"],"properties":{"record":{"$ref":"#/components/schemas/ApiKey"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/account/keys/{keyId}/rotate":{"parameters":[{"$ref":"#/components/parameters/KeyId"}],"post":{"tags":["Account"],"operationId":"rotateKey","summary":"Rotate an API key","description":"Creates a new key with the same name and revokes the old one immediately.","security":[{"sessionCookie":[]}],"responses":{"201":{"description":"The new key (shown once) and the revoked record.","content":{"application/json":{"schema":{"type":"object","required":["key","record","revoked","notice"],"properties":{"key":{"type":"string","pattern":"^pdfk_live_"},"record":{"$ref":"#/components/schemas/ApiKey"},"revoked":{"$ref":"#/components/schemas/ApiKey"},"notice":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/webhooks/lemonsqueezy":{"post":{"tags":["Webhooks"],"operationId":"lemonSqueezyWebhook","summary":"Lemon Squeezy subscription events","description":"Verifies `X-Signature` (HMAC-SHA256 hex of the raw body with the signing secret) and maps\n`subscription_*` events to plans. Answers 422 when the account or variant is not known yet, so the\nsender retries. Idempotent by webhook id.\n","security":[{"webhookSignature":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Received.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Received but not applied yet (unknown account or variant); retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResult"}}}}}}},"/v1/errors":{"get":{"tags":["Meta"],"operationId":"listErrors","summary":"Error reference","security":[],"responses":{"200":{"description":"Every error code with its HTTP status and default message.","content":{"application/json":{"schema":{"type":"object","propertyNames":{"$ref":"#/components/schemas/ErrorCode"},"additionalProperties":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer"},"message":{"type":"string"}}}}}}}}}},"/v1/brand":{"get":{"tags":["Meta"],"operationId":"getBrand","summary":"Brand constants","security":[],"responses":{"200":{"description":"Product name and public URLs.","content":{"application/json":{"schema":{"type":"object","required":["name","product","tagline"],"properties":{"name":{"type":"string","examples":["Ktavly"]},"product":{"type":"string"},"tagline":{"type":"string"},"siteUrl":{"type":"string","format":"uri"},"apiUrl":{"type":"string","format":"uri"}}}}}}}}},"/healthz":{"get":{"tags":["Meta"],"operationId":"health","summary":"Health check","security":[],"responses":{"200":{"description":"The service is up. Includes renderer queue statistics.","content":{"application/json":{"schema":{"type":"object","required":["ok","renderer"],"properties":{"ok":{"const":true},"renderer":{"type":"object","required":["active","waiting","concurrency"],"properties":{"active":{"type":"integer","minimum":0},"waiting":{"type":"integer","minimum":0},"concurrency":{"type":"integer","minimum":1},"rendersOnBrowser":{"type":"integer","minimum":0}}}}}}}}}}},"/openapi.json":{"get":{"tags":["Meta"],"operationId":"getOpenApiJson","summary":"This specification (JSON)","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/openapi.yaml":{"get":{"tags":["Meta"],"operationId":"getOpenApiYaml","summary":"This specification (YAML)","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document.","content":{"application/yaml":{"schema":{"type":"string"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"pdfk_live_…","description":"API key from the dashboard."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Alternative to the Authorization header."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"sid","description":"Dashboard session set by `POST /v1/auth/login`."},"webhookSignature":{"type":"apiKey","in":"header","name":"X-Signature","description":"HMAC-SHA256 hex digest of the raw request body using the webhook signing secret."}},"parameters":{"TemplateId":{"name":"id","in":"path","required":true,"description":"Template id.","schema":{"type":"string","examples":["invoice","invoice-he","receipt","report","certificate"]}},"KeyId":{"name":"keyId","in":"path","required":true,"description":"API key id (not the key itself).","schema":{"type":"string","pattern":"^[\\w-]{1,64}$"}}},"headers":{"X-Pages":{"description":"Number of pages in the PDF.","schema":{"type":"integer","minimum":1}},"X-Usage-Used":{"description":"Documents rendered this UTC calendar month (account-wide).","schema":{"type":"integer"}},"X-Usage-Limit":{"description":"Documents included in the plan per month.","schema":{"type":"integer"}},"X-Usage-Remaining":{"description":"Included documents left this month.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests per minute allowed for this key's plan.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-Request-Id":{"description":"Quote this when contacting support. It identifies timing and status, never content.","schema":{"type":"string","format":"uuid"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"responses":{"Ok":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"const":true}}}}}},"Accepted":{"description":"Accepted. The same answer is given whether or not the address is registered.","content":{"application/json":{"schema":{"type":"object","required":["ok","message"],"properties":{"ok":{"const":true},"message":{"type":"string"}}}}}},"KeyCreated":{"description":"The new key. Copy it now; it is not shown again.","content":{"application/json":{"schema":{"type":"object","required":["key","record","notice"],"properties":{"key":{"type":"string","pattern":"^pdfk_live_","examples":["pdfk_live_3x…"]},"record":{"$ref":"#/components/schemas/ApiKey"},"notice":{"type":"string"}}}}}},"BadRequest":{"description":"`invalid_json`, `missing_input` or `token_invalid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"`unauthorized`, `invalid_key`, `invalid_credentials`, `not_signed_in` or `webhook_signature_invalid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"QuotaExceeded":{"description":"`quota_exceeded`: the monthly limit was reached (Free plan, or overage not enabled).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"quota_exceeded","message":"Monthly document limit reached for your plan. Upgrade, or enable overage on a paid plan.","details":{"used":200,"limit":200,"resetsAt":"2026-11-01T00:00:00.000Z"},"requestId":"6f1c3e9a-4a35-4a0c-9d0e-2b7c5d3f8a10"}}}}},"Forbidden":{"description":"`key_revoked`, `email_not_verified` or `csrf_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"`not_found` or `template_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"MethodNotAllowed":{"description":"`method_not_allowed`.","headers":{"Allow":{"description":"The allowed method.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"`key_limit`: 10 active keys already exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"`payload_too_large`: body over 2 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnsupportedMediaType":{"description":"`unsupported_media_type`: send `Content-Type: application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationFailed":{"description":"`validation_failed`: `details` lists every problem.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"validation_failed","message":"The data or options did not pass validation. See \"details\".","details":["data must have required property 'items'"],"requestId":"6f1c3e9a-4a35-4a0c-9d0e-2b7c5d3f8a10"}}}}},"RateLimited":{"description":"`rate_limited` or `account_locked`.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"`render_failed` or `server_misconfigured`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerBusy":{"description":"`server_busy`: the render queue is full; your quota was not charged.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RenderTimeout":{"description":"`render_timeout`: rendering took longer than 30 s.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"ErrorCode":{"type":"string","enum":["invalid_json","missing_input","unauthorized","invalid_key","key_revoked","quota_exceeded","template_not_found","not_found","method_not_allowed","payload_too_large","unsupported_media_type","validation_failed","rate_limited","render_failed","render_timeout","webhook_signature_invalid","invalid_credentials","not_signed_in","email_not_verified","csrf_failed","token_invalid","key_limit","account_locked","server_misconfigured","server_busy"]},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","requestId"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string"},"details":{"description":"Extra context: a list of validation problems, or quota numbers."},"requestId":{"type":"string","format":"uuid"}}}}},"Hex":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},"Length":{"type":"string","pattern":"^\\d+(\\.\\d+)?(mm|cm|in|px)$","examples":["12mm","8.5in","800px"]},"FontId":{"type":"string","enum":["inter","heebo","frank-ruhl-libre","playfair-display","noto-sans-arabic","jetbrains-mono"]},"Styles":{"type":"object","additionalProperties":false,"properties":{"primaryColor":{"$ref":"#/components/schemas/Hex"},"accentColor":{"$ref":"#/components/schemas/Hex"},"textColor":{"$ref":"#/components/schemas/Hex"},"fontFamily":{"$ref":"#/components/schemas/FontId"},"headingFont":{"$ref":"#/components/schemas/FontId"},"direction":{"type":"string","enum":["ltr","rtl","auto"]},"locale":{"type":"string","description":"BCP 47 locale for numbers, currency and dates, e.g. `he-IL`. RTL languages flip the layout.","examples":["en-US","he-IL","ar"]},"logoUrl":{"type":"string","description":"An https URL (needs `allowRemoteAssets`) or a base64 `data:image/…` URL. Empty string removes the logo."},"accentStyle":{"type":"string","enum":["band","line","minimal"]},"density":{"type":"string","enum":["comfortable","compact"]}}},"Slot":{"oneOf":[{"type":"string","maxLength":500,"description":"Plain text (escaped). Placeholders: `{page}`, `{pages}`, `{title}`."},{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","maxLength":500},"html":{"type":"string","maxLength":5000},"align":{"type":"string","enum":["start","center","end"]}}}]},"RenderOptions":{"type":"object","additionalProperties":false,"properties":{"format":{"type":"string","enum":["A3","A4","A5","Letter","Legal","Tabloid"]},"width":{"$ref":"#/components/schemas/Length"},"height":{"$ref":"#/components/schemas/Length"},"landscape":{"type":"boolean"},"margin":{"oneOf":[{"$ref":"#/components/schemas/Length"},{"type":"object","additionalProperties":false,"properties":{"top":{"$ref":"#/components/schemas/Length"},"right":{"$ref":"#/components/schemas/Length"},"bottom":{"$ref":"#/components/schemas/Length"},"left":{"$ref":"#/components/schemas/Length"}}}]},"header":{"$ref":"#/components/schemas/Slot"},"footer":{"$ref":"#/components/schemas/Slot"},"pageNumbers":{"oneOf":[{"type":"boolean"},{"type":"object","additionalProperties":false,"properties":{"position":{"type":"string","enum":["header","footer"]},"align":{"type":"string","enum":["start","center","end"]},"format":{"type":"string","maxLength":100,"examples":["Page {page} of {pages}"]}}}]},"filename":{"type":"string","pattern":"^[\\p{L}\\p{N} ._()-]{1,120}$","description":"Sets Content-Disposition. `.pdf` is appended if missing."},"fonts":{"type":"array","items":{"$ref":"#/components/schemas/FontId"},"description":"Extra embedded fonts for raw HTML."},"allowRemoteAssets":{"type":"boolean","default":false,"description":"Allow public https images, fonts and stylesheets. Private and internal addresses are always blocked."}}},"InlineTemplate":{"type":"object","description":"A template exported from the editor (`base` + overrides), or custom Handlebars HTML + CSS + schema. Used for this one request and never stored.","additionalProperties":false,"properties":{"base":{"type":"string","description":"Built-in template id to start from."},"html":{"type":"string","maxLength":500000},"css":{"type":"string","maxLength":500000},"schema":{"type":"object","additionalProperties":true},"styles":{"$ref":"#/components/schemas/Styles"},"labels":{"type":"object","additionalProperties":{"type":"string","maxLength":200}},"page":{"type":"object","additionalProperties":true},"name":{"type":"string"}}},"RenderRequest":{"type":"object","additionalProperties":false,"description":"Send `template` (+ `data`) or `html`, not both.","properties":{"template":{"oneOf":[{"type":"string","description":"Built-in template id.","examples":["invoice","invoice-he","receipt","report","certificate"]},{"$ref":"#/components/schemas/InlineTemplate"}]},"data":{"type":"object","additionalProperties":true,"description":"Validated against the template's JSON Schema."},"html":{"type":"string","description":"A full HTML document or a fragment. JavaScript does not run."},"styles":{"$ref":"#/components/schemas/Styles"},"options":{"$ref":"#/components/schemas/RenderOptions"}}},"RenderJsonResponse":{"type":"object","required":["filename","pages","bytes","contentType","base64"],"properties":{"filename":{"type":"string"},"pages":{"type":"integer","minimum":1},"bytes":{"type":"integer"},"contentType":{"const":"application/pdf"},"base64":{"type":"string","contentEncoding":"base64"}}},"PlaygroundRequest":{"type":"object","required":["template"],"properties":{"template":{"oneOf":[{"type":"string"},{"type":"object","required":["base"],"properties":{"base":{"type":"string"},"styles":{"$ref":"#/components/schemas/Styles"},"labels":{"type":"object","additionalProperties":{"type":"string"}},"page":{"type":"object","additionalProperties":true}}}]},"data":{"type":"object","additionalProperties":true},"styles":{"$ref":"#/components/schemas/Styles"},"options":{"$ref":"#/components/schemas/RenderOptions"}}},"PageSetup":{"type":"object","additionalProperties":true,"properties":{"format":{"type":"string"},"landscape":{"type":"boolean"},"margin":{"type":"object","additionalProperties":{"type":"string"}}}},"TemplateSummary":{"type":"object","required":["id","name","description","page","languages","samples"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"page":{"$ref":"#/components/schemas/PageSetup"},"languages":{"type":"array","items":{"type":"string"},"description":"Label packs available."},"samples":{"type":"array","items":{"type":"string"},"description":"Sample data languages."}}},"TemplateList":{"type":"object","required":["templates","fonts","formats"],"properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/TemplateSummary"}},"fonts":{"type":"array","items":{"type":"object","required":["id","label","scripts"],"properties":{"id":{"$ref":"#/components/schemas/FontId"},"label":{"type":"string"},"scripts":{"type":"array","items":{"type":"string"}}}}},"formats":{"type":"array","items":{"type":"string"}}}},"TemplateDetail":{"type":"object","required":["id","name","description","page","styles","labels","schema","sample","samples"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"page":{"$ref":"#/components/schemas/PageSetup"},"styles":{"type":"object","additionalProperties":true},"labels":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"string"}}},"schema":{"type":"object","additionalProperties":true},"sample":{"type":"object","additionalProperties":true},"samples":{"type":"object","additionalProperties":{"type":"object","additionalProperties":true}}}},"ApiKey":{"type":"object","required":["id","name","prefix","createdAt","revoked"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string","description":"First characters of the key, for recognising it."},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"},"revoked":{"type":"boolean"},"lastUsedMonth":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}$"},"used":{"type":"integer","minimum":0,"description":"Documents this month with this key."},"replacedBy":{"type":["string","null"],"description":"Id of the key that replaced this one after a rotation."}}},"UsageSummary":{"type":"object","required":["account","period","usage","keys","history"],"properties":{"account":{"type":"object","required":["id","plan","planName","overageEnabled"],"properties":{"id":{"type":"string"},"plan":{"type":"string","enum":["free","starter","growth","scale"]},"planName":{"type":"string"},"overageEnabled":{"type":"boolean"},"subscriptionStatus":{"type":["string","null"]}}},"period":{"type":"object","required":["month","resetsAt"],"properties":{"month":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"resetsAt":{"type":"string","format":"date-time"}}},"usage":{"type":"object","required":["used","limit","remaining","overage","ratePerMinute"],"properties":{"used":{"type":"integer","minimum":0},"limit":{"type":"integer","minimum":0},"remaining":{"type":"integer","minimum":0},"overage":{"type":"integer","minimum":0},"overageRateUsd":{"type":["number","null"]},"ratePerMinute":{"type":"integer"}}},"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"history":{"type":"array","items":{"type":"object","required":["month","count"],"properties":{"month":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"count":{"type":"integer","minimum":0}}}}}},"Billing":{"type":"object","description":"Lemon Squeezy hosted checkout and customer-portal links for the signed-in account. Off until paid checkout opens (checkoutEnabled false, no links). Checkout URLs are prefilled with the account email and carry the account id as custom data. Subscribers get the portal link (plan changes and cancellation) instead of upgrades.","required":["checkoutEnabled","testMode","upgrades","portalUrl"],"properties":{"checkoutEnabled":{"type":"boolean"},"testMode":{"type":"boolean","description":"Links point to Lemon Squeezy test-mode products."},"upgrades":{"type":"array","items":{"type":"object","required":["plan","name","priceUsd","docsPerMonth","url"],"properties":{"plan":{"type":"string","enum":["starter","growth","scale"]},"name":{"type":"string"},"priceUsd":{"type":"number"},"docsPerMonth":{"type":"integer"},"url":{"type":"string","format":"uri","example":"https://ktavly.lemonsqueezy.com/checkout/buy/0f1e2d3c-aaaa-bbbb-cccc-1234567890ab?checkout%5Bemail%5D=ada%40example.com&checkout%5Bcustom%5D%5Baccount_id%5D=acct_123"}}}},"portalUrl":{"type":["string","null"],"format":"uri","example":"https://ktavly.lemonsqueezy.com/billing"}}},"Credentials":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email","maxLength":254},"password":{"$ref":"#/components/schemas/Password"}}},"Password":{"type":"string","minLength":10,"maxLength":256,"description":"At least 10 characters; common passwords and ones containing your email name are refused."},"EmailOnly":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","maxLength":254}}},"TokenOnly":{"type":"object","required":["token"],"properties":{"token":{"type":"string","maxLength":100}}},"WebhookResult":{"type":"object","required":["received"],"properties":{"received":{"const":true},"handled":{"type":"boolean"},"event":{"type":"string"},"reason":{"type":"string"}},"additionalProperties":true}}}}