Sincronização de histórico

Evento: history · Transporte: POST para o seu receptor

Receba lotes de dados processados durante sincronizações e recuperações de histórico.

Variações do payload

EventType permanece history. O campo event identifica o conteúdo do lote: por exemplo messages, chats, calls, labels ou chat_labels. Leia o array correspondente; não suponha que todos os arrays existam no mesmo evento.

Sincronizações de etiquetas podem acrescentar history_type, full_sync, summary e chunk. Esses metadados não são universais para todo histórico.

Identificação da origem

request_id identifica de onde o lote veio. No histórico enviado após ler o QR code ou o código de pareamento, todos os lotes (inclusive o status de conclusão) trazem o mesmo request_id, gerado a cada pareamento e devolvido em POST /instance/connect antes da leitura do código e também informado em GET /instance/history-sync/status. Em POST /labels/refresh, os lotes labels e chat_labels trazem o request_id devolvido na resposta da requisição. Nas respostas de POST /message/history-sync, o request_id do pedido vem em cada mensagem e no topo dos lotes messages e chats quando todo o lote pertence ao mesmo pedido; lotes mistos podem omiti-lo no topo.

Resultado de POST /message/history-sync

Cada pedido termina com um único lote event: "status" com o request_id do pedido e os campos status, has_more, history_access, received_messages e next_request. Quando o celular indica mais mensagens, mas não entrega uma página que avance, status vem blocked, com success: false e o motivo em error, sem next_request: não ofereça "carregar mais" com o mesmo pedido. Reenvie next_request para pedir as mensagens anteriores; null indica que não há uma próxima requisição utilizável; isso só comprova o início da conversa quando has_more é false. has_more: false indica o início da conversa; null indica que não foi confirmado. Mensagens que chegarem depois de um timeout, até cerca de 5 minutos após o pedido, vêm com o mesmo request_id, sem novo status; depois disso chegam sem request_id.

Conversas individuais e LID

Em conversas individuais, chatid vem pelo número da conversa (@s.whatsapp.net), mesmo quando o celular devolve o histórico pelo LID; o LID fica em chatlid. Assim as mensagens recuperadas aparecem em POST /message/find pela mesma conversa das mensagens ao vivo.

Como processar

Faça upsert pelos IDs, aceite vários lotes e tolere sobreposição com eventos ao vivo. O recebimento de um lote não comprova que todo o histórico do WhatsApp foi sincronizado. Quando chunk.has_more existir, ele descreve aquela sequência de chunks.

Sequências e conclusão

Cada origem de lotes recebe um batchChunkOrder: o histórico inicial, cada parte que o WhatsApp envia depois dele e cada resposta de POST /message/history-sync. Chats, mensagens e ligações da mesma origem compartilham o número. Identifique uma sequência por batchChunkOrder + event e acompanhe seus lotes com batchNumber e batchTotal. Lotes de sequências diferentes podem chegar intercalados.

Lotes com dados chegam com batchHistoryStatus: "loading". O fim é informado por um lote sem dados, com event: "status" e batchHistoryStatus: "complete"; seu batchChunkOrder é o da última sequência enviada. No histórico do pareamento, ele sai quando o WhatsApp indica o fim da sincronização completa ou após cerca de 2 minutos sem novas partes do histórico. Quando o pareamento pede só o histórico normal (history_days ausente, fora de 0–365 ou de 1 a 90 em POST /instance/connect), o WhatsApp não envia a sincronização completa, e o complete sai por esse prazo. Se partes atrasadas chegarem depois, um novo complete é enviado: considere o de maior batchChunkOrder. A numeração recomeça em 1 a cada pareamento.

Um novo pareamento pode reenviar mensagens já recebidas. Deduplique por messageid e faça upsert pelos IDs: lotes também podem se sobrepor a eventos recebidos ao vivo.

Nos pedidos manuais, o status final tem uma sequência própria e batchHistoryStatus: "complete", inclusive quando status: "timeout". Esse marcador encerra o pedido; só has_more: false confirma o início da conversa. completed aguarda o processamento dos chunks em andamento do chat. Um timeout pode ser parcial, e mensagens atrasadas ainda podem chegar. O status manual é um lote único, com batchNumber: 1 e batchTotal: 1. Com fallbackMode: "try_alternates", o celular pode responder igual ao pedido pelo número e ao pedido pelo LID; a resposta repetida é descartada e não reenvia chats nem mensagens. Uma resposta com mensagens novas ainda pode chegar depois do status, com o mesmo request_id.

Habilitar este evento

Adicione history à lista events do webhook da instância. Preserve os outros eventos de que sua integração precisa.

{
  "enabled": true,
  "url": "https://seu-sistema.example/hooks/whatsapp",
  "events": [
    "history"
  ]
}

Envie essa configuração para POST /webhook, usando o header token. O JSON acima configura a assinatura; os exemplos de entrega abaixo são o que seu servidor recebe.

Campos do payload

O envelope comum vem na raiz. Campos condicionais podem estar ausentes ou nulos conforme o evento; não use a tabela como obrigação de presença de todos os campos.

