{"openapi":"3.1.0","info":{"title":"NexiChat API","version":"1","summary":"Chat completions from the Nexi Air, Nexi Prime and Nexi Nova models, in the OpenAI request format.","description":"NexiChat (nexichat.ai) is a Portuguese AI assistant by NexiAgent. This API exposes its three models — Nexi Air, Nexi Prime and Nexi Nova — through an OpenAI-compatible chat-completions endpoint, priced in euros per million tokens.\n\n## Getting a key\n\n1. Create a developer account at https://developer.nexichat.ai (self-serve, no sales call).\n2. The first account receives 1.00 € of free test credit; top-ups are 10, 20, 50, 100 €.\n3. Create a key (`nexi_live_…`) and send it as `Authorization: Bearer <key>`.\n\n## Models and prices (EUR per 1M tokens)\n\n| brand | model id | input | output |\n| --- | --- | --- | --- |\n| `air` | `nexi-air-v1` | 0.26 € | 3.10 € |\n| `prime` | `nexi-prime-v1` | 1.80 € | 6.20 € |\n| `nova` | `nexi-nova-v1` | 3.20 € | 9.50 € |\n\n`nova` is a reasoning model: it returns `reasoning_content` next to `content`. `prime` and `nova` accept `tools` (function calling); `air` does not.\n\n## Rate limits\n\n| tier | account | requests / min | tokens / min |\n| --- | --- | --- | --- |\n| Tier 1 | below 10 € deposited or in balance | 10 | 10,000 |\n| Tier 2 | 10 € to 100 € deposited or in balance | 100 | 200,000 |\n| Tier 3 | above 100 € deposited or in balance | 500 | 1,000,000 |\n\nAuthenticated chat responses include `RateLimit-Policy` and `RateLimit` from the enforced request/token windows. Values are snapshots; streaming includes the maximum token reservation. Every 429 includes `Retry-After` seconds, also available as `error.retry_after`.\n\n## API versioning and deprecation policy\n\nThe API uses URL versioning (/v1). Compatible additions may ship in v1; clients must ignore unknown response fields. Breaking contract changes require a new major version. No v1 retirement is currently announced. A migration guide and dates will be published before removal. Announced deprecations will use Deprecation (RFC 9745) and, when scheduled, Sunset (RFC 8594) response headers.\nVersion policy: https://docs.nexichat.ai/en#versioning.\n\n## Errors\n\nEvery error is JSON: `{ \"error\": { \"code\", \"message\", \"retry_after\"? } }`. The code is stable and machine-readable; the message is for people.\n\nFull documentation: https://docs.nexichat.ai/en. Site index for agents: https://nexichat.ai/llms.txt.","termsOfService":"https://nexichat.ai/termos","contact":{"name":"NexiAgent support","email":"suporte@nexichat.ai","url":"https://nexichat.ai/contacto"},"license":{"name":"Proprietary","url":"https://nexichat.ai/termos"}},"externalDocs":{"description":"NexiChat API documentation","url":"https://docs.nexichat.ai/en"},"servers":[{"url":"https://api.nexichat.ai/v1","description":"Production"}],"tags":[{"name":"chat","description":"Chat completions."},{"name":"models","description":"The models available to the key."},{"name":"identity","description":"Sign in with NexiChat (OpenID Connect)."}],"security":[{"apiKey":[]}],"paths":{"/chat/{brand}":{"post":{"operationId":"chatCompletion","tags":["chat"],"summary":"Create a chat completion","description":"Sends a conversation to one of the three models and returns the reply, either whole (JSON) or as server-sent events when `stream` is `true`. Tool calls are returned in the OpenAI shape; with `stream`, each tool call arrives in a single chunk with its arguments already complete.","parameters":[{"$ref":"#/components/parameters/brand"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}}},"responses":{"200":{"description":"The completion. JSON when `stream` is `false`; `text/event-stream` otherwise, where each `data:` line carries one ChatCompletionChunk and the stream ends with `data: [DONE]`.","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}},"x-request-id":{"description":"Identifier of this request, for support.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletion"}},"text/event-stream":{"schema":{"$ref":"#/components/schemas/ChatCompletionChunk"}}}},"400":{"description":"`invalid_request` — Fix the request body; `message` names the first invalid field.\n\n`tools_unsupported` — This model does not accept `tools`. Use `prime` or `nova`, or drop `tools`.","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_request","message":"Human-readable message."}}}}},"401":{"description":"`invalid_api_key` — Send `Authorization: Bearer nexi_live_…` with an active key from the developer console.","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Human-readable message."}}}}},"402":{"description":"`insufficient_balance` — Top up the account in the developer console, or lower `max_tokens`.","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"insufficient_balance","message":"Human-readable message."}}}}},"404":{"description":"`unknown_model` — Use one of the brands in `GET /models`: air, prime or nova.","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unknown_model","message":"Human-readable message."}}}}},"429":{"description":"`rate_limit_rpm` — Wait `retry_after` seconds; higher tiers unlock with deposits or balance.\n\n`rate_limit_tpm` — Wait `retry_after` seconds or send fewer tokens per minute.\n\n`daily_spend_limit_reached` — The daily spend ceiling set in the console is reached; raise it or wait for the next day (UTC).","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds before an automatic retry. Daily spend limits reset at midnight UTC; limits can also be adjusted in the console.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limit_rpm","message":"Human-readable message.","retry_after":12}}}}},"502":{"description":"`upstream_error` — The model provider failed; retry with backoff.","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"upstream_error","message":"Human-readable message."}}}}},"503":{"description":"`model_unavailable` — The model is temporarily unavailable; retry later or use another brand.","headers":{"RateLimit-Policy":{"description":"Enforced minute quotas for requests and tokens (IETF HTTPAPI RateLimit draft). Present after API key validation and the relevant limiter.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds to reset (t), as a snapshot. SSE includes the maximum token reservation; concurrent requests may consume more.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests per minute for this key.","schema":{"type":"integer","minimum":1}},"X-RateLimit-Remaining":{"description":"Remaining requests in the window.","schema":{"type":"integer","minimum":0}},"X-RateLimit-Reset":{"description":"Request window reset, Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"model_unavailable","message":"Human-readable message."}}}}}}}},"/models":{"get":{"operationId":"listModels","parameters":[],"tags":["models"],"summary":"List the models","description":"The three public models with their identifiers, context window, output ceiling and price in euros per million tokens.","responses":{"200":{"description":"The list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelList"}}}},"401":{"description":"`invalid_api_key` — Send `Authorization: Bearer nexi_live_…` with an active key from the developer console.","headers":{},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Human-readable message."}}}}}}}},"/api/oidc/userinfo":{"servers":[{"url":"https://nexichat.ai","description":"NexiChat (OpenID Connect provider)"}],"get":{"operationId":"getUserInfo","parameters":[],"tags":["identity"],"summary":"Claims of the signed-in person","description":"The OpenID Connect UserInfo endpoint. Returns the claims the granted scopes allow: `sub` always; `email` and `email_verified` with `email`; `name` and `picture` with `profile`; the active plan with `plano`, read at call time rather than at login.","security":[{"oauth2":["openid"]}],"responses":{"200":{"description":"The claims.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"401":{"description":"Missing, expired or invalid access token. The `WWW-Authenticate` header names the protected-resource metadata (RFC 9728).","headers":{"WWW-Authenticate":{"schema":{"type":"string"},"description":"Bearer challenge."}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","const":"invalid_token"}}}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"nexi_live_…","description":"An API key from https://developer.nexichat.ai. Keys are shown once, at creation; revoke and recreate to rotate."},"oauth2":{"type":"oauth2","description":"Sign in with NexiChat. Authorization code with PKCE (S256) only; discovery at https://nexichat.ai/.well-known/openid-configuration.","flows":{"authorizationCode":{"authorizationUrl":"https://nexichat.ai/api/oidc/authorize","tokenUrl":"https://nexichat.ai/api/oidc/token","refreshUrl":"https://nexichat.ai/api/oidc/token","scopes":{"openid":"Sign in with the NexiChat account (the `sub` claim).","profile":"Name and picture.","email":"Email address and whether it is verified.","plano":"The active NexiChat plan, read at call time.","offline_access":"A rotating refresh token, to keep the session without a new login."}}}}},"parameters":{"brand":{"name":"brand","in":"path","required":true,"description":"Which model answers.","schema":{"type":"string","enum":["air","prime","nova"]}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable cause.","enum":["invalid_request","tools_unsupported","invalid_api_key","insufficient_balance","unknown_model","rate_limit_rpm","rate_limit_tpm","daily_spend_limit_reached","upstream_error","model_unavailable"]},"message":{"type":"string","description":"What happened, for people."},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before automatically retrying a 429; matches Retry-After."}}}}},"SystemMessage":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","const":"system"},"content":{"type":"string","minLength":1,"maxLength":200000}}},"UserMessage":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","const":"user"},"content":{"type":"string","minLength":1,"maxLength":200000}}},"AssistantMessage":{"type":"object","required":["role"],"properties":{"role":{"type":"string","const":"assistant"},"content":{"type":["string","null"],"maxLength":200000},"tool_calls":{"type":"array","maxItems":64,"items":{"$ref":"#/components/schemas/ToolCall"}}}},"ToolMessage":{"type":"object","required":["role","content","tool_call_id"],"properties":{"role":{"type":"string","const":"tool"},"content":{"type":"string","maxLength":200000,"description":"The tool result, as text."},"tool_call_id":{"type":"string","minLength":1,"maxLength":200}}},"Message":{"oneOf":[{"$ref":"#/components/schemas/SystemMessage"},{"$ref":"#/components/schemas/UserMessage"},{"$ref":"#/components/schemas/AssistantMessage"},{"$ref":"#/components/schemas/ToolMessage"}],"discriminator":{"propertyName":"role","mapping":{"system":"#/components/schemas/SystemMessage","user":"#/components/schemas/UserMessage","assistant":"#/components/schemas/AssistantMessage","tool":"#/components/schemas/ToolMessage"}}},"ToolCall":{"type":"object","required":["id","function"],"properties":{"id":{"type":"string","minLength":1,"maxLength":200},"type":{"type":"string","const":"function"},"function":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":64},"arguments":{"type":"string","maxLength":100000,"default":"{}","description":"JSON-encoded arguments."}}}}},"Tool":{"type":"object","required":["function"],"properties":{"type":{"type":"string","const":"function"},"function":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":64},"description":{"type":"string","maxLength":4000,"default":""},"parameters":{"type":"object","additionalProperties":true,"description":"A JSON Schema object describing the arguments."}}}}},"ChatRequest":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","minItems":1,"maxItems":200,"items":{"$ref":"#/components/schemas/Message"},"description":"The conversation so far. At least one `user` or `assistant` message."},"tools":{"type":"array","maxItems":64,"items":{"$ref":"#/components/schemas/Tool"},"description":"Functions the model may call (`prime` and `nova` only)."},"stream":{"type":"boolean","default":false,"description":"Server-sent events instead of one JSON body."},"max_tokens":{"type":"integer","minimum":1,"description":"Ceiling on output tokens."},"temperature":{"type":"number","minimum":0,"maximum":2}},"example":{"messages":[{"role":"user","content":"Quem descobriu o caminho marítimo para a Índia?"}]}},"Usage":{"type":"object","required":["prompt_tokens","completion_tokens","total_tokens"],"properties":{"prompt_tokens":{"type":"integer","minimum":0},"completion_tokens":{"type":"integer","minimum":0},"total_tokens":{"type":"integer","minimum":0}}},"ChatCompletion":{"type":"object","required":["id","object","created","model","choices","usage"],"properties":{"id":{"type":"string"},"object":{"type":"string","const":"chat.completion"},"created":{"type":"integer","description":"Unix time, seconds."},"model":{"type":"string","enum":["nexi-air-v1","nexi-prime-v1","nexi-nova-v1"]},"choices":{"type":"array","items":{"type":"object","required":["index","message","finish_reason"],"properties":{"index":{"type":"integer"},"message":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","const":"assistant"},"content":{"type":"string"},"tool_calls":{"type":"array","items":{"$ref":"#/components/schemas/ToolCall"}},"reasoning_content":{"type":"string","description":"The reasoning trace (`nova` only), separate from the answer."}}},"finish_reason":{"type":"string","enum":["stop","length","tool_calls","content_filter","error"]}}}},"usage":{"$ref":"#/components/schemas/Usage"}}},"ChatCompletionChunk":{"type":"object","required":["id","object","created","model","choices"],"properties":{"id":{"type":"string"},"object":{"type":"string","const":"chat.completion.chunk"},"created":{"type":"integer","description":"Unix time, seconds."},"model":{"type":"string"},"choices":{"type":"array","items":{"type":"object","required":["index","delta"],"properties":{"index":{"type":"integer","const":0},"delta":{"type":"object","properties":{"role":{"type":"string","const":"assistant"},"content":{"type":"string"},"reasoning_content":{"type":"string"},"tool_calls":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/ToolCall"},{"type":"object","required":["index"],"properties":{"index":{"type":"integer","minimum":0}}}]},"description":"Each call arrives whole, in one chunk. The index identifies the call within the reply."}}},"finish_reason":{"type":["string","null"]}}}},"usage":{"$ref":"#/components/schemas/Usage"}}},"Model":{"type":"object","required":["id","object","owned_by","endpoint","context_window","max_output_tokens","reasoning","pricing_eur_per_1m"],"properties":{"id":{"type":"string","enum":["nexi-air-v1","nexi-prime-v1","nexi-nova-v1"]},"object":{"type":"string","const":"model"},"owned_by":{"type":"string","const":"nexichat"},"endpoint":{"type":"string","enum":["/v1/chat/air","/v1/chat/prime","/v1/chat/nova"]},"context_window":{"type":"integer","description":"Tokens."},"max_output_tokens":{"type":"integer"},"reasoning":{"type":"boolean","description":"Whether the model returns `reasoning_content`."},"pricing_eur_per_1m":{"type":"object","required":["inPerM","outPerM"],"properties":{"inPerM":{"type":"number","description":"EUR per 1M input tokens."},"outPerM":{"type":"number","description":"EUR per 1M output tokens."}}}}},"ModelList":{"type":"object","required":["object","data"],"properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Model"}}}},"UserInfo":{"type":"object","required":["sub"],"properties":{"sub":{"type":"string"},"email":{"type":"string","format":"email"},"email_verified":{"type":"boolean"},"name":{"type":"string"},"picture":{"type":"string","format":"uri"},"plano":{"type":"string","description":"The plan identifier (present with the `plano` scope).","enum":["FREE","LITE","PRO","ELITE"]},"plano_pago":{"type":"boolean","description":"Whether the plan is a paid one (present with the `plano` scope)."}}}}}}