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
@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
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).
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,
niadrapode ser umNiadraou umAsyncNiadra: com o cliente síncrono, chame os gêmeos*_sync.override_agent_id=(Python) ouinboundFields(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
storeentre os webhooks (em memória por padrão; passe o seu para mais de uma instância), eotherTool(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.

