{
    "openapi": "3.0.3",
    "info": {
        "title": "AskCybex API",
        "version": "1.0.0",
        "description": "Train an AI on your own documents, then query it from anywhere.\n\n**Two ways to call it:**\n\n1. **OpenAI-compatible** (`/api/v1/*`) \u2014 point any tool already built for the OpenAI Chat Completions format at AskCybex by changing the base URL and the key. No SDK, no rewrite.\n2. **Native REST** (`/api/*`) \u2014 the endpoints our own WordPress plugins use: chat, Server-Sent Event streaming, deployment details and answer feedback.\n\n**Authentication.** Every request is authenticated with a deployment API key, created in your dashboard under *API key*. One key maps to exactly one deployment (one trained chatbot), so the key alone determines which documents the answer comes from.\n\n**Billing.** Calls consume tokens from your account balance. A reply that cannot be grounded in your documents is refused before the language model is called, and costs nothing.",
        "contact": {
            "name": "AskCybex Support",
            "email": "contact@askcybex.com",
            "url": "https://askcybex.com"
        }
    },
    "servers": [
        {
            "url": "https://askcybex.com",
            "description": "AskCybex"
        }
    ],
    "tags": [
        {
            "name": "OpenAI-compatible",
            "description": "Drop-in replacement for the OpenAI Chat Completions API. Use with Open WebUI, LangChain, the OpenAI SDKs, or anything else that speaks this format."
        },
        {
            "name": "Chat",
            "description": "Native chat endpoints, including live token streaming over Server-Sent Events."
        },
        {
            "name": "Deployment",
            "description": "Configuration and pre-generated question/answer pairs for a deployment."
        },
        {
            "name": "Feedback",
            "description": "Record whether an answer was helpful."
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "paths": {
        "/api/v1/models": {
            "get": {
                "tags": [
                    "OpenAI-compatible"
                ],
                "operationId": "listModels",
                "summary": "List the model your key maps to",
                "description": "Returns the deployment behind this API key formatted as an OpenAI model object. Clients such as Open WebUI call this to populate their model picker.",
                "responses": {
                    "200": {
                        "description": "The deployment, as a model list.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ModelList"
                                },
                                "example": {
                                    "object": "list",
                                    "data": [
                                        {
                                            "id": "98",
                                            "object": "model",
                                            "created": 1770000000,
                                            "owned_by": "askcybex"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                }
            }
        },
        "/api/v1/chat/completions": {
            "post": {
                "tags": [
                    "OpenAI-compatible"
                ],
                "operationId": "createChatCompletion",
                "summary": "Chat completion (OpenAI format)",
                "description": "Answers the last user message using your trained documents, and returns it in the standard OpenAI ChatCompletion shape.\n\nSet `model` to your deployment id (the `id` returned by `/api/v1/models`). Prior messages in `messages` are used as conversation history.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/ChatCompletionRequest"
                            },
                            "example": {
                                "model": "98",
                                "messages": [
                                    {
                                        "role": "user",
                                        "content": "What is your refund policy?"
                                    },
                                    {
                                        "role": "assistant",
                                        "content": "Returns are accepted within 30 days."
                                    },
                                    {
                                        "role": "user",
                                        "content": "Does that include sale items?"
                                    }
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The completion.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ChatCompletionResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/BadRequest"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/OutOfTokens"
                    }
                }
            }
        },
        "/api/v1/chat/completions-with-sources": {
            "post": {
                "tags": [
                    "OpenAI-compatible"
                ],
                "operationId": "createChatCompletionWithSources",
                "summary": "Chat completion, with the sources it used",
                "description": "Identical to `/api/v1/chat/completions`, but the response also carries a `sources` array naming the documents the answer was drawn from, and `chunks` with the exact retrieved passages and their relevance scores.\n\nUse this when you must show citations, or audit where an answer came from.\n\nNot available on Private GPT deployments \u2014 they run on a backend that does not implement citation retrieval, and return `400 sources_unsupported`.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/ChatCompletionRequest"
                            },
                            "example": {
                                "model": "98",
                                "messages": [
                                    {
                                        "role": "user",
                                        "content": "What is Hogwarts?"
                                    }
                                ],
                                "conversation_id": "conv-9f1c2b7d"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The completion plus its sources.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ChatCompletionWithSources"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    }
                }
            }
        },
        "/api/v1/chat/completions-contextual": {
            "post": {
                "tags": [
                    "OpenAI-compatible"
                ],
                "operationId": "createChatCompletionContextual",
                "summary": "Chat completion with history-aware document search",
                "description": "Use this for **multi-turn conversations**.\n\nThe other endpoints search your documents using the question exactly as typed. A follow-up that leans on context \u2014 *\"who are the students **there**?\"* \u2014 is therefore searched literally, and usually matches nothing: the conversation history reaches the model that writes the answer, but never the document search.\n\nThis endpoint resolves the follow-up into a standalone question **before** searching (*\"who are the students at Hogwarts?\"*), searches with that, and then answers using your original wording. The resolved query is returned as `search_query` so you can see exactly what was searched.\n\nEverything else matches `/chat/completions-with-sources`: same key, same two conversation styles, same `sources` and `chunks`.\n\n**Cost.** One extra small model call per turn, and only when there is history to resolve \u2014 a first question costs the same as the other endpoints.\n\nNot available on Private GPT deployments (`400 contextual_unsupported`).",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/ChatCompletionRequest"
                            },
                            "example": {
                                "model": "98",
                                "messages": [
                                    {
                                        "role": "user",
                                        "content": "Who are the students there?"
                                    }
                                ],
                                "conversation_id": "conv-dc4d05c139d35b910ff4be69"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The completion, its sources, and the query that was searched.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ChatCompletionContextual"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/BadRequest"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/OutOfTokens"
                    }
                }
            }
        },
        "/api/chat/": {
            "post": {
                "tags": [
                    "Chat"
                ],
                "operationId": "chat",
                "summary": "Ask a question",
                "description": "Sends a visitor message and returns the complete answer.\n\nPass the same `conversation_id` across turns to keep a thread connected \u2014 follow-up questions like \"how much does that cost?\" then resolve correctly. Generate it once per visitor (a UUID is ideal) and reuse it.\n\nRate limit: 60 requests per minute.",
                "security": [
                    {
                        "apiKeyForm": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "$ref": "#/components/schemas/ChatRequest"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The answer. Check for an `error` key \u2014 this endpoint returns HTTP 200 with an error message rather than an error status.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ChatResponse"
                                },
                                "example": {
                                    "details": {
                                        "name": "Support Bot"
                                    },
                                    "response": "We accept returns within 30 days of delivery, provided the item is unused.",
                                    "chat_log_id": 48210
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/api/stream/": {
            "get": {
                "tags": [
                    "Chat"
                ],
                "operationId": "streamChat",
                "summary": "Ask a question, streamed live (SSE)",
                "description": "Same answer as `/api/chat/`, delivered token by token over Server-Sent Events so the reply appears as it is written.\n\nConsume it with `EventSource` in the browser, or any SSE client. Each event is a `data:` line containing JSON:\n\n```\ndata: {\"token\": \"We accept \"}\ndata: {\"token\": \"returns \"}\ndata: {\"done\": true, \"conversation_id\": \"9f1c\u2026\", \"tokens_count\": 84, \"chat_log_id\": 48210}\n```\n\nA `{\"error\": \"\u2026\"}` event is sent if the answer cannot be produced. The stream closes after `done`.\n\nRate limit: 60 requests per minute.",
                "security": [
                    {
                        "apiKeyQuery": []
                    }
                ],
                "parameters": [
                    {
                        "name": "api_key",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Your deployment API key."
                    },
                    {
                        "name": "msg",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The visitor question."
                    },
                    {
                        "name": "conversation_id",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        },
                        "description": "Thread id, reused across turns."
                    },
                    {
                        "name": "visitor_email",
                        "in": "query",
                        "schema": {
                            "type": "string",
                            "format": "email"
                        },
                        "description": "Captured visitor email, stored on the chat log."
                    },
                    {
                        "name": "lang",
                        "in": "query",
                        "schema": {
                            "type": "string"
                        },
                        "description": "Visitor language, e.g. `en` or `fr`."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "An SSE stream (`text/event-stream`).",
                        "content": {
                            "text/event-stream": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Invalid API key."
                    },
                    "402": {
                        "$ref": "#/components/responses/OutOfTokens"
                    },
                    "404": {
                        "description": "Deployment not found or not published."
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/api/faq/details/": {
            "post": {
                "tags": [
                    "Deployment"
                ],
                "operationId": "getDeploymentDetails",
                "summary": "Get deployment name and Q&A pairs",
                "description": "Returns the deployment's display name and any pre-generated question/answer pairs \u2014 used to render an FAQ list or seed suggested questions before the visitor types anything.\n\nRate limit: 120 requests per minute.",
                "security": [
                    {
                        "apiKeyForm": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "api_key"
                                ],
                                "properties": {
                                    "api_key": {
                                        "type": "string",
                                        "description": "Your deployment API key."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Deployment details.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeploymentDetails"
                                },
                                "example": {
                                    "details": {
                                        "name": "Support Bot",
                                        "questions": [
                                            {
                                                "question": "Do you ship internationally?",
                                                "answer": "Yes, to 40 countries."
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/api/feedback/": {
            "post": {
                "tags": [
                    "Feedback"
                ],
                "operationId": "submitFeedback",
                "summary": "Rate an answer",
                "description": "Records whether an answer was helpful, against the `chat_log_id` returned by `/api/chat/` or in the SSE `done` event. Ratings appear in your dashboard under Reports, next to the question that produced them.\n\nRate limit: 120 requests per minute.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "chat_log_id",
                                    "feedback"
                                ],
                                "properties": {
                                    "chat_log_id": {
                                        "type": "integer",
                                        "description": "The id of the answer being rated.",
                                        "example": 48210
                                    },
                                    "feedback": {
                                        "type": "integer",
                                        "enum": [
                                            0,
                                            1
                                        ],
                                        "description": "`1` = helpful, `0` = not helpful."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Stored.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "success": {
                                            "type": "boolean"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/BadRequest"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "Send your deployment API key as `Authorization: Bearer <key>`. Used by the /api/v1 endpoints."
            },
            "apiKeyForm": {
                "type": "apiKey",
                "in": "header",
                "name": "X-API-KEY",
                "description": "Native endpoints also accept the key as an `api_key` form field, which is what the WordPress plugins send."
            },
            "apiKeyQuery": {
                "type": "apiKey",
                "in": "query",
                "name": "api_key",
                "description": "The streaming endpoint takes the key as a query parameter, because EventSource cannot set headers."
            }
        },
        "responses": {
            "Unauthorized": {
                "description": "Missing or invalid API key, or the deployment is not published.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "BadRequest": {
                "description": "The request was malformed or a required field was missing.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "OutOfTokens": {
                "description": "Your token balance is exhausted. Top up to continue.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "Too many requests. See the `X-RateLimit-Remaining` response header.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            }
        },
        "schemas": {
            "ModelList": {
                "type": "object",
                "properties": {
                    "object": {
                        "type": "string",
                        "example": "list"
                    },
                    "data": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Model"
                        }
                    }
                }
            },
            "Model": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "Your deployment id \u2014 pass this as `model`."
                    },
                    "object": {
                        "type": "string",
                        "example": "model"
                    },
                    "created": {
                        "type": "integer"
                    },
                    "owned_by": {
                        "type": "string",
                        "example": "askcybex"
                    }
                }
            },
            "ChatMessage": {
                "type": "object",
                "required": [
                    "role",
                    "content"
                ],
                "properties": {
                    "role": {
                        "type": "string",
                        "enum": [
                            "user",
                            "assistant",
                            "system"
                        ],
                        "example": "user"
                    },
                    "content": {
                        "type": "string",
                        "example": "What is your refund policy?"
                    }
                }
            },
            "ChatCompletionRequest": {
                "type": "object",
                "required": [
                    "model",
                    "messages"
                ],
                "properties": {
                    "model": {
                        "type": "string",
                        "description": "Your **deployment id** (the `id` from `/api/v1/models`) \u2014 not the question. Required by the OpenAI format; the key already determines which deployment answers.",
                        "example": "98"
                    },
                    "messages": {
                        "type": "array",
                        "description": "The conversation. The **last `user` message is the question**; earlier `user`/`assistant` messages are used as history. `system` messages are ignored \u2014 the deployment persona governs tone.",
                        "items": {
                            "$ref": "#/components/schemas/ChatMessage"
                        }
                    },
                    "conversation_id": {
                        "type": "string",
                        "description": "Optional. Keeps a thread together **without resending history**: send only the new user message and reuse this id each turn.\n\nIf you omit it, one is minted and returned as `conversation_id` on the response \u2014 reuse that value next turn. Client-supplied `messages` history takes precedence when both are present.",
                        "example": "conv-9f1c2b7d"
                    },
                    "user": {
                        "type": "string",
                        "description": "OpenAI's end-user field. Accepted as a fallback thread id when `conversation_id` is absent."
                    },
                    "stream": {
                        "type": "boolean",
                        "default": false,
                        "description": "Stream the reply as SSE chunks."
                    },
                    "temperature": {
                        "type": "number",
                        "description": "Overrides the deployment default for this call."
                    },
                    "max_tokens": {
                        "type": "integer"
                    }
                }
            },
            "ChatCompletionResponse": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "object": {
                        "type": "string",
                        "example": "chat.completion"
                    },
                    "created": {
                        "type": "integer"
                    },
                    "model": {
                        "type": "string"
                    },
                    "conversation_id": {
                        "type": "string",
                        "description": "Non-standard, additive. Reuse it as `conversation_id` on the next request to continue the thread without resending history."
                    },
                    "choices": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Choice"
                        }
                    },
                    "usage": {
                        "$ref": "#/components/schemas/Usage"
                    }
                }
            },
            "ChatCompletionWithSources": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/ChatCompletionResponse"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "sources": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                },
                                "description": "Distinct document names the answer was drawn from, extensions stripped."
                            },
                            "chunks": {
                                "type": "array",
                                "description": "The exact passages retrieved, with their relevance scores.",
                                "items": {
                                    "type": "object",
                                    "properties": {
                                        "text": {
                                            "type": "string"
                                        },
                                        "source": {
                                            "type": "string"
                                        },
                                        "score": {
                                            "type": "number"
                                        }
                                    }
                                }
                            }
                        }
                    }
                ]
            },
            "ChatCompletionContextual": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/ChatCompletionWithSources"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "search_query": {
                                "type": "string",
                                "description": "The standalone question the documents were actually searched for, after your follow-up was resolved against the conversation. Equals the original question when there was no history.",
                                "example": "Who are the students at Hogwarts?"
                            }
                        }
                    }
                ]
            },
            "Choice": {
                "type": "object",
                "properties": {
                    "index": {
                        "type": "integer"
                    },
                    "message": {
                        "$ref": "#/components/schemas/ChatMessage"
                    },
                    "finish_reason": {
                        "type": "string",
                        "example": "stop"
                    }
                }
            },
            "Usage": {
                "type": "object",
                "properties": {
                    "prompt_tokens": {
                        "type": "integer"
                    },
                    "completion_tokens": {
                        "type": "integer"
                    },
                    "total_tokens": {
                        "type": "integer"
                    }
                }
            },
            "ChatRequest": {
                "type": "object",
                "required": [
                    "api_key",
                    "msg"
                ],
                "properties": {
                    "api_key": {
                        "type": "string",
                        "description": "Your deployment API key."
                    },
                    "msg": {
                        "type": "string",
                        "description": "The visitor question."
                    },
                    "conversation_id": {
                        "type": "string",
                        "description": "Thread id, reused across turns to keep context."
                    },
                    "visitor_email": {
                        "type": "string",
                        "format": "email",
                        "description": "Captured visitor email, stored on the chat log."
                    }
                }
            },
            "ChatResponse": {
                "type": "object",
                "properties": {
                    "details": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                                "description": "The deployment name."
                            }
                        }
                    },
                    "response": {
                        "type": "string",
                        "description": "The answer."
                    },
                    "chat_log_id": {
                        "type": "integer",
                        "description": "Pass this to /api/feedback/ to attach a rating."
                    },
                    "error": {
                        "type": "string",
                        "description": "Present instead of `response` when the request failed."
                    }
                }
            },
            "DeploymentDetails": {
                "type": "object",
                "properties": {
                    "details": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string"
                            },
                            "questions": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "properties": {
                                        "question": {
                                            "type": "string"
                                        },
                                        "answer": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "error": {
                        "type": "string"
                    }
                }
            },
            "Error": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "string"
                    }
                }
            }
        }
    }
}