# Chat completions

> POST /v1/chat/completions no formato da OpenAI, com streaming, ferramentas, imagens e raciocínio.


A requisição e a resposta do OpenAI Chat Completions, rodando nos modelos da NotHoly. Aponte um SDK da OpenAI para a URL base e não mude mais nada.

```endpoint
POST https://api.notholy.com/v1/chat/completions
```

## Requisição

| Campo | Tipo | Obrigatório | Observações |
|---|---|---|---|
| `model` | string | Sim | Um ID de modelo, por exemplo `notholy-specter`. Veja [Modelos](/pt-br/getting-started/models) |
| `messages` | array | Sim | Papéis `system`, `developer` (lido como `system`), `user`, `assistant` e `tool`. O conteúdo é uma string ou uma lista de partes `text` e `image_url` |
| `max_completion_tokens` / `max_tokens` | number | Não | Limite de saída. Se os dois vierem, vale `max_completion_tokens`. O padrão é a saída máxima do modelo; um valor maior retorna `400` |
| `stream` | boolean | Não | `true` retorna server-sent events. Veja [Streaming](/pt-br/guides/streaming) |
| `tools` | array | Não | Só ferramentas do tipo function. Outros tipos retornam `400 unsupported_parameter` |
| `tool_choice` | string ou object | Não | Repassado ao modelo quando `tools` está definido |
| `reasoning_effort` | string | Não | Um dos `reasoning_efforts` do modelo. Qualquer outro valor retorna `400`. Veja [Raciocínio](/pt-br/guides/reasoning) |
| `response_format` | object | Não | Repassado ao modelo |
| `temperature`, `top_p`, `stop` | number, number, string ou array | Não | Repassados ao modelo |
| `n` | number | Não | Só `1` |

Campos desconhecidos são ignorados. Imagens são aceitas nos modelos cujo `input_modalities` inclui `image`; nos outros, retornam `400 unsupported_parameter`.

## Resposta sem streaming

```json
{
  "id": "chatcmpl-8Kp2c1Vx",
  "object": "chat.completion",
  "created": 1793836800,
  "model": "notholy-specter",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "An IDOR lets a user read or change another user's object by changing its ID…",
        "refusal": null,
        "reasoning_content": "…"
      },
      "finish_reason": "stop",
      "logprobs": null
    }
  ],
  "usage": {
    "prompt_tokens": 42,
    "completion_tokens": 118,
    "total_tokens": 160,
    "prompt_tokens_details": { "cached_tokens": 0 },
    "completion_tokens_details": { "reasoning_tokens": 64 }
  }
}
```

- `reasoning_content` só aparece quando o modelo raciocinou.
- Com tool calls, `message.tool_calls` traz itens `{id, type: "function", function: {name, arguments}}`, `finish_reason` é `tool_calls` e `content` é `null` quando não há texto.
- `finish_reason` é `stop`, `length`, `tool_calls` ou `content_filter`.
- Com streaming, o último chunk antes de `data: [DONE]` sempre traz `usage`, mandando ou não `stream_options.include_usage`.

## Exemplo

```bash test
curl https://api.notholy.com/v1/chat/completions \
  -H "Authorization: Bearer $NOTHOLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "notholy-specter",
    "messages": [
      {"role": "system", "content": "You are reviewing code for an authorized assessment."},
      {"role": "user", "content": "What does a missing ownership check in GET /api/invoices/:id lead to?"}
    ]
  }'
```

```python test
import os
from openai import OpenAI

client = OpenAI(base_url="https://api.notholy.com/v1", api_key=os.environ["NOTHOLY_API_KEY"])

r = client.chat.completions.create(
    model="notholy-specter",
    messages=[{"role": "user", "content": "List three checks for an SSRF fix."}],
    max_tokens=400,
)
print(r.choices[0].message.content)
print(r.usage.prompt_tokens, r.usage.completion_tokens)
```

```js test
import OpenAI from 'openai';

const client = new OpenAI({ baseURL: 'https://api.notholy.com/v1', apiKey: process.env.NOTHOLY_API_KEY });

const r = await client.chat.completions.create({
  model: 'notholy-specter',
  messages: [{ role: 'user', content: 'List three checks for an SSRF fix.' }],
  max_tokens: 400,
});
console.log(r.choices[0].message.content);
```

## Cabeçalhos de resposta

| Cabeçalho | Significado |
|---|---|
| `x-request-id` | Mande para o suporte quando reportar um problema |
| `x-notholy-concurrency-limit` | Quantas requisições você pode rodar ao mesmo tempo |
| `x-notholy-concurrency-remaining` | Vagas livres quando esta requisição começou |
| `x-notholy-credits-remaining` | Créditos disponíveis em USD quando esta requisição começou |

## Erros

`400` corpo ou parâmetro inválido · `401` chave ausente ou inválida · `402` créditos ou orçamento da chave insuficientes · `403` a chave não pode usar este modelo · `404` modelo desconhecido · `429` limite de taxa ou de concorrência (respeite o `retry-after`) · `502`/`504` o modelo falhou ou estourou o tempo · `503` modelo indisponível. Tabela completa: [Erros](/pt-br/guides/errors).
