> ## Documentation Index
> Fetch the complete documentation index at: https://symphony-docs.fcamara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Criando projetos com a API

> Guia completo para construir scripts, backends, automações e assistentes em cima do Symphony, com receitas prontas.

## O que é

Este guia é para quem vai **construir alguma coisa** em cima do Symphony: um script, um backend, uma automação, um assistente interno, um agente que roda sozinho. Ele parte do princípio de que você sabe programar, mas **não tem acesso ao código do Symphony**. Tudo o que está aqui você consegue fazer só com a sua chave de API e um cliente HTTP.

Se você quer apenas o passo a passo rápido para gerar a chave e fazer a primeira chamada, comece por [Conectar via API](/desenvolvedores/api/conectar-api) e volte aqui quando for montar o projeto de verdade.

<Info>
  **A ideia central:** a API de conversa do Symphony **fala o mesmo protocolo da OpenAI**. Se o seu projeto já usa a biblioteca `openai` (Python, Node, LangChain, LlamaIndex, Vercel AI SDK…), você troca a URL e a chave e o resto do código continua igual. A diferença é que passa a usar os modelos, os agentes e as bases de conhecimento da sua empresa, com o consumo controlado pela sua organização.
</Info>

## Pré-requisitos

* Conta ativa no **AI Symphony**.
* Uma **chave de API** (começa com `sk-`).
* O **ID da sua empresa**, para o header `X-Enterprise-Id`.
* Um cliente HTTP qualquer (cURL, `requests`, `fetch`, Postman) ou uma biblioteca compatível com OpenAI.

## 1. Preparando o acesso

### 1.1 Gerar a sua chave de API

<Steps>
  <Step title="Clique no seu nome de usuário">
    Abra o **Symphony** e clique no seu nome, no canto inferior esquerdo.
  </Step>

  <Step title="Abra as chaves de API">
    Vá em **Configurações** → aba **Conta** (ou **Conexões**, dependendo da versão) → **Chaves de API**.
  </Step>

  <Step title="Gere e copie a chave">
    Clique para gerar a chave e **copie o valor completo**. Ele começa com `sk-`.
  </Step>
</Steps>

<Warning>
  **Você só tem uma chave.** Gerar uma nova **substitui** a anterior: toda integração que usava a chave antiga para de funcionar na mesma hora. Antes de clicar em gerar de novo, confirme que nenhum projeto em produção depende da chave atual.
</Warning>

### 1.2 Descobrir o ID da sua empresa

O Symphony atende várias empresas, e **cada uma tem o próprio catálogo de modelos, agentes e bases de conhecimento**. Como a chave de API não carrega essa informação, você precisa dizer em qual empresa está trabalhando, pelo header:

```
X-Enterprise-Id: fcamara-0000-0000-0000-000000000001
```

Peça esse ID ao **administrador do Symphony na sua empresa**. Ele encontra o valor no painel administrativo.

<Warning>
  Este é o erro número 1 de quem começa: **sem esse header, você recebe "modelo não encontrado" para um modelo que você vê funcionando na tela.** O sistema não sabe de qual empresa você está falando e cai no catálogo padrão do ambiente, que provavelmente não é o seu.
</Warning>

### 1.3 Os três headers que você vai usar sempre

| Header | Valor | Quando |
| - | - | - |
| `Authorization` | `Bearer sk-...` | Sempre |
| `X-Enterprise-Id` | ID da sua empresa | Sempre |
| `Content-Type` | `application/json` | Em requisições com corpo JSON |

É só isso. **Você não precisa de cookies nem de token CSRF.** Esses passos aparecem em documentos que descrevem como o *navegador* conversa com o Symphony. Quem integra com chave de API não passa por eles.

**Endereço base:** `https://symphony.fcamara.com`

## 2. Sua primeira chamada

```bash theme={null}
curl -X POST https://symphony.fcamara.com/api/chat/completions \
  -H "Authorization: Bearer sk-..." \
  -H "X-Enterprise-Id: SEU_ID_DE_EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "azure.gpt-5-chat",
    "messages": [
      { "role": "user", "content": "Explique o que é uma API em duas frases." }
    ]
  }'
```

