Skip to main content
A Retell chega ao seu servidor por três caminhos, e o adaptador responde cada um: o webhook de ligação recebida (call_inbound, configurado no número), as custom functions do Retell LLM e o webhook do agente (call_started, transfer_started, call_ended, call_analyzed). Toda requisição traz x-retell-signature (v=<unix ms>,d=<HMAC-SHA256 em hex do corpo + ms>, com a chave de API da Retell que assina webhooks); sem uma assinatura válida, dentro de cinco minutos, a resposta é 401. Nenhum pacote da Retell é necessário.

Instalar

A integração em TypeScript chega com o @niadra/sdk 0.3.0, pronto no ramo main e no npm quando for publicado; até lá, o pacote do npm é o 0.1.1.

As cinco primitivas

O id da conversa na Niadra é o call_id da Retell (em Python, metadata.niadra_conversation_id quando a ligação o traz, o que outbound() define). O sujeito é o número do cliente: quem liga numa ligação recebida, o número chamado numa de saída; passe subject= (uma função da ligação) para clientes identificados de outro jeito.

Exemplo mínimo

O código em Python está em examples/retell_server.py no repositório do SDK.

LLM próprio (websocket)

Com um LLM próprio (o LLM WebSocket da Retell), é o seu servidor que chama o modelo. Em TypeScript, handlers.llm(callId, { send, instructions }) é uma sessão por websocket (/llm-websocket/:call_id): open() pede os detalhes da ligação (e fala a sua saudação), receive(event) responde os pings, registra as falas quando uma resposta é exigida e devolve um turno cujas messages trazem as suas instruções, as notas do agente, o contexto e a ligação até ali, com o turn_block no fim da última fala do cliente; turn.respond(text) envia a resposta (em pedaços com { complete: false }, com endCall ou transferNumber, que registra o transbordo). As falas levam a mesma chave de idempotência no websocket e no call_ended, então registrar pelos dois caminhos guarda cada uma uma vez. A Retell não assina o websocket, então a sessão nunca tira o cliente dos call_details do próprio socket: ela lê e registra só para uma ligação que um webhook assinado registrou (inbound ou call_started, no store); até lá, o modelo recebe as mensagens sem contexto e nada é gravado. trustCallDetails: true liga o comportamento antigo, só para um socket que aceita a Retell e mais ninguém (lista de IPs, segredo na URL).
Em Python não há sessão de websocket: abra a conversa com o call_id e use o wrap() do SDK de modelo ou o adaptador do framework que você chama; os webhooks acima continuam cuidando do contexto, das ferramentas e dos eventos.

Memória do agente

RetellWebhooks(..., agent_memory=True) e tool_configs(url, agent_memory=True) (Python), ou retell({ ..., agentMemory: true }) (TypeScript), trazem as notas do próprio agente na variável niadra_agent_memory (ponha {{niadra_agent_memory}} logo antes de {{niadra_context}}), antes do contexto nas mensagens do LLM próprio, e acrescentam search_agent_memory às custom functions. Veja Memória do agente.

Limites

  • Em Python, niadra pode ser um Niadra ou um AsyncNiadra: com o cliente síncrono, chame os gêmeos *_sync. override_agent_id= (Python) ou inboundFields(call) (TypeScript) acrescentam campos à resposta do webhook de entrada, como o agente que atende.
  • Em TypeScript, o cliente e o nível de verificação de cada ligação ficam num store entre os webhooks (em memória por padrão; passe o seu para mais de uma instância), e otherTool(name, args, call) atende as custom functions que não são da Niadra, para um URL só servir todas.
  • A Niadra lenta ou fora nunca derruba uma ligação: o webhook de entrada responde variáveis vazias, uma ferramenta responde que o histórico está indisponível, e o webhook continua respondendo 200.
  • Testado com cargas no formato público da Retell e assinaturas calculadas no próprio teste, contra o emulador; em TypeScript os handlers rodam também no Deno, no Bun e no workerd.

Próximos passos

Vapi

o mesmo desenho, com um URL de servidor só.

ElevenLabs

os webhooks de início, ferramenta e pós-chamada.