Limites e erros
Limites de chamadas
Seção intitulada “Limites de chamadas”Toda rota /v1 e todo pedido ao MCP passam por dois baldes, os dois na mesma conta:
- o da empresa, que soma todos os tokens dela;
- o do token, para um script não esgotar a empresa sozinho.
Os baldes são por classe de operação. A classe de cada rota e de cada ferramenta está na Referência da API (x-nexo-operation.rateClass) e na Referência do MCP.
| Classe | O que conta | Empresa | Token | Rajada empresa / token |
|---|---|---|---|---|
read |
Leituras, listas, prévias, consultas de exportação; cada mensagem ao MCP | 300/min | 120/min | 60 / 30 |
content |
Conteúdo de conversa: leitura, trechos, transcrição, sessão de WhatsApp | 120/min | 60/min | 30 / 15 |
export |
Pedir uma exportação (POST /v1/exports) |
10/hora | 5/hora | 3 / 2 |
file |
Arquivo de uma conversa, downloads e envio, confirmação e download de materiais | 60/min | 30/min | 15 / 10 |
write |
Mudanças na Calibração e na Base de conhecimento | 60/min | 30/min | 20 / 10 |
crm_write |
Escrita no CRM (próximo passo de um negócio) | 30/min | 15/min | 10 / 5 |
Os números são iguais para todas as empresas, e GET /v1/me devolve os que valem para o seu token em rateLimits. O limite é contado no esquema de balde de tokens (GCRA): a rajada é quantas chamadas seguidas cabem antes de o ritmo valer. Um 429 pela empresa não gasta o balde do token, e vice-versa.
No MCP, cada mensagem JSON-RPC do pedido gasta uma unidade de read antes de qualquer coisa (num lote, uma por mensagem), e um tools/call gasta também a classe da ferramenta.
Cabeçalhos
Seção intitulada “Cabeçalhos”Toda resposta traz o estado dos dois baldes da classe, no formato do rascunho da IETF:
RateLimit-Policy: "read";q=300;w=60, "read-credential";q=120;w=60RateLimit: "read";r=59;t=1, "read-credential";r=29;t=1q é a cota, w a janela em segundos, r o que resta e t os segundos até o balde se recompor.
Quando passa do limite
Seção intitulada “Quando passa do limite”429 com Retry-After (segundos) e o motivo em meta:
{ "type": "public_api.rate_limited", "title": "PublicRateLimitedError", "status": 429, "detail": "Muitas chamadas em pouco tempo. Tente de novo em 12 segundos.", "requestId": "019fcae7-…", "meta": { "class": "read", "limitedBy": "credential", "limit": 120, "windowSeconds": 60, "retryAfterSeconds": 12 }}limitedBy diz qual balde recusou: company (a empresa inteira) ou credential (só este token). Espere Retry-After e tente de novo. No MCP, um tools/call recusado volta como resultado com isError e esse mesmo corpo, para o modelo ler e esperar.
Outros limites
Seção intitulada “Outros limites”- Downloads: além da classe
file, os downloads (PDFs de transcrição e link do áudio) gastam a mesma cota do app, 60 por pessoa e 600 por empresa a cada 10 minutos; o pedido de ZIP, 10 por pessoa e 60 por empresa por hora. Passou:429 core.too_many_attempts. - Exportações: no máximo duas em andamento por empresa; a terceira recebe
429 exports.too_many_running. - Token inválido: 20 falhas a cada 5 minutos por endereço; depois,
429 public_api.auth_failures_limited. - Tokens: no máximo 20 válidos por pessoa (Autenticação).
Todo erro é application/problem+json:
{ "type": "public_api.insufficient_scope", "title": "InsufficientScopeError", "status": 403, "detail": "Este token não tem o escopo conversations:read, exigido por esta operação. Gere um token com esse escopo.", "requestId": "019fcae7-…", "meta": { "operation": "conversations.reading", "scope": "conversations:read" }}| Campo | Significado |
|---|---|
type |
Código estável, em inglês (<contexto>.<motivo>). É nele que o seu código deve decidir |
title |
Nome do erro, para log |
status |
O mesmo status HTTP |
detail |
Mensagem em português para uma pessoa ler; pode mudar de texto |
requestId |
Id da requisição. Mande ao suporte junto com o horário |
meta |
Dados do erro, quando existem (o escopo que faltou, o limite, o id não encontrado…) |
Os erros comuns a toda rota:
| Status | type |
Quando |
|---|---|---|
| 400 | validation_failed |
Parâmetro ou corpo inválido; os detalhes da validação vêm em meta |
| 401 | public_api.invalid_credential |
Token ausente, inválido, expirado ou revogado |
| 403 | public_api.insufficient_scope |
O token não tem o escopo da operação (meta.scope) |
| 403 | public_api.role_not_allowed |
O seu papel não permite a operação |
| 403 | public_api.product_not_contracted |
O plano da empresa não inclui a operação |
| 404 | <contexto>.not_found e afins |
O recurso não existe ou está fora da sua visibilidade (ex.: deals.not_found) |
| 429 | public_api.rate_limited |
Passou do limite da classe (acima) |
| 500 | internal_error |
Erro inesperado do nosso lado; tente de novo e, se persistir, mande o requestId |
Os erros específicos de cada rota (por exemplo, conversations.deal_unavailable ou exports.too_many_running) estão nas respostas de cada operação na Referência da API.
Um recurso fora da sua visibilidade responde 404, como se não existisse: o vendedor não descobre que o negócio de outra pessoa existe.
Paginação
Seção intitulada “Paginação”As listas usam um de dois formatos:
- Por página:
page(começa em 1) epageSize(até 100), comtotalna resposta. Os negócios (GET /v1/deals) usampageesize(até 200, padrão 50). - Por cursor:
GET /v1/conversationseGET /v1/activityrecebemlimit(até 200) ecursor, e devolvemnextCursor. Para a página seguinte, mande onextCursorcomocursor; quando ele vier nulo, acabou. O cursor de conversas é estável: conversa nova entrando no meio não faz a página seguinte pular nem repetir item.
Os limites e o padrão de cada lista estão nos parâmetros da operação na Referência da API.
Arquivos por link assinado
Seção intitulada “Arquivos por link assinado”Nenhum arquivo passa pela API: exportações, o arquivo de uma conversa, downloads e materiais da Base de conhecimento saem como link assinado do S3, com expiresAt na resposta. O link já baixa com o nome certo e não precisa do token.
| O quê | Validade do link |
|---|---|
Arquivos de uma exportação (GET /v1/exports/{id} quando ready) |
15 minutos |
Arquivo de uma conversa (POST /v1/conversations/{channel}/{id}/file, em PDF, TXT ou JSON) |
15 minutos |
Downloads (/v1/downloads/...: transcrição em PDF, áudio da reunião, ZIP) |
15 minutos |
Material da Base de conhecimento (POST /v1/knowledge/materials/{id}/download-url) |
5 minutos |
O link expirou? Peça de novo: consultar a exportação gera links novos. As exportações ficam guardadas por 7 dias e podem ser apagadas antes (DELETE /v1/exports/{id}); depois disso aparecem como expired, sem links.
Cada exportação traz um manifest.json com, para cada arquivo, o tamanho, o sha256 e o número de linhas, para você conferir se o download veio inteiro.