A resposta vem no formato clássico da OpenAI:

```json theme={null}
{
  "id": "chatcmpl-DXttDwwlWkL6YkzARekAPP2cuFUX2",
  "object": "chat.completion",
  "model": "azure.gpt-5-chat",
  "choices": [
    {
      "index": 0,
      "finish_reason": "stop",
      "message": { "role": "assistant", "content": "Uma API é..." }
    }
  ],
  "usage": { "prompt_tokens": 24, "completion_tokens": 61, "total_tokens": 85 }
}
```

<Tip>
  **Só `model` e `messages` são obrigatórios.** Se você viu em algum lugar que precisa mandar `id`, `chat_id` e `session_id`, isso descreve o que a interface do Symphony envia, e não é exigência da API. Você só precisa desses campos se quiser que a conversa apareça no histórico da plataforma (veja a seção **8. Mantendo o histórico da conversa**).
</Tip>

### O que mais dá para mandar

| Campo | Para quê |
| - | - |
| `stream` | `true` devolve a resposta em tempo real, token a token |
| `temperature` | Quão criativa é a resposta |
| `max_tokens` | Limite de tamanho da resposta |
| `top_p` | Controle alternativo de aleatoriedade |
| `stop` | Sequências que interrompem a geração |
| `stream_options` | `{"include_usage": true}` para receber a contagem de tokens no streaming |
| `response_format` | Força a resposta a seguir um formato JSON (veja a receita **5.3**) |
| `tools` / `tool_choice` | Function calling: o modelo pede, o seu código executa |
| `files` | Anexar documentos ou bases de conhecimento (veja a seção **7**) |
| `features` | Ligar busca na web, geração de imagem ou interpretador de código, quando a sua conta tem permissão |
| `chat_id` / `id` | Gravar a conversa no histórico do Symphony |

<Note>
  Nem todo modelo aceita todo parâmetro. Alguns recusam `stop`, outros só aceitam a temperatura padrão. Quando isso acontece, o Symphony devolve uma mensagem explicando exatamente qual parâmetro incomodou.
</Note>

## 3. Descobrindo os modelos e agentes que você pode usar

**Nunca chute o valor de `model`.** Os identificadores são definidos pela sua empresa e mudam de ambiente para ambiente.

```bash theme={null}
curl https://symphony.fcamara.com/api/models \
  -H "Authorization: Bearer sk-..." \
  -H "X-Enterprise-Id: SEU_ID_DE_EMPRESA"
```

```json theme={null}
{
  "data": [
    { "id": "azure.gpt-5-chat", "name": "Azure gpt-5-chat", "owned_by": "openai" },
    { "id": "fc-code-assistant", "name": "Code Vader", "info": { "base_model_id": "azure.gpt-5-chat" } }
  ]
}
```

Três coisas importantes nessa lista:

* **Ela já vem filtrada pelo que a sua conta pode usar.** Modelo desativado, agente que ninguém compartilhou com você e modelo fora do catálogo da empresa simplesmente não aparecem. Se algo está na sua tela mas não está aqui, o problema quase sempre é o `X-Enterprise-Id`.
* **Agentes aparecem junto com os modelos.** Todo item que tem `info.base_model_id` é um agente criado na plataforma. Usar um agente é o atalho mais poderoso da API: o prompt de sistema, as bases de conhecimento e as ferramentas configuradas nele são aplicados automaticamente, sem você mandar nada além do `model`.
* **Copie o `id` exatamente como veio.** Usar o nome do modelo no fornecedor original (`gemini-2.5-pro`) em vez do identificador do Symphony (`FC.gemini-2.5-pro`) é a causa mais comum de erro 404.

## 4. Conectando o seu projeto com uma biblioteca pronta

Como a API é compatível com a da OpenAI, você não precisa escrever cliente HTTP na mão. Aponte a `base_url` para `https://symphony.fcamara.com/api`: a biblioteca acrescenta `/chat/completions` sozinha.

