{
  "openapi": "3.1.0",
  "info": {
    "title": "Ompera Client API",
    "version": "0.2.0",
    "description": "OpenAI-compatible client API. Authenticate with an Ompera API key as a Bearer token. A model is always an LLM, and only chat uses one: name it with an id from GET /models. Translation (/translations), text from images (/ocr), speech to text (/audio/transcriptions) and embeddings (/embeddings) are separate services: each has its own endpoint and runs on an engine made for it. They need no model; Ompera sends each request to a free provider that offers the service. Ompera answers with another model only when the key's offline setting allows it; chat responses name the model that answered in the X-Ompera-Model header and the response's model field. During the beta, each key may run 4 requests at once and a request may take up to 5 minutes."
  },
  "servers": [
    {
      "url": "https://api.ompera.net/v1"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List the models (LLMs) this key can call now",
        "description": "Only online models the key may use are listed. my/ addresses are the account's own models, <pool>/<model> addresses come from pool members, other IDs are Ompera network models.",
        "responses": {
          "200": {
            "description": "Model list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/capabilities": {
      "get": {
        "operationId": "listCapabilities",
        "summary": "Show which services are available now",
        "responses": {
          "200": {
            "description": "Services",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/chat/completions": {
      "post": {
        "operationId": "createChatCompletion",
        "summary": "Create an OpenAI-compatible chat completion",
        "description": "Returns JSON, or server-sent events when stream is true. Streaming, tools and JSON output need the matching model capability. Fields not listed here reach the model server unchanged. After a streamed HTTP 200, a failure arrives as a data event with an error object, followed by [DONE].",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatCompletionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Chat completion, or a stream of chat.completion.chunk events",
            "headers": {
              "X-Ompera-Model": {
                "$ref": "#/components/headers/XOmperaModel"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletion"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "data: <chat.completion.chunk JSON> events ending with data: [DONE]"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RetryableError"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          },
          "504": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/embeddings": {
      "post": {
        "operationId": "createEmbedding",
        "summary": "Create text embeddings",
        "description": "Service; no model needed. Vectors have 384 dimensions (all-MiniLM-L6-v2) and work best for English text.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "input"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "description": "Not needed; ignored. OpenAI SDKs require it, so any value works."
                  },
                  "input": {
                    "description": "At most 65536 bytes in total.",
                    "oneOf": [
                      {
                        "type": "string",
                        "minLength": 1
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "minLength": 1
                        },
                        "minItems": 1,
                        "maxItems": 64
                      }
                    ]
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OpenAI-compatible embeddings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "const": "list"
                    },
                    "model": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "embedding"
                        ],
                        "properties": {
                          "object": {
                            "const": "embedding"
                          },
                          "index": {
                            "type": "integer"
                          },
                          "embedding": {
                            "type": "array",
                            "items": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RetryableError"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          },
          "504": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/ocr": {
      "post": {
        "operationId": "createOcr",
        "summary": "Extract text from an image",
        "description": "Ompera-specific service; no model needed. Reads Ukrainian and English text. The image must decode to 1 byte through 23 MiB.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "image_base64"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "description": "Not needed; ignored. OpenAI SDKs require it, so any value works."
                  },
                  "image_base64": {
                    "type": "string",
                    "contentEncoding": "base64"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OCR result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "text"
                  ],
                  "properties": {
                    "text": {
                      "type": "string"
                    },
                    "model": {
                      "type": "string"
                    },
                    "confidence": {
                      "type": "number",
                      "minimum": 0,
                      "maximum": 1
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RetryableError"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          },
          "504": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/audio/transcriptions": {
      "post": {
        "operationId": "createTranscription",
        "summary": "Transcribe an audio file",
        "description": "OpenAI-compatible multipart form. Service; no model needed. Accepts WAV, MP3 or FLAC up to 23 MiB and recognizes 99 languages. A JSON body with audio_base64 is also accepted.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "description": "Not needed; ignored. OpenAI SDKs require it, so any value works."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "language": {
                    "type": "string",
                    "description": "Spoken language, such as en; detected when omitted."
                  },
                  "response_format": {
                    "type": "string",
                    "enum": [
                      "json",
                      "text"
                    ]
                  },
                  "translate": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ],
                    "description": "true returns the speech translated into English."
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "audio_base64"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "description": "Not needed; ignored. OpenAI SDKs require it, so any value works."
                  },
                  "audio_base64": {
                    "type": "string",
                    "contentEncoding": "base64"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime_type": {
                    "type": "string"
                  },
                  "language": {
                    "type": "string"
                  },
                  "response_format": {
                    "type": "string",
                    "enum": [
                      "json",
                      "text"
                    ]
                  },
                  "translate": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transcription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "text"
                  ],
                  "properties": {
                    "text": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RetryableError"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          },
          "504": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/translations": {
      "post": {
        "operationId": "createTranslation",
        "summary": "Translate text",
        "description": "Service; no model needed. About 60 languages, with the source language detected when omitted. Non-streaming. Input is limited to 4096 UTF-8 bytes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranslationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Translated text",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranslationResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RetryableError"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          },
          "504": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An Ompera API key from Workspace → API keys."
      }
    },
    "headers": {
      "XOmperaModel": {
        "description": "The model address that answered. Differs from the requested model only when the key's offline setting switched to another pool model.",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "OpenAI-style API error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RetryableError": {
        "description": "model_busy or client_concurrency_exceeded; retry after Retry-After",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "message",
              "code"
            ],
            "properties": {
              "message": {
                "type": "string"
              },
              "type": {
                "type": "string"
              },
              "code": {
                "type": "string",
                "description": "invalid_request_error, unsupported_model_capability, invalid_api_key, model_not_allowed, unshielded_access_required, model_not_found, model_busy, client_concurrency_exceeded, all_providers_failed, provider_request_failed, provider_protocol_error, provider_unavailable or request_timeout. Errors from the model server or service engine itself keep its status and body."
              }
            }
          }
        }
      },
      "ModelList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          }
        }
      },
      "Model": {
        "type": "object",
        "required": [
          "id",
          "object",
          "owned_by",
          "kind",
          "capabilities"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The address to send as model."
          },
          "object": {
            "const": "model"
          },
          "created": {
            "type": "integer"
          },
          "owned_by": {
            "type": "string",
            "enum": [
              "ompera-owner",
              "ompera-pool",
              "ompera-community"
            ],
            "description": "ompera-owner: the account's own model (my/…); ompera-pool: a pool member's model; ompera-community: an Ompera network model."
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "const": "chat",
            "description": "Models are LLMs."
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "chat plus any of streaming, tools, json_object, json_schema."
          },
          "verification": {
            "type": "string",
            "enum": [
              "exact",
              "provider_claimed"
            ]
          },
          "pool": {
            "type": "string",
            "description": "Pool models only."
          },
          "copies": {
            "type": "integer",
            "description": "Pool models only: members running the model now."
          },
          "available_nodes": {
            "type": "integer"
          },
          "capacity": {
            "type": "integer",
            "description": "Requests the model can run at once."
          },
          "inflight": {
            "type": "integer",
            "description": "Requests running now."
          }
        },
        "additionalProperties": true
      },
      "ChatMessage": {
        "type": "object",
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "system",
              "user",
              "assistant",
              "tool"
            ]
          },
          "content": {
            "description": "Text, or an array of content parts when the model accepts them.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "tool_calls": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "tool_call_id": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "ChatCompletionRequest": {
        "type": "object",
        "required": [
          "model",
          "messages"
        ],
        "properties": {
          "model": {
            "type": "string"
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/ChatMessage"
            }
          },
          "stream": {
            "type": "boolean",
            "default": false,
            "description": "Needs the streaming capability."
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "OpenAI function tools. Needs the tools capability."
          },
          "tool_choice": {
            "description": "Passed to the model server.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object"
              }
            ]
          },
          "response_format": {
            "type": "object",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "text",
                  "json_object",
                  "json_schema"
                ],
                "description": "json_object and json_schema need the matching capability; other types are refused."
              },
              "json_schema": {
                "type": "object"
              }
            },
            "additionalProperties": true
          },
          "temperature": {
            "type": "number"
          },
          "max_tokens": {
            "type": "integer"
          },
          "top_p": {
            "type": "number"
          },
          "stop": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "seed": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "ChatCompletion": {
        "type": "object",
        "required": [
          "choices"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "chat.completion"
          },
          "created": {
            "type": "integer"
          },
          "model": {
            "type": "string",
            "description": "The model address that answered."
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "message": {
                  "$ref": "#/components/schemas/ChatMessage"
                },
                "finish_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "additionalProperties": true
            }
          },
          "usage": {
            "type": "object",
            "description": "Present when the model server reports it.",
            "properties": {
              "prompt_tokens": {
                "type": "integer"
              },
              "completion_tokens": {
                "type": "integer"
              },
              "total_tokens": {
                "type": "integer"
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "TranslationRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "input",
          "target_language"
        ],
        "properties": {
          "model": {
            "type": "string",
            "description": "Not needed; ignored."
          },
          "input": {
            "type": "string",
            "minLength": 1,
            "description": "At most 4096 bytes when encoded as UTF-8."
          },
          "source_language": {
            "type": "string",
            "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
            "description": "Detected when omitted."
          },
          "target_language": {
            "type": "string",
            "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"
          }
        }
      },
      "TranslationResult": {
        "type": "object",
        "required": [
          "object",
          "text",
          "target_language"
        ],
        "properties": {
          "object": {
            "const": "translation"
          },
          "text": {
            "type": "string"
          },
          "source_language": {
            "type": "string",
            "description": "The given source language, or the detected one."
          },
          "target_language": {
            "type": "string"
          }
        }
      },
      "ServiceList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          }
        }
      },
      "Service": {
        "type": "object",
        "required": [
          "id",
          "object",
          "endpoint",
          "available_nodes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "translation",
              "ocr",
              "speech_to_text",
              "embeddings"
            ]
          },
          "object": {
            "const": "capability"
          },
          "name": {
            "type": "string"
          },
          "endpoint": {
            "type": "string",
            "description": "Path to call, such as /v1/translations."
          },
          "available_nodes": {
            "type": "integer",
            "description": "Providers offering the service now; 0 means requests get provider_unavailable."
          },
          "capacity": {
            "type": "integer"
          },
          "inflight": {
            "type": "integer"
          }
        }
      }
    }
  }
}
