ShvIA

Docs · API nativa

Chat stream

Os endpoints de chat nativos são os que os apps do ShvIA usam. Em comparação com os endpoints /v1, eles guardam as suas conversas no servidor e aceitam um esforço de raciocínio. POST /api/v1/chat também aceita anexos de arquivo e pode usar os seus combos. Eles têm ainda convenções próprias, descritas aqui.

EndpointEstiloGuarda a conversa
POST /api/v1/chatserver-sent eventssim
POST /api/v1/agent/chatuma resposta JSONsim

POST/api/v1/chat#

Devolve a resposta em streaming, como server-sent events.

  • Auth: a sua chave de API.
  • Limite: 60 requisições por minuto por conta por padrão. Veja limites e cotas.
  • Corpo: JSON, ou multipart/form-data quando você envia arquivos.
bash
curl -sN "$SHVIA_URL/api/v1/chat" \
  -H "Authorization: Bearer $SHVIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Say hello in one short sentence.",
    "profile_name": "'"$SHVIA_MODEL"'",
    "session_id": "support-ticket-1042"
  }'

Requisição#

CampoTipoNotas
messagestringo turno do usuário. prompt é aceito como apelido. No máximo 32000 caracteres por padrão. Uma mensagem vazia é respondida com um evento de erro
profile_namestringo id do modelo, model@server, de modelos. profile é aceito como apelido
model, serverstringum nome de modelo simples e a chave de um servidor, como alternativa a profile_name. model não entende o @: ponha o id composto em profile_name
session_idstringa chave que continua uma conversa, veja abaixo. No máximo 191 caracteres
effortstringlow, medium, high, xhigh ou max. Qualquer outro valor é ignorado. Um valor válido é salvo na conversa
combo_idintegeruse um dos seus combos em vez de escolher um modelo. Um id desconhecido é ignorado
files[]fileanexos, só com multipart/form-data

Quando nenhum modelo é informado, o gateway usa o seu padrão salvo e, depois, o padrão da instalação. Não existe temperature, nem max_tokens, nem o campo system, nem como enviar uma lista de mensagens: a conversa é a que está guardada no servidor, e o servidor acrescenta as próprias instruções a ela.

Com um único modelo, este endpoint não recorre ao seu modelo padrão como /v1 e agent/chat fazem: um modelo sem chave por trás, ou que falha, termina num evento error. Dê a ele outros modelos para tentar com um combo.

Anexos#

  • Arquivos de texto e de código: até 10 por mensagem, 262144 bytes cada e 1048576 bytes no total.
  • Imagens: PNG, JPEG, WebP ou GIF, até 8 por mensagem e 20 MiB cada. Envie só para um modelo que enxerga imagens, ou a requisição é recusada.
  • Pacotes (zip, tar, tgz) são abertos e os arquivos de texto dentro deles são usados.
  • O grupo da sua conta define mais um limite para o número de anexos por mensagem.

Conversas e session_id#

Com uma chave de API, toda chamada a este endpoint pertence a uma conversa num projeto chamado API. Se a chamada continua uma conversa anterior ou não, isso depende de session_id:

  • Com session_id: a conversa mais recente sua com esse mesmo valor continua, e os turnos anteriores são enviados ao modelo, quantos couberem na janela de contexto dele: os mais antigos podem ser resumidos ou deixados de fora. Um valor novo começa uma conversa nova.
  • Sem ele: toda chamada é uma conversa nova com um turno.

O stream nunca diz o id da conversa. Para lê-lo depois, liste as suas conversas e compare o campo session_id. Veja conversas. Você não pode enviar um conversation_id a este endpoint; para continuar uma conversa pelo id, use POST /api/v1/agent/chat.

O stream#

A resposta é text/event-stream. Cada evento é uma linha, data: {"kind": "…", "data": {…}}, seguida de uma linha em branco. Não há linhas event: nem id:, e nenhum comentário de keep-alive. Como a requisição é um POST com headers, o EventSource de um navegador não pode ser usado: leia o stream do corpo. Ignore qualquer kind que você não conheça, já que novos são acrescentados com o tempo. Tire a resposta final de done. Se a instalação deixa o chat usar ferramentas, o stream também pode trazer tool_call, tool_result e eventos parecidos, e um evento text_clear, que quer dizer que o texto mostrado até agora deve ser descartado.