<CodeGroup>
  ```python Python theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="sk-...",
      base_url="https://symphony.fcamara.com/api",
      default_headers={"X-Enterprise-Id": "SEU_ID_DE_EMPRESA"},
  )

  resposta = client.chat.completions.create(
      model="azure.gpt-5-chat",
      messages=[{"role": "user", "content": "Explique o que é uma API."}],
  )
  print(resposta.choices[0].message.content)
  ```

  ```ts Node / TypeScript theme={null}
  import OpenAI from 'openai';

  const client = new OpenAI({
  	apiKey: process.env.SYMPHONY_API_KEY,
  	baseURL: 'https://symphony.fcamara.com/api',
  	defaultHeaders: { 'X-Enterprise-Id': process.env.SYMPHONY_ENTERPRISE_ID! }
  });

  const resposta = await client.chat.completions.create({
  	model: 'azure.gpt-5-chat',
  	messages: [{ role: 'user', content: 'Explique o que é uma API.' }]
  });
  console.log(resposta.choices[0].message.content);
  ```

  ```python LangChain theme={null}
  from langchain_openai import ChatOpenAI

  llm = ChatOpenAI(
      model="azure.gpt-5-chat",
      api_key="sk-...",
      base_url="https://symphony.fcamara.com/api",
      default_headers={"X-Enterprise-Id": "SEU_ID_DE_EMPRESA"},
  )
  ```

  ```python Python (requests) theme={null}
  import os
  import requests

  BASE_URL = "https://symphony.fcamara.com"
  HEADERS = {
      "Authorization": f"Bearer {os.environ['SYMPHONY_API_KEY']}",
      "X-Enterprise-Id": os.environ["SYMPHONY_ENTERPRISE_ID"],
      "Content-Type": "application/json",
  }

  resposta = requests.post(
      f"{BASE_URL}/api/chat/completions",
      headers=HEADERS,
      json={
          "model": "azure.gpt-5-chat",
          "messages": [{"role": "user", "content": "Explique o que é uma API."}],
      },
      timeout=(10, 300),  # 10s para conectar, 300s para a resposta terminar
  )
  resposta.raise_for_status()
  print(resposta.json()["choices"][0]["message"]["content"])
  ```
</CodeGroup>

### No VS Code / JetBrains

Se o seu "projeto" é programar com assistência de IA no editor, não precisa de código: veja [Plug-in para IDE](/desenvolvedores/plugin-ide).

## 5. Receitas de projeto

### 5.1 Usar um agente da empresa como motor do seu produto

É a receita mais subestimada. Em vez de manter um prompt gigante dentro do seu código, crie um **agente** na interface do Symphony, com prompt de sistema, base de conhecimento e ferramentas, e chame-o pelo `id`:

```json theme={null}
{
  "model": "fc-code-assistant",
  "messages": [{ "role": "user", "content": "Revise esta função e aponte riscos." }]
}
```

**Por que vale a pena:** quem ajusta o comportamento passa a ser a pessoa de negócio ou o especialista, direto na interface, sem redeploy do seu sistema. O seu código fica responsável só pela integração.

### 5.2 Assistente com memória de conversa

A API é **sem estado por padrão**: cada chamada é independente. Para manter contexto, mande o histórico:

```python theme={null}
historico = [{"role": "system", "content": "Você é um assistente de suporte técnico. Seja objetivo."}]

def perguntar(pergunta):
    historico.append({"role": "user", "content": pergunta})
    r = client.chat.completions.create(model="azure.gpt-5-chat", messages=historico)
    resposta = r.choices[0].message.content
    historico.append({"role": "assistant", "content": resposta})
    return resposta
```

<Warning>
  **O histórico cresce e o custo cresce junto**: você paga tokens por tudo que reenvia. Em conversas longas, corte as mensagens mais antigas ou peça ao modelo um resumo periódico do que foi discutido e substitua o trecho antigo por esse resumo. Se estourar a janela do modelo, você recebe um erro de limite de contexto.

  Existe uma alternativa em que o Symphony guarda o histórico para você. Veja a seção **8. Mantendo o histórico da conversa**.
</Warning>

### 5.3 Extração de dados estruturados

Quando você precisa de JSON confiável em vez de texto, use `response_format`:

