# Oppermind Lato 1 API Oppermind Lato 1 reads text, images and documents, calls the developer's functions and built-in tools, and generates images and video. Each endpoint handles one kind of output: text endpoints return text and tool calls, /images returns images, /videos returns video. ## Base URL - Public: https://www.oppermind.com/api/v1 - Sites hosted on Oppermind are static and run in a sandbox: call the API from a server you control, never from page JavaScript. ## Authentication - Every request must include: Authorization: Bearer (keys start with opmd_sk_) - Content-Type: application/json - Security: never expose API keys client-side. Proxy through a backend. ## Models | Model ID | Modality | Endpoints | Billed per | |---------|----------|-----------|------------| | oppermind-lato-1 | Text, images and documents in, text out | /messages, /chat/completions, /responses | Million input, cached input and output tokens, plus built-in tool calls | | oppermind-lato-1-vision | Image generation | /images | Image | | oppermind-lato-1-video | Video generation | /videos | Second of video | Any "model" value is accepted. Responses always report the Oppermind model id. ## Text endpoints - POST {base}/messages (alias POST {base}/chat): Chat Completions style request, Oppermind native response. - POST {base}/chat/completions: Chat Completions compatible request and response. Any Chat Completions client library works with baseURL https://www.oppermind.com/api/v1 and model oppermind-lato-1. Only n = 1. logprobs, logit_bias and legacy functions/function_call return 400. - POST {base}/responses: Responses API compatible request and response. Stateless: previous_response_id, conversation, stored prompts, store: true and background: true return 400 OPMD_MODEL_001. Send the whole conversation in input. - stream: true works on all three text endpoints. See Streaming. ## Streaming - Send "stream": true to /messages, /chat/completions or /responses for server-sent events. - /chat/completions: chat.completion.chunk objects in data: lines, then data: [DONE]. The last chunk carries finish_reason, usage and billing. With stream_options: { "include_usage": true } usage also arrives in a final chunk with empty choices. - /responses: response.created, response.output_text.delta, function call events, then response.completed (or response.incomplete) with the full response, usage and billing. - /messages: message_start, content_block_start, content_block_delta (text_delta or input_json_delta), content_block_stop, message_delta (stop_reason, usage, billing), then message_stop. - Text arrives about 100 tokens behind the model because every piece is checked before it is sent. - Billing settles when the reply ends. A client that disconnects is charged for the text produced up to that point. A reply with no answer ends with its finish reason and a notice, and is not charged under the same rule as a non-streamed reply. - An error after the stream starts arrives as an error event: an error object in a data line for /chat/completions, error then response.failed for /responses, an error event for /messages. ## POST /api/v1/messages Request body (Chat Completions format): ```json { "model": "oppermind-lato-1", "max_tokens": 1024, "messages": [ { "role": "system", "content": "Answer briefly." }, { "role": "user", "content": [ { "type": "text", "text": "Summarise this report." }, { "type": "file", "file": { "file_id": "file-0123456789abcdef0123456789abcdef" } } ] } ] } ``` Roles: system, developer, user, assistant, tool. A tool message needs the tool_call_id of an earlier assistant tool call. Response (Oppermind native): ```json { "id": "req_...", "model": "oppermind-lato-1", "type": "message", "content": [ { "type": "text", "text": "..." } ], "usage": { "input_tokens": 1830, "output_tokens": 256, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0, "reasoning_tokens": 0 }, "stop_reason": "end_turn", "tool_usage": {}, "billing": { "charged_aud": "0.001234" } } ``` - Function calls arrive as content blocks { "type": "tool_use", "id", "name", "input" } with stop_reason "tool_use". - stop_reason: end_turn | max_tokens | tool_use | refusal. Web search adds a "citations" array of { url, title }. ## POST /api/v1/chat/completions Response: { id, object: "chat.completion", created, model, choices: [ { index, message: { role, content, tool_calls?, annotations? }, finish_reason } ], usage: { prompt_tokens, completion_tokens, total_tokens, prompt_tokens_details: { cached_tokens }, completion_tokens_details: { reasoning_tokens } }, tool_usage? (only when built-in tools ran), billing: { charged_aud } } finish_reason: stop | length | tool_calls | content_filter. ## POST /api/v1/responses Request: { model, instructions?, input: string or array of items, tools?, tool_choice?, text?: { format }, max_output_tokens?, reasoning?: { effort }, temperature?, top_p?, prompt_cache_key?, user? } Input items: { role, content } messages (system, developer, user, assistant), { type: "function_call", call_id, name, arguments }, { type: "function_call_output", call_id, output }. Reasoning items are accepted and ignored. Content parts: input_text, input_image { image_url | file_id, detail }, input_file { file_id | file_data, filename }. Response: { id: "resp_...", object: "response", created_at, status: completed | incomplete, incomplete_details?, model, output: [ message with output_text content | function_call ], output_text, usage: { input_tokens, input_tokens_details: { cached_tokens }, output_tokens, output_tokens_details: { reasoning_tokens }, total_tokens }, tool_usage, billing: { charged_aud } } seed and stop are read on /messages and /chat/completions only. ## Inputs: images and documents - Images: { "type": "image_url", "image_url": { "url": "https://... or data:image/png;base64,...", "detail": "auto|low|high" } }. JPEG, PNG, GIF or WebP as a base64 data URL (GIF and WebP are converted to PNG; an animation keeps its first frame), or JPEG or PNG as an https URL. - Documents: { "type": "file", "file": { "file_id": "file-..." } } or { "type": "file", "file": { "file_data": "data:application/pdf;base64,...", "filename": "report.pdf" } }. PDF (including scanned PDFs, read page by page up to 20 pages), Word, Excel, PowerPoint (including macro-enabled and legacy files), OpenDocument, project schedules (MPP, MS Project XML, Primavera XER/XML, MPX), CSV, TXT, MD, JSON, code. A PDF sent as an image_url data URL is also read as a document. - Text beyond 1,000,000 characters per document is cut off with a note. A file that cannot be read returns 422 OPMD_FILE_002. Every file is checked for malware before it is stored or read; a file that fails returns 422 OPMD_FILE_003. - Request bodies up to 31 MB. Up to 2000 messages per request. ## Files API - POST {base}/files: raw bytes with header X-Filename (URL-encoded) or ?filename=, or JSON { "filename", "data": base64 or data URL, "mime_type"?, "purpose"? }. Or multipart/form-data with the file in a field named file and an optional purpose field. Up to 31 MB per request, one file per request. - GET {base}/files (?limit=1..1000, default 100, ?after=) returns { object: "list", data, has_more, first_id, last_id }. - GET {base}/files/{id} returns { id, object: "file", bytes, created_at, filename, purpose, mime_type }. - GET {base}/files/{id}/content returns the bytes. - DELETE {base}/files/{id} returns { id, object: "file", deleted: true }. - Per account: up to 1,000 files and 10 GiB. File ids look like file-<32 hex>. ## Tools - Function calling: tools: [ { "type": "function", "function": { "name", "description", "parameters" } } ] (the flat Responses shape works too), tool_choice: auto | none | required | { "type": "function", "function": { "name" } }, parallel_tool_calls. - Built-in: { "type": "web_search", "filters": { "allowed_domains": [...] } or { "excluded_domains": [...] }, "max_uses"? (400 OPMD_CAPABILITY_001 where a cap cannot be enforced) }, { "type": "x_search", "allowed_x_handles"?, "excluded_x_handles"?, "from_date"?, "to_date"? }, { "type": "code_interpreter" }, { "type": "mcp", "server_url": "https://...", "server_label", "allowed_tools"?, "authorization"?, "headers"? }. - Some built-in tools or options may not be available on every model version: those return 400 OPMD_CAPABILITY_001 with a clear message. ## Request options - max_tokens / max_completion_tokens / max_output_tokens: default 16384, up to 128000. Reasoning tokens count as output. - response_format: { "type": "json_object" } or { "type": "json_schema", "json_schema": { "name", "schema", "strict" } } (text.format on /responses). - reasoning_effort (or reasoning.effort): none | minimal | low | medium | high | xhigh. - temperature 0 to 2 and top_p 0 to 1 (model default when omitted), seed, stop (some model versions do not support stop), user. - Caching is automatic: a repeated opening (system text, tools, documents) is served from cache, billed at the lower cached input rate and reported as cached tokens in usage. prompt_cache_key (optional) groups related requests to raise the cache hit rate. cache_control markers on content parts are accepted and used where the model version supports them. Only input is cached. ## POST /api/v1/images Endpoint: POST {base}/images 1 to 4 images per request (n outside that range is clamped), prompt up to 8 KB. Some options may not apply on every model version. Request body: ```json { "model": "oppermind-lato-1-vision", "prompt": "A futuristic city at dusk", "n": 1, "aspect_ratio": "16:9", "response_format": "url" } ``` Response: { "model": "oppermind-lato-1-vision", "data": [ { "url": "/api/v1/images/proxy?token=..." } ], "usage": { "images_generated": 1 } }. Fetch each url with the same bearer key. ## POST /api/v1/videos Endpoint: POST {base}/videos Video generation is synchronous - the response returns the finished video url directly. No polling is required. duration_seconds is 1 to 15 (clamped, default 10). resolution 720p or 1080p (default). Requires a key with full permission. Request body: ```json { "model": "oppermind-lato-1-video", "prompt": "Ocean waves at sunset", "duration_seconds": 10, "resolution": "1080p" } ``` Response: ```json { "id": "req_01abc123", "model": "oppermind-lato-1-video", "status": "succeeded", "url": "{base}/videos/proxy?token=...", "duration_seconds": 10, "resolution": "1080p" } ``` ## GET /api/v1/videos/{id} Endpoint: GET {base}/videos/{id} You probably do not need this. Video generation is synchronous, so POST {base}/videos already returns the finished url. This returns a confirmation only, and exists so clients written against a polling-style API do not break. ## GET /api/v1/openapi.json Endpoint: GET {base}/openapi.json The machine-readable OpenAPI 3.1 description of this API - point an SDK generator or Postman at it to scaffold a typed client. Public: no API key needed. ## GET /api/v1/changelog Endpoint: GET {base}/changelog Machine-readable changelog and deprecation policy. Poll this to learn about breaking changes before they reach you. Requires a valid API key. ## GET /api/v1/pricing Endpoint: GET {base}/pricing Current AUD rates: text per million input, cached input and output tokens, tools per built-in tool call, image.per_image, video.per_second_720p / per_second_1080p. image or video is null when that capability is not available. Rates can change, so read them from here instead of hardcoding them. ## Error codes /messages, /images and /videos return { "error": { "type", "code", "message" } }. /chat/completions, /responses and /files return { "error": { "message", "type", "code", "param" } }. Authentication and rate-limit errors use the first shape everywhere. Branch on error.code. | Code | HTTP | Meaning | |------|------|---------| | OPMD_MODEL_001 | 400 | Invalid request (409 when the request was already processed) | | OPMD_MODEL_002 | 400 | More than 2000 messages or input items | | OPMD_MODEL_003 | 413 / 400 | Body over 31 MB, or image prompt over 8 KB | | OPMD_MODEL_005 | 400 | Streaming is not available for this request | | OPMD_CAPABILITY_001 | 400 | The current model version cannot use this tool, option or content type | | OPMD_UPSTREAM_400 | 400 / 413 / 415 / 422 | The model rejected the request (for example an unreadable image). Reason in the message. Not charged. | | OPMD_EMPTY_001 | 502 | The model returned no answer (for example it ran out of output tokens). Not charged, unless built-in tools already ran during the request or more than 20 requests in the past hour returned no answer; the error message says when a request was charged. | | OPMD_FILE_001 | 404 | No file with that id | | OPMD_FILE_002 | 400 / 409 / 413 / 415 / 422 | File empty, too large, unreadable, unsupported file type, or over the account limit | | OPMD_FILE_003 | 422 | File did not pass our file safety checks and was not accepted. Not charged | | OPMD_AUTH_001 | 401 | Invalid or missing API key | | OPMD_AUTH_002 | 403 | Key lacks permission for this endpoint, or account suspended | | OPMD_AUTH_003 | 403 | Caller IP not in the key's allowlist | | OPMD_BILLING_001 | 402 | Not enough credit | | OPMD_POLICY_001 | 403 | Request breaks the usage policy | | OPMD_MODERATION_001 | 403 | Image or video prompt blocked by content moderation | | OPMD_RATE_001 | 429 | Per-key request limit reached | | OPMD_RATE_002 | 429 | Model busy, retry shortly | | OPMD_IDEMP_001 | 400 | Malformed Idempotency-Key | | OPMD_IDEMP_002 | 409 | Same Idempotency-Key still running | | OPMD_PROVIDER_001 | 503 | Capability not available right now | | OPMD_GW_001 | 500 / 502 / 503 | Gateway error, retry with backoff | | OPMD_GW_002 | 503 | IP allowlist check unavailable, retry | | OPMD_ROUTE_404 | 404 | Unknown endpoint | ## Response headers (useful for debugging) - X-Request-ID: unique request ID - X-Model: oppermind-lato-1 - X-Route-Type: text | image | video - X-Powered-By: Oppermind Lato 1 - X-API-Version: dated contract build - RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset - Idempotent-Replay: true when a response is replayed for a repeated Idempotency-Key ## Limits - 120 requests per minute per API key unless the key has a different limit - Request body: 31 MB. Messages or input items: 2000 per request - Output tokens: default 16384, up to 128000 - Files: 31 MB per upload, 1,000 files and 10 GiB per account - Images: prompt up to 8 KB, 1 to 4 per request. Video: 1 to 15 seconds ## Webhooks - Add endpoints in the developer dashboard (Settings, Webhooks). https on port 443 or 8443, public internet only. The signing secret (whsec_...) is shown once; it can be rolled. - Events (JSON POST body { id: "evt_...", type, created, data }): - request.completed: data { request_id, endpoint, route_type, status, input_tokens?, output_tokens?, units?, charged_aud, latency_ms } - request.error: data { request_id, endpoint, route_type, status, error_code, latency_ms } - billing.threshold: data { credit_balance_aud, alert_threshold_aud } (once per drop below the alert threshold) - key.created / key.deleted: data { key_id, name, key_prefix, permissions } - webhook.test: sent by the dashboard's Send test button - Headers: Oppermind-Event, Oppermind-Delivery (event id, same on every retry; use it to ignore repeats), Oppermind-Signature: t=,v1=." with the signing secret>. Verify against the raw body and reject timestamps more than 5 minutes old; compare with a constant-time comparison. - Reply 2xx within 10 seconds. Other replies are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours; 50 failures in a row pause the endpoint. Events never contain prompts or responses. ## Pricing Prepaid credit at Oppermind's AUD rates: per million input tokens, per million cached input tokens, per million output tokens (reasoning tokens count as output), per built-in tool call, per image and per second of video. Rates can change. Current rates are on the developer dashboard Billing page and at GET {base}/pricing. Every text response reports its exact charge in billing.charged_aud. Requests the model rejects are not charged. Replies with no answer (OPMD_EMPTY_001) are not charged unless built-in tools already ran or more than 20 requests in the past hour returned no answer. ## Client libraries - There is no official SDK yet. Use any HTTP client, or any Chat Completions client library with baseURL https://www.oppermind.com/api/v1, apiKey opmd_sk_... and model oppermind-lato-1. - cURL example (text): ```bash curl {base}/messages -H "Authorization: Bearer opmd_sk_..." -H "Content-Type: application/json" -d '{"model":"oppermind-lato-1","max_tokens":1024,"messages":[{"role":"user","content":"Hello!"}]}' ``` HTML version: https://oppermind.com/developers/docs OpenAPI spec: https://www.oppermind.com/api/v1/openapi.json