Símbolo da AmivuSímbolo da AmivuDevelopers

Avisar sobre um pedido

Do evento no ERP à mensagem no WhatsApp do cliente.

Um evento no seu ERP — pedido despachado, boleto vencendo, entrega concluída — vira uma mensagem no WhatsApp do cliente. São três chamadas, e a terceira é a que envia.

O fluxo

Pedido despachado no ERP
        ↓
1. encontre (ou crie) o contato        POST /v1/contacts    external_id
        ↓
2. encontre (ou abra) a conversa       POST /v1/conversations  external_id
        ↓
3. envie                               POST /v1/messages    Idempotency-Key
        ↓
4. acompanhe                           webhook message.delivered | message.failed

Os dois external_id fazem o trabalho pesado: eles tornam os passos 1 e 2 seguros de repetir, e o Idempotency-Key cuida do passo 3.

O código

JavaScript
async function avisarSobreOPedido(pedido) {
  // 1. O contato. Repetir devolve o mesmo — sem tabela de-para.
  const contato = await amivu("/v1/contacts", {
    method: "POST",
    body: {
      external_id: `cliente-${pedido.clienteId}`,
      name: pedido.clienteNome,
      phone: pedido.clienteTelefone,
    },
  });

  // 2. A conversa DO PEDIDO. Um external_id por pedido mantém o histórico
  //    de cada um separado — e reprocessar não abre uma segunda.
  const conversa = await amivu("/v1/conversations", {
    method: "POST",
    body: {
      contact_id: contato.id,
      channel_id: process.env.AMIVU_CHANNEL_ID,
      external_id: `pedido-${pedido.numero}`,
      metadata: { numero: pedido.numero, valor: pedido.total },
    },
  });

  // 3. A mensagem. A chave descreve a OPERAÇÃO, não a tentativa.
  return amivu("/v1/messages", {
    method: "POST",
    headers: { "Idempotency-Key": `pedido-${pedido.numero}-despachado` },
    body: {
      conversation_id: conversa.id,
      type: "text",
      text: `Olá, ${pedido.clienteNome}! Seu pedido #${pedido.numero} saiu para entrega.`,
    },
  });
}
Uma conversa por pedido, ou uma por cliente?
Por pedido, quando cada um tem um ciclo próprio e o atendente precisa ver o contexto daquele. Por cliente, quando o assunto é contínuo. Os dois funcionam — o que não funciona é misturar os dois critérios na mesma base.

Acompanhar o resultado

A resposta do envio é queued: aceita por nós, ainda não pelo canal. O que aconteceu de verdade chega por webhook.

JavaScript
app.post("/webhooks/amivu", async (req, res) => {
  if (!assinaturaValida(req)) return res.status(401).end();
  res.status(200).end();

  const { type, data } = req.body;
  const pedido = data.conversation?.external_id;
  if (!pedido) return;

  if (type === "message.delivered") {
    await marcarAvisoEntregue(pedido);
  }

  if (type === "message.failed") {
    // O código é do provedor e quase sempre pede AÇÃO, não retentativa.
    await abrirTarefaDeContato(pedido, data.message.error);
  }

  if (type === "message.received") {
    // O cliente respondeu: leve para o time, não para o robô.
    await notificarAtendimento(pedido, data.message.text);
  }
});

A janela de 24 horas

No WhatsApp oficial, mensagem livre só sai dentro de 24 horas desde a última mensagem do cliente. Fora disso, o canal exige um modelo aprovado.

Descubra se o canal tem essa restrição lendo capabilities.session_window em GET /v1/channels/{id}. Onde ela existe, o envio fora da janela é recusado pelo provedor e você recebe message.failed com o motivo.

Veja Canais e capacidades.

Escrevemos sobre operação de atendimento, canais e IA no blog da Amivu.