```json theme={null}
{
  "model": "azure.gpt-5-chat",
  "messages": [
    { "role": "user", "content": "Extraia nome e cargo: 'Ana Souza, gerente de produto'." }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "pessoa",
      "schema": {
        "type": "object",
        "properties": {
          "nome": { "type": "string" },
          "cargo": { "type": "string" }
        },
        "required": ["nome", "cargo"],
        "additionalProperties": false
      }
    }
  }
}
```

Nem todo modelo suporta o recurso. Os que não suportam devolvem texto livre. **Sempre valide o JSON no seu lado** antes de confiar nele.

### 5.4 Perguntas e respostas sobre os documentos da empresa (RAG)

Crie a base de conhecimento pela interface (é mais simples) e depois use o `id` dela nas suas chamadas. Veja a seção **7. Trabalhando com documentos e bases de conhecimento**.

### 5.5 Deixar o modelo chamar funções do seu sistema

O padrão de *function calling* funciona igual ao da OpenAI: você descreve as funções, o modelo devolve `tool_calls`, **o seu código executa** e devolve o resultado como uma mensagem de papel `tool`.

```json theme={null}
{
  "model": "azure.gpt-5-chat",
  "messages": [{ "role": "user", "content": "Qual o status do pedido 4821?" }],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "consultar_pedido",
        "description": "Consulta o status de um pedido pelo número",
        "parameters": {
          "type": "object",
          "properties": { "numero": { "type": "string" } },
          "required": ["numero"]
        }
      }
    }
  ],
  "tool_choice": "auto"
}
```

<Note>
  Não confunda com as **Ferramentas (Tools)** cadastradas no Symphony ([Ferramentas](/guia/ferramentas)): aquelas a plataforma executa sozinha, e vêm de graça quando você usa um agente que as tem configuradas. As `tools` desta receita são funções do **seu** sistema, que só o seu código sabe executar.
</Note>

### 5.6 Processamento em lote

Para classificar, resumir ou traduzir muitos itens:

* **Limite a concorrência.** Comece com 3 a 5 requisições em paralelo e aumente devagar, observando erros `429`.
* **Salve o progresso** a cada item, para poder retomar de onde parou.
* **Faça retry só nos erros certos** (veja a seção **9. Erros**).
* **Acompanhe o consumo** em `GET /api/v1/consumption/me`. Um lote grande pode consumir o seu limite mensal antes de terminar.

## 6. Respostas em tempo real (streaming)

Com `"stream": true`, a resposta chega em pedaços conforme é gerada. É o que dá a sensação de "digitando" nas interfaces de chat.

```python theme={null}
for chunk in client.chat.completions.create(
    model="azure.gpt-5-chat",
    messages=[{"role": "user", "content": "Conte até cinco."}],
    stream=True,
):
    pedaco = chunk.choices[0].delta.content
    if pedaco:
        print(pedaco, end="", flush=True)
```

Se for ler o fluxo na mão, o formato é uma sequência de linhas `data: {...}`, encerrada por `data: [DONE]`.

<Warning>
  **Três armadilhas que pegam quase todo mundo:**

  1. **Um erro pode chegar depois do `200 OK`.** O código de status é enviado antes de a resposta começar. Se o modelo falhar no meio da geração, a falha vem como um evento dentro do fluxo, não como um erro HTTP. Trate isso.
  2. **Não conte com um pedaço por token.** Os eventos podem chegar agrupados ou partidos pela rede. Acumule por linha antes de interpretar.
  3. **`[DONE]` é sinalização, não conteúdo.** Ignore-o e ignore as linhas em branco.
</Warning>

## 7. Trabalhando com documentos e bases de conhecimento

### 7.1 Enviar um documento junto com a pergunta

