{"openapi":"3.0.3","info":{"title":"LCAI Consumer API","description":"HTTP API for consumer auth, chat compatibility endpoints, gateway proxying, and protocol indexer access. The Developer API (the OpenAI-compatible /v1 routes, keyed by API keys) is under the Developer API and API keys tags.","version":"0.1.0"},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"The sign-in token from POST /api/auth/verify."},"apiKeyAuth":{"type":"http","scheme":"bearer","description":"A Developer API key (lcai_...), minted with POST /api/api-keys."}},"schemas":{}},"paths":{"/health":{"get":{"summary":"Liveness check","tags":["Health"],"description":"Returns service liveness for container/runtime health checks.","responses":{"200":{"description":"Default Response"}}}},"/ready":{"get":{"summary":"Readiness check","tags":["Health"],"description":"Returns readiness signal for orchestration and traffic routing.","responses":{"200":{"description":"Default Response"}}}},"/api/models":{"get":{"summary":"List available models","tags":["Gateway"],"description":"Returns configured model registry for client-side selection.","responses":{"200":{"description":"Default Response"}}}},"/api/models/{modelId}/capabilities":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"modelId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/sessions/select":{"post":{"summary":"Select gateway session","tags":["Gateway"],"description":"Proxies dispatcher session selection with gateway proxy-auth validation.","responses":{"200":{"description":"Default Response"}}}},"/api/sessions/prepare":{"post":{"summary":"Prepare gateway session","tags":["Gateway"],"description":"Proxies dispatcher session preparation with encrypted worker/disputer keys.","responses":{"200":{"description":"Default Response"}}}},"/api/sessions/{id}/token":{"get":{"summary":"Get session token","tags":["Gateway"],"description":"Fetches dispatcher token for a prepared session id.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/blobs":{"post":{"summary":"Submit blob payload","tags":["Gateway"],"description":"Splits and submits base64 payload as one or more chain blobs under lock protection.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/auth/challenge":{"get":{"summary":"Create SIWE challenge","tags":["Auth"],"description":"Returns EIP-4361 challenge payload (message + nonce) for wallet signing.","responses":{"200":{"description":"Default Response"}}}},"/api/auth/nonce":{"get":{"summary":"Get SIWE nonce","tags":["Auth"],"description":"Returns a nonce-only SIWE compatibility response.","responses":{"200":{"description":"Default Response"}}}},"/api/auth/verify":{"post":{"summary":"Verify SIWE signature","tags":["Auth"],"description":"Verifies signed SIWE message and returns a dispatcher-issued consumer bearer token.","responses":{"200":{"description":"Default Response"}}}},"/api/auth/refresh":{"post":{"responses":{"200":{"description":"Default Response"}}}},"/api/auth/csrf":{"get":{"summary":"Get CSRF token","tags":["Auth"],"description":"Returns NextAuth-compatible CSRF token payload.","responses":{"200":{"description":"Default Response"}}}},"/api/auth/session":{"get":{"summary":"Get current session","tags":["Auth"],"description":"Returns NextAuth-compatible session shape when bearer token is valid, otherwise null.","responses":{"200":{"description":"Default Response"}}}},"/api/auth/signout":{"post":{"summary":"Sign out","tags":["Auth"],"description":"Returns NextAuth-compatible sign-out payload contract.","responses":{"200":{"description":"Default Response"}}}},"/api/chat/{id}":{"get":{"summary":"Get chat by id","tags":["Chat"],"description":"Returns chat metadata and active protocol-session binding for the given chat id.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat/{id}/protocol-session":{"put":{"summary":"Bind protocol session to chat","tags":["Protocol"],"description":"Registers active on-chain session/model for a chat. Idempotent for already-active bindings.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat":{"post":{"summary":"Create chat","tags":["Chat"],"description":"Creates a chat and first user message in one request.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"patch":{"summary":"Update chat title","tags":["Chat"],"description":"Updates a chat title via query parameter `id`.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"delete":{"summary":"Delete chat","tags":["Chat"],"description":"Deletes a single chat via query parameter `id`.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/history":{"get":{"summary":"List chat history","tags":["Chat"],"description":"Lists authenticated user chats with cursor pagination via `starting_after` / `ending_before`. This endpoint remains part of the active client contract.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"delete":{"summary":"Delete all user chats","tags":["Chat"],"description":"Deletes all chats belonging to the authenticated user. This endpoint remains part of the active client contract.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat/{id}/messages":{"post":{"summary":"Persist chat message","tags":["Chat"],"description":"Stores a message for protocol and non-streaming paths; creates chat on-demand if missing.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}},"get":{"summary":"List chat messages","tags":["Chat"],"description":"Returns all persisted messages for a chat, ordered by creation time.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat/{id}/stream":{"get":{"summary":"Resume assistant stream","tags":["Chat"],"description":"SSE compatibility endpoint that replays last assistant message for a chat.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/search":{"get":{"summary":"Search messages","tags":["Chat"],"description":"Performs full-text message search scoped to authenticated user chats.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat/{id}/export":{"get":{"summary":"Export chat","tags":["Backup"],"description":"Exports chat and message history as JSON attachment.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat/import":{"post":{"summary":"Import chat","tags":["Backup"],"description":"Imports chat/messages from JSON payload and creates a new chat record.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat/restore":{"post":{"summary":"Restore chat from IPFS","tags":["Backup"],"description":"Restores chat backup by CID, decrypting private backups with wallet-derived key when needed.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/chat/{id}/backup":{"get":{"summary":"Get backup status","tags":["Backup"],"description":"Returns backup metadata, CID, timestamp, and encryption status for a chat.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}},"post":{"summary":"Backup chat to IPFS","tags":["Backup"],"description":"Uploads chat backup to IPFS and stores CID metadata in chat record.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/indexer/models":{"get":{"summary":"List indexer models","tags":["Indexer"],"description":"Returns whitelisted models from worker indexer.","responses":{"200":{"description":"Default Response"}}}},"/api/indexer/models/{modelId}":{"get":{"summary":"Get model details","tags":["Indexer"],"description":"Returns a specific whitelisted model from worker indexer.","parameters":[{"schema":{"type":"string"},"in":"path","name":"modelId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/sessions/{id}":{"get":{"summary":"Get session details","tags":["Indexer"],"description":"Returns indexer-backed protocol session data for a session id.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/sessions/{id}/jobs":{"get":{"summary":"List session jobs","tags":["Indexer"],"description":"Returns paginated jobs for a protocol session from worker indexer.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/jobs":{"get":{"summary":"List user jobs","tags":["Indexer"],"description":"Builds a jobs dashboard feed by combining local chat messages with indexer state.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/jobs/{id}":{"get":{"summary":"Get job details","tags":["Indexer"],"description":"Returns worker indexer details for a specific job id.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/jobs/{id}/dispute":{"get":{"summary":"Get dispute details","tags":["Indexer"],"description":"Returns dispute record tied to a job id when available.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/workers/availability":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/api/workers/{address}":{"get":{"summary":"Get worker profile","tags":["Indexer"],"description":"Returns worker metadata and supported model list from worker indexer.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"address","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/vote":{"get":{"summary":"List message votes","tags":["Feedback"],"description":"List message votes keyed by `chatId` query parameter.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"patch":{"summary":"Upsert message vote","tags":["Feedback"],"description":"Upsert message vote using `type: up|down` payload contract.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/prompts":{"get":{"summary":"List prompts","tags":["Prompts"],"description":"Returns built-in presets merged with user-created custom prompts.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"post":{"summary":"Create prompt","tags":["Prompts"],"description":"Creates a prompt template; accepts either name/prompt or title/content fields.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}},"delete":{"summary":"Delete prompt","tags":["Prompts"],"description":"Delete prompt by query id. Preset prompts cannot be deleted.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/api/sessions/{id}/messages":{"post":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/balance":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/api/sessions/sortition/request":{"post":{"responses":{"200":{"description":"Default Response"}}}},"/api/sessions/sortition/{reqId}/keys":{"post":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"reqId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/api-keys":{"post":{"operationId":"createApiKey","summary":"Mint an API key","tags":["API keys"],"description":"Mints a key bound to the signed-in wallet. The response is the only one that ever carries the key. Fields other than name and spendCapWei are ignored.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64},"spendCapWei":{"description":"Lifetime spend cap, in wei; none if omitted.","type":"string","pattern":"^[1-9][0-9]{0,77}$"}}}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The key, shown this once.","content":{"application/json":{"schema":{"type":"object","required":["id","prefix","name","spendCapWei","spentWei","limitHits","createdAt","revokedAt","key"],"properties":{"id":{"type":"string","format":"uuid"},"prefix":{"type":"string","description":"The start of the key, to recognise it by."},"name":{"type":"string","nullable":true},"spendCapWei":{"type":"string","pattern":"^[0-9]+$","description":"Lifetime spend cap; null is uncapped.","nullable":true},"spentWei":{"type":"string","pattern":"^[0-9]+$","description":"Spent so far, counted against spendCapWei."},"limitHits":{"type":"object","description":"How often each limit has refused the key.","required":["rate","concurrency","spendCap","sessionOpens"],"properties":{"rate":{"type":"integer"},"concurrency":{"type":"integer"},"spendCap":{"type":"integer"},"sessionOpens":{"type":"integer"}}},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":"string","format":"date-time","nullable":true},"key":{"type":"string","description":"The API key (lcai_...)."}},"description":"The key, shown this once."}}}},"400":{"description":"The request is invalid (error validation_error), or its body is not JSON (error Bad Request).","content":{"application/json":{"schema":{"description":"The request is invalid (error validation_error), or its body is not JSON (error Bad Request).","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"401":{"description":"Missing or expired sign-in token.","content":{"application/json":{"schema":{"description":"Missing or expired sign-in token.","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"409":{"description":"The wallet holds as many active keys as it may (error api_key_limit); revoke one first.","content":{"application/json":{"schema":{"description":"The wallet holds as many active keys as it may (error api_key_limit); revoke one first.","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"429":{"description":"Too many calls from this wallet (60 a minute).","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until the same call may pass."}},"content":{"application/json":{"schema":{"description":"Too many calls from this wallet (60 a minute).","type":"object","required":["statusCode","error","retryAfterSec"],"properties":{"statusCode":{"type":"integer","enum":[429]},"error":{"type":"string","enum":["rate_limited"]},"retryAfterSec":{"type":"integer"}}}}}}}},"get":{"operationId":"listApiKeys","summary":"List API keys","tags":["API keys"],"description":"Every key the signed-in wallet has minted, revoked ones included, newest first. Never the keys themselves.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The wallet's keys.","content":{"application/json":{"schema":{"description":"The wallet's keys.","type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"type":"object","required":["id","prefix","name","spendCapWei","spentWei","limitHits","createdAt","revokedAt"],"properties":{"id":{"type":"string","format":"uuid"},"prefix":{"type":"string","description":"The start of the key, to recognise it by."},"name":{"type":"string","nullable":true},"spendCapWei":{"type":"string","pattern":"^[0-9]+$","description":"Lifetime spend cap; null is uncapped.","nullable":true},"spentWei":{"type":"string","pattern":"^[0-9]+$","description":"Spent so far, counted against spendCapWei."},"limitHits":{"type":"object","description":"How often each limit has refused the key.","required":["rate","concurrency","spendCap","sessionOpens"],"properties":{"rate":{"type":"integer"},"concurrency":{"type":"integer"},"spendCap":{"type":"integer"},"sessionOpens":{"type":"integer"}}},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":"string","format":"date-time","nullable":true}}}}}}}}},"401":{"description":"Missing or expired sign-in token.","content":{"application/json":{"schema":{"description":"Missing or expired sign-in token.","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"429":{"description":"Too many calls from this wallet (60 a minute).","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until the same call may pass."}},"content":{"application/json":{"schema":{"description":"Too many calls from this wallet (60 a minute).","type":"object","required":["statusCode","error","retryAfterSec"],"properties":{"statusCode":{"type":"integer","enum":[429]},"error":{"type":"string","enum":["rate_limited"]},"retryAfterSec":{"type":"integer"}}}}}}}}},"/api/api-keys/{id}":{"patch":{"operationId":"setApiKeySpendCap","summary":"Change an API key's spend cap","tags":["API keys"],"description":"Sets an active key's lifetime spend cap; null removes it. A cap below what the key has spent refuses its next completion. Fields other than spendCapWei are ignored.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"spendCapWei":{"nullable":true,"description":"The new lifetime spend cap, in wei; null removes it.","type":"string","pattern":"^[1-9][0-9]{0,77}$"}},"required":["spendCapWei"]}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The key, with its new cap.","content":{"application/json":{"schema":{"type":"object","required":["id","prefix","name","spendCapWei","spentWei","limitHits","createdAt","revokedAt"],"properties":{"id":{"type":"string","format":"uuid"},"prefix":{"type":"string","description":"The start of the key, to recognise it by."},"name":{"type":"string","nullable":true},"spendCapWei":{"type":"string","pattern":"^[0-9]+$","description":"Lifetime spend cap; null is uncapped.","nullable":true},"spentWei":{"type":"string","pattern":"^[0-9]+$","description":"Spent so far, counted against spendCapWei."},"limitHits":{"type":"object","description":"How often each limit has refused the key.","required":["rate","concurrency","spendCap","sessionOpens"],"properties":{"rate":{"type":"integer"},"concurrency":{"type":"integer"},"spendCap":{"type":"integer"},"sessionOpens":{"type":"integer"}}},"createdAt":{"type":"string","format":"date-time"},"revokedAt":{"type":"string","format":"date-time","nullable":true}},"description":"The key, with its new cap."}}}},"400":{"description":"The request is invalid (error validation_error), or its body is not JSON (error Bad Request).","content":{"application/json":{"schema":{"description":"The request is invalid (error validation_error), or its body is not JSON (error Bad Request).","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"401":{"description":"Missing or expired sign-in token.","content":{"application/json":{"schema":{"description":"Missing or expired sign-in token.","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"404":{"description":"No key by that id for this wallet (error not_found).","content":{"application/json":{"schema":{"description":"No key by that id for this wallet (error not_found).","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"409":{"description":"The key is revoked (error api_key_revoked).","content":{"application/json":{"schema":{"description":"The key is revoked (error api_key_revoked).","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"429":{"description":"Too many calls from this wallet (60 a minute).","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until the same call may pass."}},"content":{"application/json":{"schema":{"description":"Too many calls from this wallet (60 a minute).","type":"object","required":["statusCode","error","retryAfterSec"],"properties":{"statusCode":{"type":"integer","enum":[429]},"error":{"type":"string","enum":["rate_limited"]},"retryAfterSec":{"type":"integer"}}}}}}}},"delete":{"operationId":"deleteApiKey","summary":"Delete a revoked API key","tags":["API keys"],"description":"Deletes a revoked key for good: it leaves the listing, and its sessions are dropped.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Missing or expired sign-in token.","content":{"application/json":{"schema":{"description":"Missing or expired sign-in token.","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"404":{"description":"No key by that id for this wallet (error not_found).","content":{"application/json":{"schema":{"description":"No key by that id for this wallet (error not_found).","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"409":{"description":"The key is active (error api_key_active); revoke it first.","content":{"application/json":{"schema":{"description":"The key is active (error api_key_active); revoke it first.","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"429":{"description":"Too many calls from this wallet (60 a minute).","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until the same call may pass."}},"content":{"application/json":{"schema":{"description":"Too many calls from this wallet (60 a minute).","type":"object","required":["statusCode","error","retryAfterSec"],"properties":{"statusCode":{"type":"integer","enum":[429]},"error":{"type":"string","enum":["rate_limited"]},"retryAfterSec":{"type":"integer"}}}}}}}}},"/api/api-keys/{id}/revoke":{"post":{"operationId":"revokeApiKey","summary":"Revoke an API key","tags":["API keys"],"description":"Revokes the key at once. It stays in the listing, by its prefix, until it is deleted. Revoking a revoked key changes nothing.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Missing or expired sign-in token.","content":{"application/json":{"schema":{"description":"Missing or expired sign-in token.","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"404":{"description":"No key by that id for this wallet (error not_found).","content":{"application/json":{"schema":{"description":"No key by that id for this wallet (error not_found).","type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"}}}}}},"429":{"description":"Too many calls from this wallet (60 a minute).","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until the same call may pass."}},"content":{"application/json":{"schema":{"description":"Too many calls from this wallet (60 a minute).","type":"object","required":["statusCode","error","retryAfterSec"],"properties":{"statusCode":{"type":"integer","enum":[429]},"error":{"type":"string","enum":["rate_limited"]},"retryAfterSec":{"type":"integer"}}}}}}}}},"/v1/models":{"get":{"operationId":"listModels","summary":"List models","tags":["Developer API"],"description":"The whitelisted models at least one worker serves now. Send a returned `id` verbatim as `model`.","security":[{"apiKeyAuth":[]}],"responses":{"200":{"description":"The models a worker serves now.","content":{"application/json":{"schema":{"description":"The models a worker serves now.","type":"object","required":["object","data"],"properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"type":"object","required":["id","object","created","owned_by"],"properties":{"id":{"type":"string","description":"The model name, verbatim, to send as `model`."},"object":{"type":"string","enum":["model"]},"created":{"type":"integer","description":"Unix seconds."},"owned_by":{"type":"string"}}}}}}}}},"401":{"description":"Missing, unknown or revoked API key (code invalid_api_key).","content":{"application/json":{"schema":{"description":"Missing, unknown or revoked API key (code invalid_api_key).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"429":{"description":"A limit refused the call (code rate_limit_exceeded). Retry after `retry-after` seconds.","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until the same call may pass."}},"content":{"application/json":{"schema":{"description":"A limit refused the call (code rate_limit_exceeded). Retry after `retry-after` seconds.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"500":{"description":"The server failed to answer (code null).","content":{"application/json":{"schema":{"description":"The server failed to answer (code null).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}}}}},"/v1/chat/completions":{"post":{"operationId":"createChatCompletion","summary":"Create a chat completion","tags":["Developer API"],"description":"Runs the conversation as one job on chain, paid from the prepaid balance of the wallet behind the key, or, with a PAYMENT-SIGNATURE header, by that x402 payment alone.\n\nWhere the server takes x402, the API key is optional: a call with no Authorization header is paid by its PAYMENT-SIGNATURE, and its payer is held to per-payer rate and concurrency limits in place of a key's. Without a payment it is answered 402 with the requirements to pay. An Authorization header that holds no valid key is refused 401, payment or not.\n\nSampling parameters (`temperature`, `max_tokens`, `top_p`, ...) are accepted and ignored: each worker runs a model with its own settings.\n\nWith `stream: true` the answer comes as server-sent events: `chat.completion.chunk` objects, then `data: [DONE]`. A failure after the first chunk ends the stream with a `data: {\"error\": ...}` event instead of `[DONE]`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"model":{"type":"string","minLength":1},"messages":{"minItems":1,"type":"array","items":{"type":"object","properties":{"role":{"type":"string","enum":["system","developer","user","assistant"]},"content":{"anyOf":[{"type":"string"},{"minItems":1,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["text"]},"text":{"type":"string"}},"required":["type","text"]}}]}},"required":["role","content"]},"description":"The conversation so far; the last message is the user turn."},"stream":{"nullable":true,"type":"boolean"},"conversation_id":{"description":"Names the thread. A preference only: the call goes back to the session that served the thread last, when it is free. A LightChain extension.","type":"string","minLength":1,"maxLength":64}},"required":["model","messages"]}}}},"parameters":[{"schema":{"type":"string"},"in":"header","name":"payment-signature","required":false,"description":"x402 PaymentPayload paying this call with LightChain's prepaid-debit scheme, as base64 of the JSON. Its `accepted` is the prepaid-debit entry of a 402's `accepts`. When present, it alone pays: the wallet behind the key is never charged, and a refused payment is answered rather than paid another way. With it, the API key may be left out."}],"security":[{"apiKeyAuth":[]},{}],"responses":{"200":{"description":"The answer, with the `lightchain` job it came from (also in the `x-lightchain` header).","headers":{"x-lightchain":{"schema":{"type":"string"},"description":"The `lightchain` object, as JSON."},"payment-response":{"schema":{"type":"string"},"description":"x402 SettleResponse, when the payment was settled or its settlement attempted. Base64 of the JSON."}},"content":{"application/json":{"schema":{"type":"object","required":["id","object","created","model","choices","lightchain"],"properties":{"id":{"type":"string"},"object":{"type":"string","enum":["chat.completion"]},"created":{"type":"integer"},"model":{"type":"string"},"choices":{"type":"array","items":{"type":"object","required":["index","message","logprobs","finish_reason"],"properties":{"index":{"type":"integer"},"logprobs":{"type":"object","nullable":true},"message":{"type":"object","required":["role","content","refusal"],"properties":{"role":{"type":"string","enum":["assistant"]},"content":{"type":"string"},"refusal":{"type":"string","nullable":true}}},"finish_reason":{"type":"string","enum":["stop"]}}}},"lightchain":{"type":"object","description":"The on-chain job that produced the answer.","required":["job_id","session_id","tx_hash","worker"],"properties":{"job_id":{"type":"string","description":"JobRegistry job id, decimal."},"session_id":{"type":"string","description":"Session id, decimal."},"tx_hash":{"type":"string","description":"Hash of the transaction that submitted the job."},"worker":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The worker that served the job."},"dropped_messages":{"type":"integer","description":"How many of the conversation's oldest messages the job left out, to stay within the server's bound on what one job carries. Absent when it carried them all."}}}}}},"text/event-stream":{"schema":{"description":"The JSON of one `data:` event; the last event is `data: [DONE]`, or an error.","oneOf":[{"type":"object","description":"A piece of the answer.","required":["id","object","created","model","choices"],"properties":{"id":{"type":"string"},"object":{"type":"string","enum":["chat.completion.chunk"]},"created":{"type":"integer"},"model":{"type":"string"},"choices":{"type":"array","items":{"type":"object","required":["index","delta","logprobs","finish_reason"],"properties":{"index":{"type":"integer"},"logprobs":{"type":"object","nullable":true},"delta":{"type":"object","properties":{"role":{"type":"string","enum":["assistant"]},"content":{"type":"string"},"refusal":{"type":"string","nullable":true}}},"finish_reason":{"type":"string","enum":["stop",null],"nullable":true}}}},"lightchain":{"type":"object","description":"On the last chunk only.","required":["job_id","session_id","tx_hash","worker"],"properties":{"job_id":{"type":"string","description":"JobRegistry job id, decimal."},"session_id":{"type":"string","description":"Session id, decimal."},"tx_hash":{"type":"string","description":"Hash of the transaction that submitted the job."},"worker":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The worker that served the job."},"dropped_messages":{"type":"integer","description":"How many of the conversation's oldest messages the job left out, to stay within the server's bound on what one job carries. Absent when it carried them all."}}}}},{"description":"The completion failed after its first chunk (e.g. code job_failed, stream_diverged).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}]}}}},"400":{"description":"The request is invalid (code null), the prompt is too large for one job (code context_length_exceeded), or its PAYMENT-SIGNATURE is malformed or not for this server (code invalid_payload, invalid_x402_version, unsupported_scheme, invalid_network or invalid_payment_requirements).","headers":{"payment-required":{"schema":{"type":"string"},"description":"x402 PaymentRequired, listing the prepaid-debit requirements alone; on answers to a payment, to a call the delegate cannot pay, and to a call with neither key nor payment. Base64 of the JSON."}},"content":{"application/json":{"schema":{"description":"The request is invalid (code null), the prompt is too large for one job (code context_length_exceeded), or its PAYMENT-SIGNATURE is malformed or not for this server (code invalid_payload, invalid_x402_version, unsupported_scheme, invalid_network or invalid_payment_requirements).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"401":{"description":"Missing, unknown or revoked API key (code invalid_api_key). A call with no Authorization header gets this only where the server takes no x402 payments.","content":{"application/json":{"schema":{"description":"Missing, unknown or revoked API key (code invalid_api_key). A call with no Authorization header gets this only where the server takes no x402 payments.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"402":{"description":"The call cannot be paid. Codes: delegate_not_authorized, insufficient_balance, allowance_exhausted (with `accepts`); spend_cap_exceeded (with `spend_cap_wei`). A call with neither an API key nor a PAYMENT-SIGNATURE, on a server that takes x402, gets payment_required, with `accepts`. A refused PAYMENT-SIGNATURE carries its x402 reason as the code, with `accepts`: invalid_prepaid_debit_payload_expired, invalid_prepaid_debit_payload_nonce_used, invalid_prepaid_debit_payload_signature (also a payer other than the signer), invalid_prepaid_debit_payload_over_cap, insufficient_funds, invalid_prepaid_debit_payload_facilitator_mismatch, invalid_prepaid_debit_payload_recipient_mismatch, invalid_prepaid_debit_facilitator_not_authorized, invalid_transaction_state.","headers":{"payment-required":{"schema":{"type":"string"},"description":"x402 PaymentRequired, listing the prepaid-debit requirements alone; on answers to a payment, to a call the delegate cannot pay, and to a call with neither key nor payment. Base64 of the JSON."},"payment-response":{"schema":{"type":"string"},"description":"x402 SettleResponse, when the payment was settled or its settlement attempted. Base64 of the JSON."}},"content":{"application/json":{"schema":{"description":"The call cannot be paid. Codes: delegate_not_authorized, insufficient_balance, allowance_exhausted (with `accepts`); spend_cap_exceeded (with `spend_cap_wei`). A call with neither an API key nor a PAYMENT-SIGNATURE, on a server that takes x402, gets payment_required, with `accepts`. A refused PAYMENT-SIGNATURE carries its x402 reason as the code, with `accepts`: invalid_prepaid_debit_payload_expired, invalid_prepaid_debit_payload_nonce_used, invalid_prepaid_debit_payload_signature (also a payer other than the signer), invalid_prepaid_debit_payload_over_cap, insufficient_funds, invalid_prepaid_debit_payload_facilitator_mismatch, invalid_prepaid_debit_payload_recipient_mismatch, invalid_prepaid_debit_facilitator_not_authorized, invalid_transaction_state.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true},"accepts":{"type":"array","description":"The ways to pay, one per scheme: delegate first, then prepaid-debit where this server takes x402. A call with no key has no delegate entry: prepaid-debit alone.","items":{"oneOf":[{"type":"object","description":"Pay from the wallet's prepaid balance through the API's delegate.","required":["scheme","chain_id","payer","delegate","instruction"],"properties":{"scheme":{"type":"string","enum":["delegate"]},"chain_id":{"type":"integer"},"payer":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The wallet behind the key."},"delegate":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The API signer that submits jobs for the wallet."},"instruction":{"type":"object","description":"The transaction the payer sends once, with at least `minimum_value_wei` attached.","required":["contract","function","args","minimum_value_wei"],"properties":{"contract":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"JobRegistry."},"function":{"type":"string","enum":["depositAndAuthorize(address)"]},"args":{"type":"array","items":{"type":"string"}},"minimum_value_wei":{"type":"string","pattern":"^[0-9]+$","description":"An amount in wei, as a decimal string."}}}}},{"type":"object","description":"x402 PaymentRequirements of LightChain's prepaid-debit scheme: pay this one call with a signed debit authorization against the payer's prepaid balance, sent in PAYMENT-SIGNATURE. Copy it verbatim into the payment's `accepted`.","required":["scheme","network","amount","asset","payTo","maxTimeoutSeconds","extra"],"properties":{"scheme":{"type":"string","enum":["prepaid-debit"]},"network":{"type":"string","description":"CAIP-2 chain id, e.g. eip155:8200."},"amount":{"type":"string","pattern":"^[0-9]+$","description":"The job's fee: the most the call debits."},"asset":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"JobRegistry: holds the balance debited and is the EIP-712 domain's verifying contract."},"payTo":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The owner of the session the job runs in: the API itself."},"maxTimeoutSeconds":{"type":"integer"},"extra":{"type":"object","required":["name","version","facilitatorAddress"],"properties":{"name":{"type":"string","description":"EIP-712 domain name."},"version":{"type":"string","description":"EIP-712 domain version."},"facilitatorAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The only sender that may settle the authorization."}}}}}]}},"spend_cap_wei":{"type":"string","pattern":"^[0-9]+$","description":"The key's lifetime spend cap."}}}}}}}},"404":{"description":"No model by that name, or no worker serves it now (code model_not_found).","content":{"application/json":{"schema":{"description":"No model by that name, or no worker serves it now (code model_not_found).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"429":{"description":"A limit refused the call (code rate_limit_exceeded, concurrency_limit_exceeded or session_open_limit_exceeded; per key, or per payer on a call with no key). Retry after `retry-after` seconds.","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until the same call may pass."}},"content":{"application/json":{"schema":{"description":"A limit refused the call (code rate_limit_exceeded, concurrency_limit_exceeded or session_open_limit_exceeded; per key, or per payer on a call with no key). Retry after `retry-after` seconds.","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"500":{"description":"The server failed to answer (code null).","content":{"application/json":{"schema":{"description":"The server failed to answer (code null).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"502":{"description":"The job failed (code job_failed), or the x402 facilitator could not check or settle the payment (code unexpected_verify_error or unexpected_settle_error; send the same payment again).","headers":{"payment-response":{"schema":{"type":"string"},"description":"x402 SettleResponse, when the payment was settled or its settlement attempted. Base64 of the JSON."}},"content":{"application/json":{"schema":{"description":"The job failed (code job_failed), or the x402 facilitator could not check or settle the payment (code unexpected_verify_error or unexpected_settle_error; send the same payment again).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}},"503":{"description":"No worker took a new session in time (code no_worker_available; retry shortly), the chain's blob fee is above what this server pays (code blob_fee_too_high; retry later), or completions are off on this server (code not_configured).","content":{"application/json":{"schema":{"description":"No worker took a new session in time (code no_worker_available; retry shortly), the chain's blob fee is above what this server pays (code blob_fee_too_high; retry later), or completions are off on this server (code not_configured).","type":"object","required":["error"],"properties":{"error":{"type":"object","required":["message","type","param","code"],"properties":{"message":{"type":"string"},"type":{"type":"string","description":"OpenAI error type, e.g. invalid_request_error, insufficient_quota, requests."},"param":{"type":"string","nullable":true},"code":{"type":"string","description":"Machine-readable reason; see each status.","nullable":true}}}}}}}}}}}}}