{"openapi":"3.1.0","info":{"title":"mockmsg API","description":"Accounts, prepaid balances, threads and exports for mockmsg renders","license":{"name":""},"version":"1.0.0"},"paths":{"/api-keys":{"get":{"tags":["auth"],"operationId":"list","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyMetadata"}}}}},"401":{"description":""},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}}}},"post":{"tags":["auth"],"operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKey"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiKey"}}}},"401":{"description":""},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}}}}},"/api-keys/{id}":{"delete":{"tags":["auth"],"operationId":"revoke","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":""},"401":{"description":""},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}}}}},"/auth/code/request":{"post":{"tags":["auth"],"operationId":"request_code","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestCodeRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"413":{"description":"Request body too large"}}}},"/auth/code/verify":{"post":{"tags":["auth"],"operationId":"verify_code","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyCodeRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"410":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"413":{"description":"Request body too large"},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/auth/logout":{"post":{"tags":["auth"],"operationId":"logout","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"413":{"description":"Request body too large"},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/balance":{"get":{"tags":["balance"],"operationId":"get_balance","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceError"}}}}}}},"/balance/topups":{"post":{"tags":["balance"],"operationId":"create_topup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTopupRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceError"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"413":{"description":"Request body too large"},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceError"}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceError"}}}}}}},"/exports":{"get":{"tags":["exports"],"operationId":"list_exports","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExportResponse"}}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}}}},"post":{"tags":["exports"],"summary":"Idempotency-Key is scoped to the user and exact thread_id/snapshot pair, not thread contents.\nReplays return Idempotency-Replayed: true, including after thread edits or render failure.\nPre-creation failures do not consume the key. Use a new key for fresh work after a render failure.","description":"Idempotency-Key is scoped to the user and exact thread_id/snapshot pair, not thread contents. Replays return Idempotency-Replayed: true, including after thread edits or render failure. Pre-creation failures do not consume the key. Use a new key for fresh work after a render failure.","operationId":"create_export","parameters":[{"name":"Idempotency-Key","in":"header","description":"Optional nonempty key, at most 255 bytes. Different thread_id/snapshot returns 409.","required":false,"schema":{"type":["string","null"]}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExportRequest"}}},"required":true},"responses":{"200":{"description":"","headers":{"Idempotency-Replayed":{"schema":{"type":"string"},"description":"true for a replay, false for a new purchase"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"402":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"409":{"description":"Idempotency key used for a different request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"413":{"description":"Request body too large"},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}}}}},"/exports/quote":{"post":{"tags":["exports"],"description":"Issue a free, single-use quote valid for fifteen minutes. Purchase requires quote_id. QUOTE_REQUIRED and QUOTE_NOT_YOURS return 422 (missing or unresolvable input, message: Quote not found). QUOTE_EXPIRED and QUOTE_STALE return 409 (expired, changed or consumed state).","operationId":"create_quote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}}}}},"/exports/{id}":{"get":{"tags":["exports"],"operationId":"get_export","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}}}}},"/health":{"get":{"tags":["health"],"operationId":"health","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/me":{"get":{"tags":["auth"],"operationId":"me","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}}}}},"/oauth/device/code":{"post":{"tags":["Device authorization"],"summary":"Issue a device code (JSON only). Allowed scopes: render:read, balance:read, export:write; request at least one. Limited to 10 requests/minute per IP. invalid_scope covers invalid scopes or absent Content-Type; wrong Content-Type returns 415, malformed JSON returns 400 or 422 as framework plain text; retry temporarily_unavailable with backoff. Responses are no-store.","operationId":"authorize","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceCodeResponse"}}}},"400":{"description":"OAuth JSON error or framework plain-text syntax rejection"},"413":{"description":"Body exceeds 4096 bytes","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Wrong Content-Type","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Invalid JSON fields","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}}}},"/oauth/device/decision":{"post":{"tags":["Device authorization"],"summary":"Browser session and matching Origin required. Omit approve to inspect scopes; otherwise approve or deny. Limited to 10 requests/minute per user.","operationId":"decide","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Decision"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"401":{"description":"Browser session required; JSON auth error with code, message and context"},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"413":{"description":"Body exceeds 4096 bytes","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"JSON Content-Type required"},"422":{"description":"Malformed JSON fields","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"500":{"description":"Internal OAuth or session-auth JSON error"}}}},"/oauth/device/revoke-all":{"post":{"tags":["Device authorization"],"summary":"Account owners can withdraw device access without possessing the agent's secret. Browser session and matching Origin required; limited to 10 requests/minute per user.","operationId":"revoke_all","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"401":{"description":"Browser session required; JSON auth error with code, message and context"},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"429":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"500":{"description":"Internal OAuth or session-auth JSON error"}}}},"/oauth/revoke":{"post":{"tags":["Device authorization"],"summary":"Possession authorizes revocation; unknown and already revoked tokens are indistinguishable. Limited to 30 requests/minute per IP.","operationId":"revoke_device_token","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/RevokeRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"413":{"description":"Body exceeds 4096 bytes","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}}}},"/oauth/token":{"post":{"tags":["Device authorization"],"summary":"Poll at the issued interval (initially five seconds). authorization_pending means wait; slow_down means add five seconds. Stop on access_denied, expired_token or invalid_grant. grant_type must be urn:ietf:params:oauth:grant-type:device_code, otherwise unsupported_grant_type. Limited to 60 requests/minute per IP. Tokens expire in 3600 seconds; no refresh token. Responses are no-store.","operationId":"poll","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"413":{"description":"Body exceeds 4096 bytes","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}}}},"/preview":{"post":{"tags":["preview"],"operationId":"preview","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewRequest"}}},"required":true},"responses":{"200":{"description":"Spoiled WebP preview","content":{"image/webp":{"schema":{"type":"string"}}}},"413":{"description":"Request body too large"},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}}}},"429":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}}}}}}},"/stripe/webhook":{"post":{"tags":["stripe"],"operationId":"stripe_webhook","requestBody":{"content":{"application/json":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"description":""},"400":{"description":""},"413":{"description":"Request body too large"},"500":{"description":""}}}},"/threads":{"get":{"tags":["threads"],"operationId":"list_threads","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ThreadResponse"}}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}}}},"post":{"tags":["threads"],"operationId":"create_thread","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveThreadRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"413":{"description":"Request body too large"},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}}}}},"/threads/{id}":{"get":{"tags":["threads"],"operationId":"get_thread","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}}}},"put":{"tags":["threads"],"operationId":"update_thread","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveThreadRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"413":{"description":"Request body too large"},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}}}},"delete":{"tags":["threads"],"operationId":"delete_thread","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"401":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthError"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}}}}},"/v1/balance":{"get":{"tags":["balance"],"description":"Bearer API key or device access token required. Required scope: balance:read. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_get_balance","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceResponse"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/BalanceError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}}},"security":[{"bearerKey":[]}]}},"/v1/exports":{"get":{"tags":["exports"],"description":"Bearer API key or device access token required. Required scope: export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_list_exports","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExportResponse"}}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExportError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}}},"security":[{"bearerKey":[]}]},"post":{"tags":["exports"],"summary":"Idempotency-Key is scoped to the user and exact thread_id/snapshot pair, not thread contents.\nReplays return Idempotency-Replayed: true, including after thread edits or render failure.\nPre-creation failures do not consume the key. Use a new key for fresh work after a render failure.","description":"Bearer API key or device access token required. Required scope: export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. Idempotency-Key is scoped to the user and exact thread_id/snapshot pair, not thread contents. Replays return Idempotency-Replayed: true, including after thread edits or render failure. Pre-creation failures do not consume the key. Use a new key for fresh work after a render failure.","operationId":"v1_create_export","parameters":[{"name":"Idempotency-Key","in":"header","description":"Optional nonempty key, at most 255 bytes. Different thread_id/snapshot returns 409.","required":false,"schema":{"type":["string","null"]}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExportRequest"}}},"required":true},"responses":{"200":{"description":"","headers":{"Idempotency-Replayed":{"schema":{"type":"string"},"description":"true for a replay, false for a new purchase"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}},"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"402":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExportError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"409":{"description":"Idempotency key used for a different request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"413":{"description":"Request body too large","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}},"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExportError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}}},"security":[{"bearerKey":[]}]}},"/v1/exports/quote":{"post":{"tags":["exports"],"description":"Bearer API key or device access token required. Required scope: export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. Issue a free, single-use quote valid for fifteen minutes. Purchase requires quote_id. QUOTE_REQUIRED and QUOTE_NOT_YOURS return 422 (missing or unresolvable input, message: Quote not found). QUOTE_EXPIRED and QUOTE_STALE return 409 (expired, changed or consumed state).","operationId":"v1_create_quote","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"400":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExportError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"413":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}},"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExportError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}}},"security":[{"bearerKey":[]}]}},"/v1/exports/{id}":{"get":{"tags":["exports"],"description":"Bearer API key or device access token required. Required scope: export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_get_export","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}}}},"400":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExportError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportError"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ExportError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}}},"security":[{"bearerKey":[]}]}},"/v1/preview":{"post":{"tags":["preview"],"description":"Bearer API key or device access token required. Required scope: render:read. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_bearer_preview","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewRequest"}}},"required":true},"responses":{"200":{"description":"Spoiled WebP preview","content":{"image/webp":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"413":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}},"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewError"}}}}},"security":[{"bearerKey":[]}]}},"/v1/threads":{"get":{"tags":["threads"],"description":"Bearer API key or device access token required. Required scope: render:read OR export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_list_threads","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ThreadResponse"}}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}}},"security":[{"bearerKey":[]}]},"post":{"tags":["threads"],"description":"Bearer API key or device access token required. Required scope: export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_create_thread","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveThreadRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadResponse"}}}},"400":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"413":{"description":"Request body too large","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}},"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}}},"security":[{"bearerKey":[]}]}},"/v1/threads/{id}":{"get":{"tags":["threads"],"description":"Bearer API key or device access token required. Required scope: render:read OR export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_get_thread","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadResponse"}}}},"400":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}}},"security":[{"bearerKey":[]}]},"put":{"tags":["threads"],"description":"Bearer API key or device access token required. Required scope: export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_update_thread","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveThreadRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadResponse"}}}},"400":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"413":{"description":"Request body too large","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}},"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"502":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"503":{"description":"RENDER_BUSY: render capacity is full; retry after context.retry_after_seconds seconds (also supplied in Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying; equals context.retry_after_seconds"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}}},"security":[{"bearerKey":[]}]},"delete":{"tags":["threads"],"description":"Bearer API key or device access token required. Required scope: export:write. Limits: 60 requests per 60 seconds per credential AND per user across credentials. Retry-After is supplied for 503 RENDER_BUSY render-capacity refusals, with the same seconds in context.retry_after_seconds. No Retry-After header is supplied for 429 rate-limit responses. ","operationId":"v1_delete_thread","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"Framework request rejection","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing, invalid or revoked bearer key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"403":{"description":"Insufficient scope or resource ownership","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadError"}}}},"429":{"description":"Per-credential or aggregate per-user rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyError"}}}},"500":{"description":"Internal error in authentication or handler","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ThreadError"},{"$ref":"#/components/schemas/ApiKeyError"}]}}}}},"security":[{"bearerKey":[]}]}}},"components":{"schemas":{"ApiKeyError":{"type":"object","required":["code"],"properties":{"code":{"type":"string"}}},"ApiKeyMetadata":{"type":"object","required":["id","name","scopes","created_at"],"properties":{"created_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"last_used_at":{"type":["string","null"],"format":"date-time","description":"Last authenticated use, sampled at most once per hour; null until first use."},"name":{"type":"string"},"revoked_at":{"type":["string","null"],"format":"date-time"},"scopes":{"type":"array","items":{"type":"string"}}}},"AuthError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"context":{"type":["string","null"]},"message":{"type":"string"}}},"AuthorizationRequest":{"type":"object","required":["scopes"],"properties":{"scopes":{"type":["array","null"],"items":{"type":"string"}}},"additionalProperties":false},"BalanceEntryResponse":{"type":"object","required":["kind","amount_cents","created_at"],"properties":{"amount_cents":{"type":"integer","format":"int64"},"created_at":{"type":"string","format":"date-time"},"export_id":{"type":["string","null"],"format":"uuid"},"kind":{"type":"string"},"topup_id":{"type":["string","null"],"format":"uuid"}}},"BalanceError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"context":{"type":["string","null"]},"message":{"type":"string"}}},"BalanceResponse":{"type":"object","required":["balance_cents","max_balance_cents","min_topup_cents","bonus_threshold_cents","bonus_percent","entries","lots"],"properties":{"balance_cents":{"type":"integer","format":"int64"},"bonus_percent":{"type":"integer","format":"int64"},"bonus_threshold_cents":{"type":"integer","format":"int64"},"entries":{"type":"array","items":{"$ref":"#/components/schemas/BalanceEntryResponse"}},"lots":{"type":"array","items":{"$ref":"#/components/schemas/CreditLotResponse"}},"max_balance_cents":{"type":"integer","format":"int64"},"min_topup_cents":{"type":"integer","format":"int64"}}},"CheckoutResponse":{"type":"object","required":["checkout_url"],"properties":{"checkout_url":{"type":"string"}}},"CreateApiKey":{"type":"object","required":["name","scopes"],"properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},"CreateExportRequest":{"type":"object","required":["thread_id","snapshot","quote_id"],"properties":{"quote_id":{"type":["string","null"],"format":"uuid","description":"Required for a new purchase; omitted only on idempotent replay."},"snapshot":{"type":"string"},"thread_id":{"type":"string","format":"uuid"}}},"CreateTopupRequest":{"type":"object","required":["amount_cents"],"properties":{"amount_cents":{"type":"integer","format":"int64"}}},"CreatedApiKey":{"type":"object","required":["key","secret"],"properties":{"key":{"$ref":"#/components/schemas/ApiKeyMetadata"},"secret":{"type":"string","description":"Shown only in the creation response; cannot be recovered later."}}},"CreditLotResponse":{"type":"object","required":["credit_entry_id","remaining_cents","lapses_at"],"properties":{"credit_entry_id":{"type":"string","format":"uuid"},"lapses_at":{"type":"string","format":"date-time"},"remaining_cents":{"type":"integer","format":"int64"}}},"Decision":{"type":"object","required":["user_code"],"properties":{"approve":{"type":["boolean","null"]},"user_code":{"type":"string"}},"additionalProperties":false},"DecisionResponse":{"oneOf":[{"type":"object","required":["scopes"],"properties":{"scopes":{"type":"array","items":{"type":"string"}}}},{"$ref":"#/components/schemas/OkResponse"}]},"DeviceCodeResponse":{"type":"object","required":["device_code","user_code","verification_uri","expires_in","interval"],"properties":{"device_code":{"type":"string"},"expires_in":{"type":"integer","format":"int32","minimum":0},"interval":{"type":"integer","format":"int32","minimum":0},"user_code":{"type":"string"},"verification_uri":{"type":"string"}}},"ExportError":{"type":"object","required":["code","message"],"properties":{"balance_cents":{"type":["integer","null"],"format":"int64"},"code":{"type":"string","description":"Quote refusals: QUOTE_REQUIRED (422), QUOTE_EXPIRED (409),\nQUOTE_NOT_YOURS (422, quote not found), QUOTE_STALE (409, including consumed quotes)."},"context":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RenderErrorContext"}]},"error_id":{"type":["string","null"],"format":"uuid","description":"Present on internal failures only: quote it to support; the detail is in the server log."},"message":{"type":"string"},"required_cents":{"type":["integer","null"],"format":"int64"},"shortfall_cents":{"type":["integer","null"],"format":"int64"}}},"ExportResponse":{"type":"object","required":["id","thread_id","snapshot","status","urls","created_at","updated_at"],"properties":{"created_at":{"type":"string","format":"date-time"},"error":{"type":["string","null"]},"id":{"type":"string","format":"uuid"},"photo":{"type":["boolean","null"]},"price_cents":{"type":["integer","null"],"format":"int64"},"screens":{"type":["integer","null"],"format":"int32"},"snapshot":{"type":"string"},"status":{"type":"string"},"thread_id":{"type":"string","format":"uuid"},"updated_at":{"type":"string","format":"date-time"},"urls":{}}},"HealthResponse":{"type":"object","description":"Public and unauthenticated, so status only. Worker detail (errors, its\nloopback URL, bundle paths and timings) goes to the server log instead.","required":["server","database","worker"],"properties":{"database":{"type":"string"},"server":{"type":"string"},"worker":{"$ref":"#/components/schemas/WorkerHealth"}}},"MeResponse":{"type":"object","required":["id","email"],"properties":{"email":{"type":"string"},"id":{"type":"string","format":"uuid"}}},"OAuthError":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"OkResponse":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}},"PreviewError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"context":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RenderErrorContext"}]},"error_id":{"type":["string","null"],"format":"uuid","description":"Present on internal failures only: quote it to support; the detail is in the server log."},"message":{"type":"string"}}},"PreviewRequest":{"type":"object","required":["input","snapshot"],"properties":{"input":{},"snapshot":{}}},"QuoteRequest":{"type":"object","required":["thread_id","snapshot"],"properties":{"snapshot":{"type":"string"},"thread_id":{"type":"string","format":"uuid"}}},"QuoteResponse":{"type":"object","required":["quote_id","price_cents","screens","expires_at","schema_id"],"properties":{"expires_at":{"type":"string","format":"date-time","description":"RFC 3339 timestamp, exactly fifteen minutes after issue."},"price_cents":{"type":"integer","format":"int64"},"quote_id":{"type":"string","format":"uuid"},"schema_id":{"type":"string"},"screens":{"type":"integer","format":"int32"}}},"RenderErrorContext":{"oneOf":[{"type":"string"},{"type":"object","required":["retry_after_seconds"],"properties":{"retry_after_seconds":{"type":"integer","format":"int64","minimum":0}}}],"description":"Error context is a legacy string or structured render admission retry advice."},"RequestCodeRequest":{"type":"object","required":["email"],"properties":{"email":{"type":"string"}}},"RevokeRequest":{"type":"object","required":["token"],"properties":{"token":{"type":"string"}}},"SaveThreadRequest":{"type":"object","required":["title","input"],"properties":{"input":{},"title":{"type":"string"}}},"ThreadError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"context":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RenderErrorContext"}]},"error_id":{"type":["string","null"],"format":"uuid","description":"Present on internal failures only: quote it to support; the detail is in the server log."},"message":{"type":"string"}}},"ThreadResponse":{"type":"object","required":["id","title","input","created_at","updated_at"],"properties":{"created_at":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"input":{},"title":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}}},"TokenRequest":{"type":"object","required":["grant_type","device_code"],"properties":{"device_code":{"type":"string"},"grant_type":{"type":"string"}}},"TokenResponse":{"type":"object","required":["access_token","token_type","expires_in","scope"],"properties":{"access_token":{"type":"string"},"expires_in":{"type":"integer","format":"int32","minimum":0},"scope":{"type":"string"},"token_type":{"type":"string"}}},"VerifyCodeRequest":{"type":"object","required":["email","code"],"properties":{"code":{"type":"string"},"email":{"type":"string"}}},"WorkerHealth":{"type":"object","required":["ok","browserAlive"],"properties":{"browserAlive":{"type":"boolean"},"ok":{"type":"boolean"}}}},"securitySchemes":{"bearerKey":{"type":"http","scheme":"bearer"}}},"tags":[{"name":"auth"},{"name":"balance"},{"name":"threads"},{"name":"preview"},{"name":"exports"},{"name":"stripe"},{"name":"health"}]}