<Steps>
  <Step title="Suba o arquivo">
    ```bash theme={null}
    curl -X POST https://symphony.fcamara.com/api/v1/files/ \
      -H "Authorization: Bearer sk-..." \
      -H "X-Enterprise-Id: SEU_ID_DE_EMPRESA" \
      -F "file=@contrato.pdf"
    ```

    ```json theme={null}
    { "id": "3f1c...", "filename": "contrato.pdf", "data": { "status": "processing" } }
    ```
  </Step>

  <Step title="Espere o processamento terminar">
    O upload responde na hora, mas a leitura do arquivo (extração de texto, OCR) continua rodando em segundo plano. Consulte até o status sair de `processing`:

    ```bash theme={null}
    curl https://symphony.fcamara.com/api/v1/files/3f1c... \
      -H "Authorization: Bearer sk-..." -H "X-Enterprise-Id: SEU_ID_DE_EMPRESA"
    # data.status: "processing" → "ready" ou "error"
    ```
  </Step>

  <Step title="Anexe o arquivo à pergunta">
    ```json theme={null}
    {
      "model": "azure.gpt-5-chat",
      "messages": [{ "role": "user", "content": "Resuma os riscos deste contrato." }],
      "files": [{ "type": "file", "id": "3f1c..." }]
    }
    ```
  </Step>
</Steps>

<Warning>
  **PDFs grandes ou digitalizados podem levar minutos.** Se você anexar o arquivo enquanto ele ainda está `processing`, o modelo responde como se o documento estivesse vazio. Esse é um bug silencioso, difícil de descobrir depois.
</Warning>

Documentos anexados assim entram **inteiros** no contexto. É o comportamento certo quando o arquivo *é* o assunto da conversa.

### 7.2 Usar uma base de conhecimento

Bases de conhecimento servem para o caso oposto: muito conteúdo, do qual só os trechos relevantes devem entrar em cada pergunta.

Crie a base pela interface ([Base de conhecimento](/guia/base-conhecimento)), copie o `id` dela e use:

```json theme={null}
{
  "model": "azure.gpt-5-chat",
  "messages": [{ "role": "user", "content": "Qual é a política de férias?" }],
  "files": [{ "type": "collection", "id": "ID_DA_BASE" }]
}
```

O Symphony busca os trechos relevantes e os injeta no contexto automaticamente. Para listar as bases que você enxerga, use `GET /api/v1/knowledge/list/compact` (veja a [Referência da API](/desenvolvedores/api/referencia-da-api)).

<Tip>
  **Base grande responde parcialmente a perguntas amplas.** Quando a base não cabe na janela do modelo, só os trechos mais relevantes entram. Perguntas do tipo "resuma tudo" ou "quantos documentos falam sobre X" tendem a sair incompletas. Prefira perguntas específicas, ou um modelo com janela de contexto maior.
</Tip>

Se você usa um **agente** que já tem base configurada, não precisa mandar `files`: ele traz a base junto.

## 8. Mantendo o histórico da conversa

Por padrão nada é gravado: você manda o histórico a cada chamada e o Symphony não guarda nada.

Se você quiser que as conversas do seu projeto apareçam no histórico da plataforma (para auditoria, para o usuário continuar a conversa pela interface, ou só para não ter que gerenciar histórico), crie um chat e reutilize o `chat_id`:

```bash theme={null}
curl -X POST https://symphony.fcamara.com/api/v1/chats/new \
  -H "Authorization: Bearer sk-..." \
  -H "X-Enterprise-Id: SEU_ID_DE_EMPRESA" -H "Content-Type: application/json" \
  -d '{"chat": {"title": "Meu projeto", "models": ["azure.gpt-5-chat"], "messages": []}}'
# → { "id": "ca49f9ae-...", ... }
```

E então, nas chamadas seguintes:

```json theme={null}
{
  "model": "azure.gpt-5-chat",
  "chat_id": "ca49f9ae-...",
  "messages": [{ "role": "user", "content": "E quanto ao prazo?" }]
}
```

**O detalhe que vale ouro:** se você mandar o `chat_id` e **apenas a nova pergunta do usuário** (nenhuma mensagem de papel `assistant` no array), o Symphony recupera o histórico gravado e o coloca antes da sua mensagem. Ou seja: **dá para manter uma conversa longa mandando só a pergunta nova.**

Duas condições para isso funcionar:

* Se o seu array já contém alguma mensagem `assistant`, o Symphony entende que você está gerenciando o histórico e não injeta nada.
* Numa conversa nova, ainda sem nenhuma resposta gravada, também não há o que injetar.