sse
data: {"kind":"user_message","data":{"id":5121}}

data: {"kind":"model","data":{"name":"claude-sonnet-4-6@anthropic","model":"claude-sonnet-4-6","profile":"claude-sonnet-4-6@anthropic","server":"anthropic","server_label":"…","target_source":"request","request_id":"3b1a8f7e-…"}}

data: {"kind":"text_delta","data":{"delta":"Hello"}}

data: {"kind":"text_delta","data":{"delta":"! Nice to meet you."}}

data: {"kind":"usage","data":{"model":"claude-sonnet-4-6","profile":"claude-sonnet-4-6@anthropic","server":"anthropic","server_label":"…","prompt_tokens":42,"completion_tokens":8,"total_tokens":50,"thinking_time_ms":null,"response_time_ms":890.4,"reasoning_tokens":null,"effort":"medium","cost":null,"cost_estimated":0.000246}}

data: {"kind":"done","data":{"text":"Hello! Nice to meet you."}}
kinddataQuando
user_message{"id"}o primeiro, quando a sua mensagem foi salva
modelo modelo resolvido: name, model, server, target_source, request_id, e mais alguns campos que servem aos appsantes do primeiro token, e de novo se outro modelo assume depois de uma falha
reasoning_delta{"delta"}o modelo envia o raciocínio em streaming
text_delta{"delta"}cada fragmento da resposta
warning{"codigo": "resposta_nao_salva", "correlation_id"}a resposta chegou até você, mas não pôde ser salva. Não é um erro: guarde o texto
warn{"kind": "guard.repetition", "message"}uma guarda notou algo na resposta, como um laço repetitivo. A resposta continua sendo entregue e salva
citations{"message_id", "citations"}quando a resposta usou documentos de uma base de conhecimento
error{"message", "codigo", "status"}algo falhou. codigo e status só vêm quando são conhecidos
usagetokens, tempos, o effort realmente aplicado e costdepois da resposta, antes de done
done{"text"}, que vem vazio depois de um erro que termina sem texto, e [] depois de uma recusa antecipadao último. Sempre chega

cost é o custo que o provedor informou, em USD, ou null. cost_estimated é o número de tokens vezes a tabela de preços, ou null. usage.effort é o nível de esforço que o modelo realmente aplicou, ou null. Os tempos são números que podem ter casas decimais, então leia-os como float. Atenção: um data vazio é escrito como [], não como {}.

Códigos de erro#

Quando um evento error tem codigo, decida por ele. message é para pessoas e pode estar em outro idioma.

codigoSignificado
mensagem_vaziaa mensagem estava vazia
mensagem_muito_longaa mensagem passa do limite de tamanho
roteamento_sem_alvoo modelo ou o servidor não pôde ser resolvido. A mensagem pode listar alternativas
bot_desativadoa conversa que a conta tem aberta no app web pertence a um bot que está desligado
modelo_sem_visaovocê enviou imagens a um modelo que não as enxerga
anexo_invalidoum anexo não pode ser lido, é grande demais ou não é de um tipo aceito
pacote_sem_textoum pacote não tinha nenhum arquivo de texto aproveitável
sessao_id_muito_longasession_id passa de 191 caracteres
combo_sem_modeloo combo não tem nenhum modelo alcançável
limite_perfilos limites do seu grupo recusaram a chamada: o modelo não é permitido, ou um teto diário, semanal ou mensal de tokens foi atingido
resposta_vaziao modelo respondeu com nada
limite_de_requisicoeso provedor está limitando as requisições. O limite de requisições do próprio gateway é um HTTP 429, não um evento
provedor_recusou_pedidoo provedor rejeitou a requisição, com status. Outro modelo não ajuda
provedor_indisponivelo provedor falhou, ou não tem uma chave sua, com status
shvia_fora_do_aros servidores do operador não puderam ser alcançados

