Três tipos de evento
O campokind diz o que o evento registra:
Cada evento traz o
channel (whatsapp, voice, app, email, erp, crm, ticket), quem falou em speaker (customer, ai_agent, human_agent ou system), o momento em occurred_at e pelo menos um handle, sujeito ou objeto que diz de quem ou do que ele fala. Texto livre de dentro de um sistema, como a descrição de um ticket ou o corpo de um e-mail, entra como message num canal próprio.
A ordem da memória é a de occurred_at, nunca a de chegada. Uma ressincronização do WhatsApp que reenvia mensagens antigas cai no lugar certo. Carimbo de relógio fora da tolerância é corrigido pela hora de chegada e marcado.
O lote
Todo evento entra porPOST /v1/batch. Um lote aceita até 500 itens, com até 1 MB por item e 2,5 MB por lote, e mistura itens de vários tipos pelo campo type:
O evento bruto é gravado antes da resposta. Você raramente monta o lote na mão: os SDKs guardam os eventos numa fila local e enviam em lotes, sem bloquear o agente.
Idempotência
Todo item carrega umidempotency_key. Use o id da mensagem no provedor sempre que houver um: provedores de canal reenviam webhooks por dias, e o mesmo id nunca vira dois eventos. Quando não há id, o SDK gera um UUIDv7, que também ordena por tempo.
A deduplicação é pelo id, nunca por semelhança de conteúdo. Duas mensagens curtas iguais (“ok”, “sim”) são duas mensagens. Um item repetido volta contado em duplicates, sem erro.
Um item ruim não derruba o lote
O lote responde 200 quando todos os itens entraram e 207 quando algum foi recusado, sempre com o resultado por item. O que passou conta emaccepted, o que já existia conta em duplicates, e cada item recusado aparece em errors com a posição e o código:
- mensagem precisa de texto, transcrição ou referência de mídia;
- evento de sistema precisa de
canonical_type, comoinvoice.credited; - ação precisa do bloco
action, e o bloco só vale quandokindéaction; - todo evento precisa de ao menos um handle, sujeito ou objeto.
too_large. Um corpo acima de 2,5 MB, ou um lote com mais de 500 itens, é recusado inteiro com 422 invalid_input.
O SDK tenta de novo com recuo em falha de rede, 429 e 503, e nunca repete um 4xx de validação.
Conversas, tarefas e sessões
Oconversation_id é seu: pode ser o thread do WhatsApp, que dura meses, ou o id da chamada. A Niadra divide cada conversa em sessões, janelas de atividade que fecham por inatividade do canal (cerca de 20 minutos no WhatsApp, 30 no app) ou por conversation.ended. Em voz, envie conversation.ended ao desligar. Uma chamada costuma ter dois ids, o da plataforma e o do tronco: mande o segundo em conversation_aliases.
Agente interno usa task_id no lugar da conversa. A tarefa fecha com task.ended ou depois de 10 minutos sem atividade. Os SDKs cuidam disso com conversation() e task(), que emitem o fim quando o bloco termina.
Mensagem nova fica legível na camada quente em menos de 1 segundo, já no live do próximo contexto de outro canal. Ação e evento de sistema chegam ao contexto recompilado em menos de 10 segundos.
Mídia e dado tardio
Mídia nunca viaja dentro do evento. Reserve um upload emPOST /v1/media/uploads, com content_type, size_bytes (até 500 MB), o sha256 dos bytes e, sempre que souber, o subject dono do arquivo: assim o arquivo fica guardado sob aquela pessoa, e apagá-la apaga o arquivo mesmo que nenhum evento aponte para ele. A resposta traz upload_url, upload_headers e expires_at. Envie os bytes com PUT para upload_url com exatamente os cabeçalhos de upload_headers: o armazenamento recusa qualquer outro byte. Depois mande o evento com content.media_ref e content.media_sha256. Os SDKs fazem os três passos numa chamada.
Cobertura das fontes
O SDK envia umheartbeat periódico com quantos eventos mandou. A Niadra compara com o que recebeu e marca cada fonte como ok ou silent. O contexto informa essa cobertura em coverage, e uma fonte muda pode disparar um gatilho. GET /v1/sources/coverage mostra cada fonte dia a dia, por até 90 dias: o que o SDK diz que mandou (sent), o que chegou (received), o que foi recusado (rejected) e o que chegou com um tipo que o seu mapeamento não cobre (unmapped).
Qual contexto o agente usou
Quando um agente responde, o SDK carimba o turno dele comcontext_stamp: o etag do contexto que entrou no prompt e injected_at, o momento em que entrou. É assim que o aproveitamento do contexto separa o contexto que chegou tarde do que chegou e não foi usado. Dentro de um conversation(), os SDKs carimbam sozinhos quando você chama mark_injected() em Python ou markInjected() em TypeScript; só mande o carimbo à mão quando montar os eventos você mesmo.
Arquivos: semear identidade ou trazer histórico
Para volume que não cabe no caminho da conversa,POST /v1/ingest/files recebe um arquivo inteiro de até 512 MB, com o escopo track, e responde 202 com um import_id:
text/csvsemeia identidade. O cabeçalho nomeia tipos de handle (phone_e164,email,system_id…), com<type>_scopepara o namespace,<type>_2a<type>_9para mais valores do mesmo tipo e os opcionaisidempotency_key,occurred_atesubject_kind. Cada linha vira umidentifycom o métodosystem_import, sob as mesmas proteções de qualquer outro.application/x-ndjsontraz histórico: cada linha é um item dePOST /v1/batch, validado do mesmo jeito.
GET /v1/ingest/files/{import_id}: status (queued, running, completed, failed), records, accepted, duplicates, rejected e os 100 primeiros erros de linha, que trazem o número da linha e nunca o valor.
Correções
Uma correção também é evento.POST /v1/feedback recebe uma action, e cada ação pede os próprios campos: retract_fact pede fact_id; correct_fact pede fact_id e value; resolve_open_item pede open_item_id; conversation_outcome pede conversation_id e value. Um pedido sem eles é recusado com 422, em vez de ser gravado e ignorado. A resposta tem o formato da resposta do lote. Os dois SDKs têm feedback().
Próximos passos
Identidade e verificação
como os handles viram um cliente só.
Sistemas, objetos e ações
eventos de ERP e ações que fecham pendências.
Enviar um lote
a referência completa de
POST /v1/batch.