## 9. Erros: como interpretar e o que fazer

As respostas de erro trazem um código estável, além do texto. Dependendo do endpoint, o código vem em `error.code` ou em `detail.code`:

```json theme={null}
{
  "detail": {
    "code": "MODEL_UNAVAILABLE",
    "message": "It may have been removed or may not be available to your account.",
    "action": "Choose another model to continue.",
    "retryable": false,
    "request_id": "abc123..."
  }
}
```

Os formatos possíveis e um exemplo de código para ler os dois estão em [Erros e limites](/desenvolvedores/api/erros-e-limites).

<Tip>
  **Programe em cima do `code`, nunca do texto da mensagem.** O texto muda com o idioma e com melhorias de redação; o código não. E **guarde o `request_id` nos seus logs**: é por ele que o time do Symphony consegue rastrear o que aconteceu quando você abrir um chamado.
</Tip>

### O que cada código de status significa aqui

| Código | O que aconteceu | O que fazer |
| -: | - | - |
| `400` | Corpo inválido, ou um parâmetro que o modelo não aceita | Corrija o payload. Repetir não resolve |
| `401` | Chave inválida, revogada ou substituída | Gere a chave de novo e atualize **todas** as integrações |
| `402` | Seu **limite de consumo** foi atingido, ou seu acesso está bloqueado | Fale com o administrador. Repetir nunca resolve |
| `403` | Sem permissão, ou chaves de API desabilitadas no ambiente | Verifique o `X-Enterprise-Id` e confirme a política com o administrador |
| `404` | Modelo fora do catálogo da empresa, ou recurso inexistente | Confira o `X-Enterprise-Id` e refaça `GET /api/models` |
| `409` | O agente existe, mas o modelo base dele foi removido | Corrija o agente na interface do Symphony |
| `429` | Excesso de requisições, ou cota da empresa esgotada | Espere e tente de novo com backoff |
| `5xx` | Falha temporária do serviço ou do fornecedor de IA | Retry com backoff exponencial |

### Códigos que você mais vai encontrar

| `code` | Significado |
| - | - |
| `MODEL_UNAVAILABLE` | O `model` não existe no catálogo da sua empresa, ou você não tem acesso a ele |
| `AGENT_BASE_MODEL_UNAVAILABLE` | O agente perdeu o modelo base e precisa ser corrigido na interface |
| `CONTEXT_LIMIT_EXCEEDED` | Sua pergunta + histórico + arquivos passaram do que o modelo aguenta |
| `RATE_LIMITED` | O fornecedor de IA limitou temporariamente |
| `INVALID_PARAMETER` | Um parâmetro está fora do que o modelo aceita |
| `TIMEOUT`, `NETWORK_ERROR`, `SERVICE_UNAVAILABLE` | Falha passageira: vale retry |
| `STREAM_INTERRUPTED` | O fluxo terminou antes de a resposta acabar |
| `CONSUMPTION_LIMIT_REACHED` | Seu limite mensal de consumo acabou |
| `CONSUMPTION_BLOCKED` | Seu acesso à IA está bloqueado administrativamente |
| `FILE_TOO_LARGE`, `FILE_FORMAT_UNSUPPORTED`, `FILE_PROCESSING_FAILED` | Problemas com o arquivo enviado |

### Política de retry

* **Repita** em `429` e `5xx`, com backoff exponencial e um pouco de aleatoriedade, respeitando o cabeçalho `Retry-After` quando ele vier.
* **Nunca repita** `400`, `401`, `402`, `403`, `404` e `409`. Nenhum deles se resolve sozinho, e insistir só queima a sua cota de requisições.

## 10. Limites de uso

São **três limites diferentes**, e confundi-los leva a retry inútil:

<AccordionGroup>
  <Accordion title="1. Limite de requisições (429 com Retry-After)" icon="gauge">
    Um teto de requisições por minuto, contado **por endereço IP**. As respostas trazem `X-RateLimit-Limit`, `X-RateLimit-Remaining` e `X-RateLimit-Reset` para você se orientar. Como o limite é por IP, várias integrações saindo pela mesma rede dividem a mesma cota. Espere e tente de novo.
  </Accordion>

  <Accordion title="2. Cota da empresa (429, com quota_exceeded no corpo)" icon="building">
    Cota de tokens contratada pela sua organização. O corpo informa quando ela renova. Retry só resolve depois disso.
  </Accordion>

  <Accordion title="3. Seu limite de consumo (402)" icon="wallet">
    Teto de gasto mensal do seu usuário, ou bloqueio administrativo. É verificado **antes** de qualquer chamada ao modelo, então nada é gasto. Consulte a qualquer momento:

    ```bash theme={null}
    curl https://symphony.fcamara.com/api/v1/consumption/me \
      -H "Authorization: Bearer sk-..." -H "X-Enterprise-Id: SEU_ID_DE_EMPRESA"
    ```

    Repetir a requisição nunca resolve um `402`. Só um administrador aumentando o seu limite, ou a virada do mês.
  </Accordion>
</AccordionGroup>

**Tempo de resposta:** configure timeouts generosos. Modelos grandes, com muito contexto ou arquivos anexados, podem levar minutos. Um timeout curto no seu cliente vai cortar respostas legítimas.

## 11. Boas práticas

<Columns cols={2}>
  <Card title="Faça" icon="circle-check">
    * **Guarde a chave em variável de ambiente ou cofre de segredos.**
    * **Use sempre HTTPS.**
    * **Peça uma conta técnica dedicada** ao administrador, para cada integração. Isso evita que regenerar a sua chave pessoal derrube a automação, e mantém o consumo do projeto separado do seu uso individual nos relatórios da empresa.
    * **Fixe o `X-Enterprise-Id` por configuração**, nunca deduzido em tempo de execução.
    * **Chame `GET /api/models` uma vez na inicialização** e valide que o modelo que você espera existe. Falhar no start é muito melhor que falhar no meio de um lote.
    * **Registre o `request_id`** de toda resposta de erro.
    * **Meça o consumo** antes de escalar o volume.
  </Card>

  <Card title="Não faça" icon="circle-xmark">
    * **Não coloque a chave em código que roda no navegador.** Ela dá acesso total à sua conta, sem restrição de origem ou escopo. Se o seu projeto tem front-end, a chave fica no *seu* backend, que faz a chamada.
    * Não versione a chave em repositório, nem a coloque em URL, log ou parâmetro de query.
    * Não repita requisições que falharam com erro `4xx` determinístico.
    * Não trate o `200 OK` do upload de arquivo como "pronto para usar".
    * Não assuma que um modelo que funciona hoje vai existir amanhã: o catálogo é gerenciado pela sua empresa.
  </Card>
</Columns>

## 12. Perguntas frequentes

<AccordionGroup>
  <Accordion title="Preciso mesmo do X-Enterprise-Id?">
    Sim, sempre que o ambiente atende mais de uma empresa. Sem ele, o Symphony não sabe de qual catálogo você está falando e você recebe "modelo não encontrado" para modelos que existem.
  </Accordion>

  <Accordion title="Preciso do token CSRF?">
    Não, quando você usa chave de API sem enviar cookies. Aquele passo vale para quem reproduz o fluxo do navegador com token de sessão.
  </Accordion>

  <Accordion title="Minha chave expira?">
    Não. Mas ela é **substituída** se você gerar outra, e pode ser revogada pelo administrador.
  </Accordion>

  <Accordion title="Posso ter mais de uma chave, uma para cada projeto?">
    Não hoje: é uma chave por usuário. A saída é pedir uma **conta técnica separada** para cada integração.
  </Accordion>

  <Accordion title="A chave tem permissões próprias?">
    Não. Ela vale exatamente o que a sua conta vale: mesmos modelos, mesmos agentes, mesmas bases, mesmo limite de consumo. Não existe chave "somente leitura" ou restrita a um agente.
  </Accordion>

  <Accordion title="Qual modelo devo usar?">
    Um dos `id` retornados por `GET /api/models`. Se existe um agente da empresa para o seu caso de uso, prefira o agente: ele já vem com prompt, base e ferramentas prontos.
  </Accordion>

  <Accordion title="Consigo usar as bibliotecas e frameworks de IA que já conheço?">
    Sim, qualquer um compatível com o protocolo da OpenAI: SDK `openai`, LangChain, LlamaIndex, Vercel AI SDK, Continue.dev e afins.
  </Accordion>

  <Accordion title="Como sei quanto estou gastando?">
    `GET /api/v1/consumption/me`. Os relatórios completos ficam com o administrador da empresa.
  </Accordion>

  <Accordion title="Existe documentação interativa (Swagger)?">
    Não em produção. Todos os endpoints disponíveis estão descritos na [Referência da API](/desenvolvedores/api/referencia-da-api).
  </Accordion>

  <Accordion title="Recebo 401 mesmo com a chave que acabei de copiar.">
    Confira se o header está exatamente como `Authorization: Bearer sk-...`, sem aspas, sem espaço extra e com a chave completa. Confira também se ninguém gerou uma chave nova na mesma conta.
  </Accordion>

  <Accordion title="A resposta ignora o arquivo que anexei.">
    O arquivo provavelmente ainda estava sendo processado. Consulte `GET /api/v1/files/{id}` até o status ficar `ready`.
  </Accordion>