CampoTipoSignificado
EventTypestringObrigatório. Tipo de evento, por exemplo connection ou messages.
ownerstringObrigatório. Consulte o objeto ou exemplo correspondente.
tokenstringObrigatório. Token da instância; dado sensível.
BaseUrlstringObrigatório. Consulte o objeto ou exemplo correspondente.
instanceNamestringConsulte o objeto ou exemplo correspondente.
eventstringConteúdo do lote: messages, chats, calls, labels, chat_labels ou status (conclusão do histórico).
request_idstringOrigem do lote: pareamento, recarga de etiquetas ou pedido manual. Em lotes messages e chats de pedidos manuais, só aparece no topo quando todo o lote pertence ao mesmo pedido; consulte também messages[].request_id.
request_chatstringEm status de POST /message/history-sync: chat do pedido.
statusstringResultado do pedido manual: completed quando o WhatsApp confirma sua conclusão; timeout após 2 minutos sem confirmação; blocked quando o celular indica mais mensagens, mas não entregou uma página que avance (o motivo vem em error). Não comprova que toda a conversa foi recuperada.
successbooleanEm status de POST /message/history-sync: só aparece, com false, junto de error. Sempre presente com status: "blocked"; num timeout, aparece quando o próximo pedido não pôde ser montado.
errorstringEm status de POST /message/history-sync: por que não há next_request. pagination_no_progress: a partir da mesma âncora, o celular não entregou uma página que avance nem no maior count (a cada página sem avanço, o next_request dobra o count, até 100): veio vazia, sem mensagem que sirva de âncora ou repetida. É o fim do que o celular entrega a partir dessa âncora, mas o WhatsApp não confirma que é o início da conversa; repetir o pedido dá o mesmo resultado; anchor_unavailable: a mensagem de referência não está mais disponível, peça com outra; history_access_unavailable: o restante não está acessível; cursor_lookup_failed: falha ao montar o próximo pedido, tente de novo.
has_moreboolean / nullEm status de POST /message/history-sync: o que o celular declarou. true há mais mensagens no celular, false chegou ao início da conversa, null não foi confirmado. Alguns aparelhos declaram true em toda resposta sob demanda, inclusive na vazia: com eles, a paginação termina em blocked com pagination_no_progress, nunca em has_more: false.
history_accessstringEm status de POST /message/history-sync: unavailable quando há mais mensagens, mas sem acesso a elas.
received_messagesintegerEm status de POST /message/history-sync: mensagens recebidas para o pedido.
next_requestobject / nullCorpo pronto para pedir mensagens anteriores. null indica que não há próxima requisição utilizável; só has_more: false comprova o início da conversa.
next_request.numberstringObrigatório. Consulte o objeto ou exemplo correspondente.
next_request.countintegerObrigatório. Consulte o objeto ou exemplo correspondente.
next_request.messageidstringObrigatório. Consulte o objeto ou exemplo correspondente.
next_request.timestampintegerObrigatório. Timestamp da âncora em milissegundos Unix.
next_request.fromMebooleanObrigatório. Consulte o objeto ou exemplo correspondente.
messagesarray / nullLote de mensagens.
messages[].idstringIdentificador retornado pelo sistema; preserve como string.
messages[].messageidstringID da mensagem no WhatsApp.
messages[].chatidstringJID do contato, grupo ou canal.
messages[].senderstringJID do remetente.
messages[].senderNamestringNome disponível do remetente.
messages[].fromMebooleanMensagem enviada pela conta conectada.
messages[].wasSentByApibooleanOrigem na API quando esse campo estiver presente na mensagem.
messages[].isGroupbooleanIdentifica conversa de grupo.
messages[].messageTypestringTipo de mensagem normalizado.
messages[].textstringTexto ou legenda disponível.
messages[].messageTimestampintegerTimestamp da mensagem em milissegundos Unix.
messages[].contentvariávelConteúdo específico do tipo de mensagem; não possui um formato único.
messages[].statusstringEstado disponível da mensagem; pode estar vazio.
messages[].request_idstringPedido manual que recuperou esta mensagem, quando correlacionado.
chatsarray / nullLote de chats.
chats[].idstringID local do chat.
chats[].wa_chatidstringJID da conversa.
chats[].namestringNome disponível.
chats[].wa_isGroupbooleanConversa de grupo.
chats[].wa_isBlockedbooleanEstado de bloqueio no snapshot.
chats[].wa_archivedbooleanEstado de arquivamento.
chats[].wa_unreadCountintegerQuantidade de mensagens não lidas disponível.
chats[].wa_labelvariávelEtiquetas associadas, conforme a projeção do chat. Pode ser texto JSON; normalize antes de usar.
labelsarray / nullLote de etiquetas.
chat_labelsarray / nullAssociações atualizadas pela sincronização de etiquetas.
history_typestringOrigem adicional, como labels_refresh.
full_syncbooleanSe a sincronização de etiquetas foi completa.
chunkobjectMetadados opcionais de fragmentação.
chunk.indexintegerÍndice do chunk.
chunk.totalintegerTotal daquela sequência.
chunk.sizeintegerItens neste chunk.
chunk.has_morebooleanExistem mais chunks naquela sequência.
batchNumberintegerNúmero deste lote, começando em 1.
batchTotalintegerTotal de lotes desta sequência.
batchChunkOrderintegerSequência da origem deste lote, atribuída pela API. Não é a ordem do chunk no WhatsApp e recomeça em 1 a cada pareamento.
batchHistoryStatusstringloading em lotes com dados; complete no lote status que encerra o histórico.
callsarray / nullRegistros de chamadas do histórico quando event=calls.