Um stream cortado por timeout envia um error sem codigo, depois usage, depois done com o texto parcial, que é salvo. Uma resposta com um laço repetitivo é entregue e salva com um evento warn a mais.

Mais uma coisa a saber: se a conta tem aberta no app web uma conversa com um bot, o modelo ao qual o bot está fixado vale mais que o que você pediu. Leia o evento model para ver o que rodou.

POST/api/v1/agent/chat#

Uma chamada que bloqueia, uma resposta JSON.

  • Auth: a sua chave de API. Uma chave cujo grupo de conta não tem acesso à API é recusada com 401.
  • Limite: 20 requisições por minuto por conta por padrão, no contador que os outros endpoints limitados dividem. Veja limites e cotas.
bash
curl -s "$SHVIA_URL/api/v1/agent/chat" \
  -H "Authorization: Bearer $SHVIA_API_KEY" -H "Content-Type: application/json" \
  -d '{ "message": "Say hello in one short sentence.", "profile_name": "'"$SHVIA_MODEL"'" }'
json
{
  "request_id": "9eaa5ed8-e7fa-423b-9a93-d9ff3d4c8a03",
  "response": "Hello! Nice to meet you.",
  "model": "claude-sonnet-4-6",
  "server": "anthropic",
  "server_label": "…",
  "profile_name": "claude-sonnet-4-6@anthropic",
  "target_source": "request",
  "effort": null,
  "tokens": { "prompt": 42, "completion": 8, "total": 50 },
  "timing": { "queue_wait_ms": 0, "response_time_ms": 890 },
  "conversation_id": 5
}

Requisição#

CampoNotas
messageobrigatório, no máximo 32000 caracteres
profile_nameo id do modelo, model@server. model e server também funcionam como campos separados
conversation_idum inteiro. Continua uma das suas conversas. Sem ele, toda chamada cria uma nova
effortlow, medium, high, xhigh ou max. Diferente do endpoint de stream, um valor inválido é um 422

Como os endpoints /v1, este monta uma cadeia curta: o modelo que você pediu e depois o modelo padrão da sua conta. Um modelo de nuvem sem chave por trás, ou que falha com algo que outro modelo pode resolver antes de enviar qualquer texto, é substituído pelo modelo padrão, e a resposta nomeia o modelo que respondeu: leia model, server e profile_name. Veja modelos.

Para manter uma conversa de vários turnos, devolva o conversation_id da resposta anterior. Aqui não há session_id, nem anexos, nem streaming. timing.queue_wait_ms é sempre 0.

Erros#

Este endpoint responde com status HTTP e um texto detail, e sem codigo:

StatusCorpoQuando
401{"message": "Unauthenticated."}a credencial não é válida
400{"detail": "…"}o modelo ou o servidor é desconhecido, ou o provedor rejeitou a requisição como inválida. Nesse segundo caso o status é o do próprio provedor, em geral 400, e o corpo também traz request_id
403{"detail": "…"}os limites do seu grupo recusaram a chamada: um modelo não permitido, uma cota esgotada, ou servidores remotos que o grupo não pode usar
404{"detail": "…"}o conversation_id não é seu
422{"message", "errors": {…}}o corpo falhou na validação
502{"detail": "…", "request_id": "…"}o modelo respondeu com nada, ou falhou de um jeito que não vale tentar de novo
503{"detail": "…", "retry_after": 30, "request_id": "…"} e um header Retry-Aftertodos os modelos da cadeia falharam de um jeito que vale tentar de novo: um limite de requisições, uma cota esgotada no provedor, um provedor sobrecarregado, uma chave de provedor recusada ou ausente, um erro de servidor, ou um servidor que não pode ser alcançado. Espere retry_after segundos. Não quer dizer que o gateway está fora do ar, e uma chave de provedor recusada não é a sua chave do gateway
504{"detail": "…", "request_id": "…", "partial": {…}} e X-Inference-Incomplete: read_timeouto modelo parou no meio da resposta. A resposta parcial é salva

Um 403 aqui não é limite de requisições: não tente de novo sem mudar alguma coisa.