</AccordionGroup>

## 13. Limitações conhecidas

* **Uma única chave por usuário**, sem nome, sem escopo próprio e sem rotação sem interrupção. Gerar uma nova derruba a anterior imediatamente.
* **A chave equivale à sua conta inteira.** Não há credencial de serviço com permissões reduzidas.
* **O limite de requisições é por IP, não por chave.** Integrações que saem pelo mesmo IP dividem a cota.
* **Não há notificação (webhook) de conclusão** para quem consome a API: a integração é síncrona ou por streaming.
* **Não existe biblioteca oficial do Symphony.** Use uma compatível com OpenAI.
* **Não há Swagger em produção.**
* **O catálogo de modelos é gerenciado pela sua empresa** e pode mudar sem aviso para quem integra.
* **Algumas funcionalidades dependem de permissão** (busca na web, geração de imagem, interpretador de código) e podem estar desligadas para a sua conta.

## 14. Checklist antes de colocar em produção

* [ ] Chave em variável de ambiente ou cofre, nunca no código nem no front-end
* [ ] Idealmente, uma conta técnica dedicada para esta integração
* [ ] `X-Enterprise-Id` vindo de configuração, presente em todas as chamadas
* [ ] Modelo validado na inicialização com `GET /api/models`
* [ ] Tratamento de erro pelo `code` (em `error.code` ou `detail.code`), não pelo texto
* [ ] Retry com backoff apenas em `429` e `5xx`
* [ ] `request_id` gravado nos seus logs de erro
* [ ] Timeout do cliente compatível com respostas longas
* [ ] Se usa streaming: `[DONE]`, eventos partidos e erro depois do `200` tratados
* [ ] Se usa arquivos: espera o status `ready` antes de anexar
* [ ] Se a conversa é longa: estratégia definida para o histórico não estourar a janela
* [ ] Consumo monitorado em `GET /api/v1/consumption/me`

## Documentos relacionados

* [Conectar via API](/desenvolvedores/api/conectar-api): início rápido
* [Referência da API](/desenvolvedores/api/referencia-da-api): todos os endpoints disponíveis, com parâmetros e respostas
* [Erros e limites](/desenvolvedores/api/erros-e-limites): catálogo completo de códigos de erro e limites de uso
* [Ambientes e suporte](/desenvolvedores/api/ambientes-e-suporte): produção, homologação e como pedir ajuda
* [Plug-in para IDE](/desenvolvedores/plugin-ide): IA no seu editor, sem escrever integração
* [Agentes](/guia/agentes): criar o agente que o seu projeto vai chamar
* [Base de conhecimento](/guia/base-conhecimento): preparar os documentos para RAG
* [Ferramentas (Tools)](/guia/ferramentas): ferramentas executadas pela plataforma
* [Configurações da conta](/guia/configuracoes): onde fica a sua chave de API


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.