Skip to main content
A memória nasce de eventos. Tudo o que acontece com um cliente, em qualquer canal ou sistema, entra na Niadra como um evento imutável: o que ele disse, o que um sistema registrou, o que um agente fez. Nada é editado depois. Correção é evento novo, e apagamento é uma operação à parte, com comprovante.

Três tipos de evento

O campo kind 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 por POST /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 um idempotency_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 em accepted, o que já existia conta em duplicates, e cada item recusado aparece em errors com a posição e o código:
A validação acontece em dois tempos: a borda confere só a forma do envelope, e cada item é conferido depois. As regras de forma, que os SDKs também aplicam antes de enviar:
  • mensagem precisa de texto, transcrição ou referência de mídia;
  • evento de sistema precisa de canonical_type, como invoice.credited;
  • ação precisa do bloco action, e o bloco só vale quando kind é action;
  • todo evento precisa de ao menos um handle, sujeito ou objeto.
Um item acima de 1 MB volta com o código 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

O conversation_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 em POST /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.
Dado que chega depois, como a transcrição completa de uma chamada, é evento novo, não atualização. Ele marca a conversa e a memória derivada é refeita com versão nova.

Cobertura das fontes

O SDK envia um heartbeat 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 com context_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/csv semeia identidade. O cabeçalho nomeia tipos de handle (phone_e164, email, system_id…), com <type>_scope para o namespace, <type>_2 a <type>_9 para mais valores do mesmo tipo e os opcionais idempotency_key, occurred_at e subject_kind. Cada linha vira um identify com o método system_import, sob as mesmas proteções de qualquer outro.
  • application/x-ndjson traz histórico: cada linha é um item de POST /v1/batch, validado do mesmo jeito.
Acompanhe a importação por 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.