Exemplos de entrega

Exemplos ilustrativos com identificadores fictícios. O conteúdo específico e os campos opcionais variam.

Lote de mensagens

{
  "EventType": "history",
  "owner": "5511999999999",
  "token": "INSTANCE_TOKEN",
  "BaseUrl": "https://seu-servidor.example",
  "instanceName": "Atendimento",
  "event": "messages",
  "request_id": "3f1c9b52-8d4e-4a7b-9c61-2f0e5d7a1b84",
  "messages": [
    {
      "id": "5511999999999:MSG_HISTORICO",
      "messageid": "MSG_HISTORICO",
      "chatid": "5511888888888@s.whatsapp.net",
      "text": "Mensagem do histórico",
      "messageTimestamp": 1788868800000
    }
  ],
  "batchNumber": 1,
  "batchTotal": 1,
  "batchChunkOrder": 1,
  "batchHistoryStatus": "loading"
}

Lote de chats

{
  "EventType": "history",
  "owner": "5511999999999",
  "token": "INSTANCE_TOKEN",
  "BaseUrl": "https://seu-servidor.example",
  "instanceName": "Atendimento",
  "event": "chats",
  "request_id": "3f1c9b52-8d4e-4a7b-9c61-2f0e5d7a1b84",
  "chats": [
    {
      "id": "r0123456789abcd",
      "wa_chatid": "5511888888888@s.whatsapp.net",
      "name": "Contato de exemplo"
    }
  ],
  "batchNumber": 1,
  "batchTotal": 1,
  "batchChunkOrder": 1,
  "batchHistoryStatus": "loading"
}

Conclusão do histórico

{
  "EventType": "history",
  "owner": "5511999999999",
  "token": "INSTANCE_TOKEN",
  "BaseUrl": "https://seu-servidor.example",
  "instanceName": "Atendimento",
  "event": "status",
  "request_id": "3f1c9b52-8d4e-4a7b-9c61-2f0e5d7a1b84",
  "batchNumber": 1,
  "batchTotal": 1,
  "batchChunkOrder": 3,
  "batchHistoryStatus": "complete"
}

Resultado de POST /message/history-sync

{
  "EventType": "history",
  "owner": "5511999999999",
  "token": "INSTANCE_TOKEN",
  "BaseUrl": "https://seu-servidor.example",
  "instanceName": "Atendimento",
  "event": "status",
  "request_id": "5f0c2a8e-6d1b-4c3a-9e7f-1a2b3c4d5e6f",
  "request_chat": "5511888888888@s.whatsapp.net",
  "status": "completed",
  "has_more": true,
  "history_access": "available",
  "received_messages": 50,
  "next_request": {
    "number": "5511888888888@s.whatsapp.net",
    "count": 50,
    "messageid": "3EB0MAIS_ANTIGA_RECEBIDA",
    "timestamp": 1706350400000,
    "fromMe": false
  },
  "batchChunkOrder": 4,
  "batchHistoryStatus": "complete",
  "batchNumber": 1,
  "batchTotal": 1
}

Prazo do resultado vencido; histórico ainda inconclusivo

{
  "EventType": "history",
  "owner": "5511999999999",
  "token": "INSTANCE_TOKEN",
  "BaseUrl": "https://seu-servidor.example",
  "instanceName": "Atendimento",
  "event": "status",
  "request_id": "5f0c2a8e-6d1b-4c3a-9e7f-1a2b3c4d5e6f",
  "request_chat": "5511888888888@s.whatsapp.net",
  "status": "timeout",
  "has_more": null,
  "history_access": "unknown",
  "received_messages": 0,
  "next_request": {
    "number": "5511888888888@s.whatsapp.net",
    "count": 50,
    "messageid": "3EB0MAIS_ANTIGA_RECEBIDA",
    "timestamp": 1706350400000,
    "fromMe": false
  },
  "batchChunkOrder": 4,
  "batchHistoryStatus": "complete",
  "batchNumber": 1,
  "batchTotal": 1
}

Responder ao webhook

Seu receptor deve aceitar o POST JSON e devolver uma resposta 2xx rapidamente, após validar e registrar o recebimento. O corpo da resposta não é um comando para a API. Processe trabalho demorado separadamente.

O worker não repete automaticamente uma entrega HTTP malsucedida. Consulte diagnóstico de webhooks e implemente a reconciliação necessária. Eventos repetidos ou fora de ordem ainda precisam ser tolerados pelo receptor.

Referências