solar.ia

Receber eventos

Webhooks

Receba um POST no seu sistema sempre que algo acontecer no atendimento — sem ficar consultando a API.

Cadastrar#

Pelo painel, em Ajustes › Integrações › Webhooks (com histórico de entregas e botão de teste), ou pela API com Assinar eventos. Nos dois casos você escolhe a URL e os eventos, e recebe um segredo whsec_... para validar as entregas. Cada workspace tem até 50 webhooks.

Eventos#

EventoQuando dispara
conversation.createdUma conversa foi aberta (mensagem nova de um contato ou envio ativo).
conversation.updatedA conversa mudou: atribuição, etiquetas, situação aberta/pendente.
conversation.resolvedA conversa foi resolvida.
message.createdMensagem nova, recebida ou enviada. Notas internas não geram evento.
contact.createdUm contato foi criado.
contact.updatedOs dados de um contato mudaram.
contact.opted_outO contato foi descadastrado (pediu na conversa, pela equipe ou pela API). Dados: contact { id, name }, source (cliente | manual | api) e at.
contact.opted_inO descadastro foi desfeito: o contato volta a receber mensagens automáticas. Mesmos dados.

O botão Testar do painel envia o evento webhook.test, com data.message e data.sent_at, para você validar a integração sem esperar um evento real.

Formato da entrega#

Cada entrega é um POST com JSON. O envelope é sempre o mesmo; em data vem o objeto do evento (mensagem, conversa ou contato) no formato do motor de atendimento. Exemplo resumido de message.created:

POST no seu endereço
{
  "id": "0b6f1c9e-4d2a-4e8b-9a31-5f7c2d1e0a44",
  "event": "message.created",
  "workspace_id": "3f6c2a1e-8b4d-4c1a-9e2f-7a5b0c9d1e22",
  "webhook_id": "9a8b7c6d-1111-4e2f-8a3b-2c1d0e9f8a77",
  "timestamp": "2026-09-25T14:03:11Z",
  "data": {
    "id": 9812,
    "content": "Oi, quero saber do meu pedido",
    "message_type": "incoming",
    "private": false,
    "created_at": "2026-09-25T14:03:10.512Z",
    "conversation": { "id": 120, "status": "open", "inbox_id": 4 },
    "sender": { "id": 35, "name": "Maria Souza", "phone_number": "+5511999990000", "type": "contact" }
  }
}

Só mensagens do cliente

message.created dispara também para mensagens enviadas pela equipe, chatbots e pela própria API. Para reagir só ao que o cliente mandou, filtre data.message_type igual a "incoming" (em alguns eventos vem o número 0) — e evite loops ao responder mensagens da própria integração.

Headers

HeaderConteúdo
X-Solaria-EventNome do evento (igual a event no corpo)
X-Solaria-DeliveryId único da entrega (igual a id) — use para descartar repetidas
X-Solaria-Signaturesha256= + HMAC-SHA256 do corpo bruto com o segredo do webhook
User-AgentSOLARIA-Webhooks/1.0

Validar a assinatura#

Calcule o HMAC-SHA256 do corpo exatamente como chegou usando o segredo, prefixe com sha256= e compare com o header em tempo constante. Recuse a entrega se não bater.

import crypto from "node:crypto"
import express from "express"

const app = express()
const SECRET = process.env.SOLARIA_WEBHOOK_SECRET // whsec_...

// use o corpo BRUTO: re-serializar o JSON muda os bytes e quebra a assinatura
app.post("/hooks/solaria", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex")
  const got = req.get("X-Solaria-Signature") ?? ""
  const ok = got.length === expected.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))
  if (!ok) return res.sendStatus(401)

  const event = JSON.parse(req.body)
  res.sendStatus(200) // responda rápido; processe depois
  console.log(event.event, event.data)
})

Tentativas e prazos#

  • Responda com qualquer status 2xx em até 10 segundos. Processe o evento em segundo plano se for demorar.
  • Em erro de rede, tempo esgotado ou status 5xx, o SOLAR.IA tenta de novo: até 3 tentativas no total, esperando 1s e depois 4s. Status 4xx não é repetido.
  • Uma mesma ocorrência pode chegar mais de uma vez: use X-Solaria-Delivery para ignorar repetidas.
  • Cada entrega (status, resposta, tempo e tentativas) fica no histórico do webhook no painel.

Endereços aceitos#

A URL precisa ser http:// ou https:// e responder pela internet. Por segurança, endereços internos (localhost, redes privadas) são recusados e redirecionamentos não são seguidos. Para testar localmente, use um túnel como ngrok ou Cloudflare Tunnel.