Reference
Beta gapsAPI reference
The frozen /v1 contract: models, chat, embeddings, responses and SSE.
Updated 2026-10-25 · MainAI Compute docs
This page renders the frozen MVP contract. Base URL: https://cloud.mainai.dk/v1. All request and response bodies are JSON unless a stream is requested. Timestamps are UTC; money is EUR cents in portal responses and EUR per million tokens in prices.
Endpoint mapLink to this section
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/models | List allow-listed models |
| POST | /v1/chat/completions | Chat, JSON or SSE |
| POST | /v1/embeddings | Text embeddings |
| POST | /v1/responses | Responses API passthrough |
| POST | /v1/completions | Legacy text completion (beta compat) |
GET
- Path
/v1/models- Purpose
- List allow-listed models
POST
- Path
/v1/chat/completions- Purpose
- Chat, JSON or SSE
POST
- Path
/v1/embeddings- Purpose
- Text embeddings
POST
- Path
/v1/responses- Purpose
- Responses API passthrough
POST
- Path
/v1/completions- Purpose
- Legacy text completion (beta compat)
Common headersLink to this section
| Request header | Required | Notes |
|---|---|---|
Authorization | Yes | Bearer sk-mai_… |
Content-Type | POST | application/json |
Idempotency-Key | No | Forwarded for safe retry of non-streaming POSTs |
OpenAI-Beta | No | Forwarded when a client sends it |
x-request-id | No | Caller correlation hint; MainAI returns its own value |
Authorization
- Required
- Yes
- Notes
Bearer sk-mai_…
Content-Type
- Required
- POST
- Notes
application/json
Idempotency-Key
- Required
- No
- Notes
- Forwarded for safe retry of non-streaming POSTs
OpenAI-Beta
- Required
- No
- Notes
- Forwarded when a client sends it
x-request-id
- Required
- No
- Notes
- Caller correlation hint; MainAI returns its own value
Responses carry x-request-id. They may also carry x-mainai-model, x-mainai-cache and x-mainai-remaining-requests. No x-mainai-*, server or upstream version header is exposed. CORS is open for the documented methods and headers.
ModelsLink to this section
/v1/modelsList models available to a key
Returns public offers that are active, probed and routing-synced, filtered to the key allow-list. The response hides provider routing and private addresses. DKK is display-only.
Request headers and body
| Field | Type | Required | Notes |
|---|---|---|---|
| Authorization | string | yes | Bearer sk-mai_…. The key must be active and the model must be in its allow-list. |
| x-request-id | string | no | Optional caller-supplied correlation ID. MainAI always returns its own x-request-id. |
AuthorizationstringyesBearer sk-mai_…. The key must be active and the model must be in its allow-list.
x-request-idstringnoOptional caller-supplied correlation ID. MainAI always returns its own x-request-id.
curl -s https://cloud.mainai.dk/v1/models \
-H "Authorization: Bearer $MAINAI_API_KEY"Response fields
| Field | Type | Required | Notes |
|---|---|---|---|
| object | "list" | yes | Always list. |
| data | array | yes | Model objects. |
| data[].id | string | yes | Model ID to send in inference requests. |
| data[].object | "model" | yes | Always model. |
| data[].owned_by | string | yes | Publisher label. Always mainai. |
| data[].context_window | integer | yes | Maximum combined input and output context. |
| data[].max_output | integer | yes | Maximum generated tokens for one request. |
| data[].access | "enterprise" | yes | Commercial access tier; both public offers are Enterprise. |
| data[].line | "thinker" or "enterprise" | yes | Product line selected by staff. |
| data[].plan | "enterprise" | yes | Required commercial plan. |
| data[].available | boolean | yes | Whether the calling organization can use a listed model. A blocked organization is rejected before listing, so listed rows are true; out-of-scope models are omitted. |
| data[].pricing | object | yes | EUR per million tokens: input, cached input, cache write and output. |
| data[].pricing.*_per_million_eur | number | yes | Exact published rate for the named token class. |
| data[].pricing_reference | object | yes | Display-only fixed DKK reference. Charged in EUR. |
| data[].pricing_reference.rate | number | yes | Fixed rate, currently 7.46 DKK per EUR. |
object"list"yesAlways list.
dataarrayyesModel objects.
data[].idstringyesModel ID to send in inference requests.
data[].object"model"yesAlways model.
data[].owned_bystringyesPublisher label. Always mainai.
data[].context_windowintegeryesMaximum combined input and output context.
data[].max_outputintegeryesMaximum generated tokens for one request.
data[].access"enterprise"yesCommercial access tier; both public offers are Enterprise.
data[].line"thinker" or "enterprise"yesProduct line selected by staff.
data[].plan"enterprise"yesRequired commercial plan.
data[].availablebooleanyesWhether the calling organization can use a listed model. A blocked organization is rejected before listing, so listed rows are true; out-of-scope models are omitted.
data[].pricingobjectyesEUR per million tokens: input, cached input, cache write and output.
data[].pricing.*_per_million_eurnumberyesExact published rate for the named token class.
data[].pricing_referenceobjectyesDisplay-only fixed DKK reference. Charged in EUR.
data[].pricing_reference.ratenumberyesFixed rate, currently 7.46 DKK per EUR.
{
"object": "list",
"data": [
{
"id": "mainai-coder",
"object": "model",
"owned_by": "mainai",
"context_window": 1000000,
"max_output": 393216,
"access": "enterprise",
"line": "thinker",
"plan": "enterprise",
"available": true,
"pricing": {"input_per_million_eur":0.60,"cached_input_per_million_eur":0.12,"cache_write_per_million_eur":0,"output_per_million_eur":1.80},
"pricing_reference": {"currency":"DKK","base_currency":"EUR","rate":7.46,"per_million_micros":{"input":4476000,"cached_input":895200,"output":13428000,"cache_write":0}}
},
{
"id": "mainai-cyber",
"object": "model",
"owned_by": "mainai",
"context_window": 1000000,
"max_output": 393216,
"access": "enterprise",
"line": "thinker",
"plan": "enterprise",
"available": true,
"pricing": {"input_per_million_eur":0.60,"cached_input_per_million_eur":0.12,"cache_write_per_million_eur":0,"output_per_million_eur":1.80},
"pricing_reference": {"currency":"DKK","base_currency":"EUR","rate":7.46,"per_million_micros":{"input":4476000,"cached_input":895200,"output":13428000,"cache_write":0}}
}
]
}- The list is scoped to the key. A model outside the key allow-list is omitted rather than denied.
- Responses are cacheable for 60 seconds. Do not use this endpoint as a health check.
Statuses
- 200
- Model list returned.
- 401
- Missing, malformed, revoked or expired key.
- 403
- Organization suspended, KYC pending or Enterprise contract missing.
- 500
- Portal catalog unavailable.
Chat completionsLink to this section
/v1/chat/completionsCreate a chat completion
OpenAI-compatible chat completion. Supports non-streaming JSON and server-sent events. Fields are passed to the selected engine; unsupported fields may be ignored by the model runtime.
Request headers and body
| Field | Type | Required | Notes |
|---|---|---|---|
| Authorization | string | yes | Bearer sk-mai_…. The key must be active and the model must be in its allow-list. |
| Content-Type | string | yes | application/json. |
| model | string | yes | A model ID from /v1/models. |
| messages | array | yes | Conversation turns. Each item has role (system, user, assistant, tool) and content (string or content-part array). |
| stream | boolean | no | When true, returns text/event-stream. Default false. |
| stream_options | object | no | Set { "include_usage": true } to receive a final usage chunk on streams. |
| max_tokens | integer | no | Output token cap. Bounded by the model's maximum output. |
| temperature | number | no | Sampling temperature. |
| top_p | number | no | Nucleus sampling probability mass. |
| stop | string or array | no | Up to four stop sequences. |
| seed | integer | no | Best-effort deterministic sampling. |
| tools | array | no | Tool definitions. Tool calling depends on model capability. |
| tool_choice | string or object | no | auto, none, required or a named tool. |
| response_format | object | no | { "type": "json_object" } for JSON mode when supported by the model. |
| presence_penalty | number | no | −2.0 to 2.0. |
| frequency_penalty | number | no | −2.0 to 2.0. |
| user | string | no | Caller-side end-user identifier for correlation. |
AuthorizationstringyesBearer sk-mai_…. The key must be active and the model must be in its allow-list.
Content-Typestringyesapplication/json.
modelstringyesA model ID from /v1/models.
messagesarrayyesConversation turns. Each item has role (system, user, assistant, tool) and content (string or content-part array).
streambooleannoWhen true, returns text/event-stream. Default false.
stream_optionsobjectnoSet { "include_usage": true } to receive a final usage chunk on streams.
max_tokensintegernoOutput token cap. Bounded by the model's maximum output.
temperaturenumbernoSampling temperature.
top_pnumbernoNucleus sampling probability mass.
stopstring or arraynoUp to four stop sequences.
seedintegernoBest-effort deterministic sampling.
toolsarraynoTool definitions. Tool calling depends on model capability.
tool_choicestring or objectnoauto, none, required or a named tool.
response_formatobjectno{ "type": "json_object" } for JSON mode when supported by the model.
presence_penaltynumberno−2.0 to 2.0.
frequency_penaltynumberno−2.0 to 2.0.
userstringnoCaller-side end-user identifier for correlation.
curl -s https://cloud.mainai.dk/v1/chat/completions \
-H "Authorization: Bearer $MAINAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mainai-coder",
"messages": [
{ "role": "system", "content": "Answer with a single paragraph." },
{ "role": "user", "content": "What does the API layer own in this architecture?" }
],
"max_tokens": 300,
"temperature": 0.2
}'Response fields
| Field | Type | Required | Notes |
|---|---|---|---|
| id | string | yes | Completion identifier (chatcmpl_…). |
| object | "chat.completion" | yes | Object type. |
| created | integer | yes | Unix timestamp in seconds. |
| model | string | yes | Served model ID. |
| choices | array | yes | Completion candidates. |
| choices[].index | integer | yes | Candidate index. |
| choices[].message | object | yes | Assistant message with role and content; may include tool_calls. |
| choices[].finish_reason | string | yes | stop, length, tool_calls, content_filter or null while streaming. |
| usage | object | yes | prompt_tokens, completion_tokens, total_tokens. |
| usage.prompt_tokens_details.cached_tokens | integer | no | Prefix-cached input tokens, when the engine reports them. |
| usage.cache_creation_tokens | integer | no | Tokens written to a provider cache, when reported. |
idstringyesCompletion identifier (chatcmpl_…).
object"chat.completion"yesObject type.
createdintegeryesUnix timestamp in seconds.
modelstringyesServed model ID.
choicesarrayyesCompletion candidates.
choices[].indexintegeryesCandidate index.
choices[].messageobjectyesAssistant message with role and content; may include tool_calls.
choices[].finish_reasonstringyesstop, length, tool_calls, content_filter or null while streaming.
usageobjectyesprompt_tokens, completion_tokens, total_tokens.
usage.prompt_tokens_details.cached_tokensintegernoPrefix-cached input tokens, when the engine reports them.
usage.cache_creation_tokensintegernoTokens written to a provider cache, when reported.
{
"id": "chatcmpl_mai_01j9x4",
"object": "chat.completion",
"created": 1770000000,
"model": "mainai-coder",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The API layer owns the public /v1 contract, key checks and header sanitation."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 42,
"completion_tokens": 21,
"total_tokens": 63,
"prompt_tokens_details": { "cached_tokens": 0 }
}
}Streaming
With stream: true, the response is text/event-stream. Each event carries one JSON chunk; the final data frame is [DONE]. Chunk delta.content is incremental. When stream_options.include_usage is set, a final chunk with an empty choices array carries usage.
event: message
data: {"id":"chatcmpl_mai_01j9x4","object":"chat.completion.chunk","created":1770000000,"model":"mainai-coder","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
event: message
data: {"id":"chatcmpl_mai_01j9x4","object":"chat.completion.chunk","created":1770000000,"model":"mainai-coder","choices":[{"index":0,"delta":{"content":"The API"},"finish_reason":null}]}
event: message
data: {"id":"chatcmpl_mai_01j9x4","object":"chat.completion.chunk","created":1770000000,"model":"mainai-coder","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]- Use
modelexactly as returned by/v1/models. - One concurrent stream per key is soft-limited by the router.
- The routing layer keeps an SSE stream open for up to 300 seconds of idle time.
Statuses
- 200
- Completion or event stream.
- 400
- Invalid JSON or invalid request fields.
- 401
- Missing, revoked or expired key.
- 402
- Wallet or key budget exhausted.
- 403
- Model gate, key allow-list or organization suspended.
- 404
- Unknown model ID.
- 413
- Request body larger than 8 MiB.
- 429
- Key or route rate limit.
- 503
- Engine unavailable or connect timeout.
- 504
- Upstream timeout.
EmbeddingsLink to this section
/v1/embeddingsCreate embeddings
OpenAI-compatible embeddings. Use the embedding model in the key allow-list. Input may be a string or an array of strings or token arrays, depending on engine support.
Request headers and body
| Field | Type | Required | Notes |
|---|---|---|---|
| Authorization | string | yes | Bearer sk-mai_…. The key must be active and the model must be in its allow-list. |
| Content-Type | string | yes | application/json. |
| model | string | yes | An embedding model, for example mainai-coder. |
| input | string or array | yes | Text to embed. Arrays are batched. |
| encoding_format | "float" or "base64" | no | Defaults to float. |
| dimensions | integer | no | Requested output dimensions when the model supports truncation. |
| user | string | no | Caller-side end-user identifier. |
AuthorizationstringyesBearer sk-mai_…. The key must be active and the model must be in its allow-list.
Content-Typestringyesapplication/json.
modelstringyesAn embedding model, for example mainai-coder.
inputstring or arrayyesText to embed. Arrays are batched.
encoding_format"float" or "base64"noDefaults to float.
dimensionsintegernoRequested output dimensions when the model supports truncation.
userstringnoCaller-side end-user identifier.
curl -s https://cloud.mainai.dk/v1/embeddings \
-H "Authorization: Bearer $MAINAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mainai-coder",
"input": ["first document", "second document"],
"encoding_format": "float"
}'Response fields
| Field | Type | Required | Notes |
|---|---|---|---|
| object | "list" | yes | Always list. |
| data | array | yes | One item per input. |
| data[].object | "embedding" | yes | Object type. |
| data[].index | integer | yes | Input index. |
| data[].embedding | array | yes | Vector of floats, or base64 string when requested. |
| model | string | yes | Served model. |
| usage | object | yes | prompt_tokens and total_tokens. |
object"list"yesAlways list.
dataarrayyesOne item per input.
data[].object"embedding"yesObject type.
data[].indexintegeryesInput index.
data[].embeddingarrayyesVector of floats, or base64 string when requested.
modelstringyesServed model.
usageobjectyesprompt_tokens and total_tokens.
{
"object": "list",
"data": [
{ "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, 0.0789] },
{ "object": "embedding", "index": 1, "embedding": [0.0321, 0.0110, -0.0678] }
],
"model": "mainai-coder",
"usage": { "prompt_tokens": 8, "total_tokens": 8 }
}- Vector length and normalization are fixed by the model. Store the model ID with each vector.
Statuses
- 200
- Embeddings returned.
- 400
- Invalid input or batch too large.
- 401
- Missing, revoked or expired key.
- 402
- Wallet or key budget exhausted.
- 403
- Model gate, key allow-list or organization suspended.
- 404
- Unknown model ID.
- 413
- Request body larger than 8 MiB.
- 429
- Key or route rate limit.
- 503
- Engine unavailable.
ResponsesLink to this section
/v1/responsesCreate a response
OpenAI Responses API shape, proxied without translation. Use it when the client already speaks Responses; chat completions is the better-documented path for new integrations. Streaming uses named SSE events.
Request headers and body
| Field | Type | Required | Notes |
|---|---|---|---|
| Authorization | string | yes | Bearer sk-mai_…. The key must be active and the model must be in its allow-list. |
| Content-Type | string | yes | application/json. |
| model | string | yes | A model ID from /v1/models. |
| input | string or array | yes | User input as text or input items. |
| instructions | string | no | System-level instructions. |
| max_output_tokens | integer | no | Output cap. |
| stream | boolean | no | When true, emits typed SSE events. |
| tools | array | no | Tool definitions, when supported by the model. |
| store | boolean | no | Passed through; portal persistence is not guaranteed. |
AuthorizationstringyesBearer sk-mai_…. The key must be active and the model must be in its allow-list.
Content-Typestringyesapplication/json.
modelstringyesA model ID from /v1/models.
inputstring or arrayyesUser input as text or input items.
instructionsstringnoSystem-level instructions.
max_output_tokensintegernoOutput cap.
streambooleannoWhen true, emits typed SSE events.
toolsarraynoTool definitions, when supported by the model.
storebooleannoPassed through; portal persistence is not guaranteed.
curl -s https://cloud.mainai.dk/v1/responses \
-H "Authorization: Bearer $MAINAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mainai-cyber",
"input": "Outline three checks before a production database restore.",
"max_output_tokens": 350
}'Response fields
| Field | Type | Required | Notes |
|---|---|---|---|
| id | string | yes | Response identifier (resp_…). |
| object | "response" | yes | Object type. |
| created_at | integer | yes | Unix timestamp in seconds. |
| status | string | yes | completed, in_progress, failed or incomplete. |
| model | string | yes | Served model. |
| output | array | yes | Output items; text lives in message content parts. |
| usage | object | no | Token usage, when the engine reports it. |
idstringyesResponse identifier (resp_…).
object"response"yesObject type.
created_atintegeryesUnix timestamp in seconds.
statusstringyescompleted, in_progress, failed or incomplete.
modelstringyesServed model.
outputarrayyesOutput items; text lives in message content parts.
usageobjectnoToken usage, when the engine reports it.
{
"id": "resp_mai_01j9x5",
"object": "response",
"created_at": 1770000000,
"status": "completed",
"model": "mainai-cyber",
"output": [
{
"type": "message",
"role": "assistant",
"content": [{ "type": "output_text", "text": "1. Verify the backup checksum…" }]
}
],
"usage": { "input_tokens": 18, "output_tokens": 96, "total_tokens": 114 }
}Streaming
Responses streaming is passthrough. Expect named events such as response.created, response.output_text.delta and response.completed, each with a data: JSON line.
event: response.created
data: {"type":"response.created","response":{"id":"resp_mai_01j9x5","status":"in_progress"}}
event: response.output_text.delta
data: {"type":"response.output_text.delta","delta":"1. Verify"}
event: response.completed
data: {"type":"response.completed","response":{"id":"resp_mai_01j9x5","status":"completed"}}- Responses is a passthrough surface. Feature coverage follows the engine behind the model.
- Prefer chat completions for tool-calling production code until Responses parity is tested for your model.
Statuses
- 200
- Response or event stream.
- 400
- Invalid JSON or unsupported field.
- 401
- Missing, revoked or expired key.
- 402
- Wallet or key budget exhausted.
- 403
- Model gate, key allow-list or organization suspended.
- 404
- Unknown model ID.
- 429
- Rate limit.
- 503
- Engine unavailable.
Legacy completionsLink to this section
/v1/completionsCreate a legacy text completion
Legacy completions endpoint accepted by the API and passed through. It is not part of the frozen contract table; treat it as a compatibility surface.
Request headers and body
| Field | Type | Required | Notes |
|---|---|---|---|
| Authorization | string | yes | Bearer sk-mai_…. The key must be active and the model must be in its allow-list. |
| model | string | yes | A model ID from /v1/models. |
| prompt | string or array | yes | Text prompt. |
| max_tokens | integer | no | Output cap. |
| temperature | number | no | Sampling temperature. |
| stream | boolean | no | SSE when supported by the engine. |
AuthorizationstringyesBearer sk-mai_…. The key must be active and the model must be in its allow-list.
modelstringyesA model ID from /v1/models.
promptstring or arrayyesText prompt.
max_tokensintegernoOutput cap.
temperaturenumbernoSampling temperature.
streambooleannoSSE when supported by the engine.
curl -s https://cloud.mainai.dk/v1/completions \
-H "Authorization: Bearer $MAINAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "mainai-coder", "prompt": "Write a one-line health check.", "max_tokens": 40 }'Response fields
| Field | Type | Required | Notes |
|---|---|---|---|
| id | string | yes | Completion identifier. |
| object | "text_completion" | yes | Object type. |
| choices[].text | string | yes | Generated text. |
| usage | object | yes | Token counts. |
idstringyesCompletion identifier.
object"text_completion"yesObject type.
choices[].textstringyesGenerated text.
usageobjectyesToken counts.
{
"id": "cmpl_mai_01j9x6",
"object": "text_completion",
"choices": [{ "index": 0, "text": "curl -fsS /health", "finish_reason": "stop" }],
"usage": { "prompt_tokens": 7, "completion_tokens": 6, "total_tokens": 13 }
}- New integrations should use chat completions.
Statuses
- 200
- Completion returned.
- 400
- Invalid request.
- 401
- Invalid key.
- 402
- Wallet or key budget exhausted.
- 403
- Model gate, allow-list or organization suspended.
- 404
- Unknown model ID.
- 503
- Engine unavailable.
Server-sent eventsLink to this section
Chat and Responses support streaming. Chat frames are data: <json> lines terminated by data: [DONE]. Responses frames add an event: name. A stream chunk is an ordinary object with a delta instead of a full message.
curl -N https://cloud.mainai.dk/v1/chat/completions \
-H "Authorization: Bearer $MAINAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mainai-coder",
"messages": [{ "role": "user", "content": "Count to five." }],
"stream": true,
"max_tokens": 80
}'- Parse until
[DONE]; do not assume the connection closes cleanly. - Accumulate
choices[0].delta.content. - Chat
finish_reasonarrives on the last content chunk. A stream can end without one if the connection fails. - Set
stream_options.include_usagewhen you need final token counts. - Do not retry a stream after content has reached the user unless duplication is acceptable.
OpenAI SDK compatibilityLink to this section
The API follows the OpenAI wire shape for the routes above. Point an OpenAI SDK at the /v1 base URL and use a MainAI key. The SDK's own retries, streaming parser and typed objects work as usual.
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MAINAI_API_KEY,
baseURL: "https://cloud.mainai.dk/v1",
});
const completion = await client.chat.completions.create({
model: "mainai-coder",
messages: [{ role: "user", content: "Say ready" }],
max_tokens: 12,
});
console.log(completion.choices[0]?.message.content);See SDKs for Python and environment-variable patterns. Not every OpenAI feature is implemented; unsupported fields can be ignored or rejected by the engine.
Error tableLink to this section
| Status | code | When |
|---|---|---|
| 400 | invalid_json | Malformed JSON body. |
| 400 | invalid_request_error | Passthrough validation failure. |
| 401 | invalid_api_key | Missing, malformed, revoked or expired key. |
| 402 | insufficient_credit | Wallet balance or key budget exhausted. |
| 403 | model_not_allowed | Model outside the key allow-list. |
| 403 | kyc_required | Identity approval is missing. |
| 403 | contract_required | KYC is approved; Enterprise contract is missing. |
| 403 | verification_required | Compatibility code for an access-tier mismatch. |
| 403 | org_suspended | Organization suspended. |
| 404 | model_not_found | Unknown model ID. |
| 404 | not_found | Unknown /v1 endpoint. |
| 413 | payload_too_large | Body over 8 MiB. |
| 429 | rate_limit_exceeded | Key or gateway rate limit. |
| 500 | server_error | Upstream or portal fault. |
| 502/503 | upstream_unavailable | Gateway or engine unavailable. |
| 504 | timeout | Upstream timeout. |
400
codeinvalid_json- When
- Malformed JSON body.
400
codeinvalid_request_error- When
- Passthrough validation failure.
401
codeinvalid_api_key- When
- Missing, malformed, revoked or expired key.
402
codeinsufficient_credit- When
- Wallet balance or key budget exhausted.
403
codemodel_not_allowed- When
- Model outside the key allow-list.
403
codekyc_required- When
- Identity approval is missing.
403
codecontract_required- When
- KYC is approved; Enterprise contract is missing.
403
codeverification_required- When
- Compatibility code for an access-tier mismatch.
403
codeorg_suspended- When
- Organization suspended.
404
codemodel_not_found- When
- Unknown model ID.
404
codenot_found- When
- Unknown
/v1endpoint.
413
codepayload_too_large- When
- Body over 8 MiB.
429
coderate_limit_exceeded- When
- Key or gateway rate limit.
500
codeserver_error- When
- Upstream or portal fault.
502/503
codeupstream_unavailable- When
- Gateway or engine unavailable.
504
codetimeout- When
- Upstream timeout.
{
"error": {
"message": "Invalid API key.",
"type": "auth_error",
"param": null,
"code": "invalid_api_key"
}
}Compatibility gapsLink to this section
- No
/v1/audio,/v1/images,/v1/files,/v1/batches,/v1/fine_tuningor assistants. - No provider-specific extensions are guaranteed.
/v1/completionsis accepted but absent from the frozen contract table.- The model list includes exact EUR pricing and the fixed DKK reference; explanations live in Models.
- No webhook callbacks for long generations.