Payloads de webhook
Configure destinos, valide a assinatura e conheça o payload de cada evento que o Mira envia.
O Mira envia eventos JSON para destinos HTTPS cadastrados por projeto. O receptor não precisa de conta nem de chave de API do Mira: o segredo do destino autentica cada mensagem.
Configurar um destino
Quem faz: admin do projeto.
Abrir Webhooks
Em Configurações do projeto, abra Integrações e clique em Webhooks. O painel lateral lista os destinos.
Adicionar o destino
Clique em Adicionar destino e informe nome, URL HTTPS pública e os eventos que o destino recebe.
Guardar o segredo
Salve e copie o segredo (mira_whsec_...) para a configuração privada do receptor. Ele aparece uma vez e não pode ser consultado depois.
Testar
Use Enviar teste e abra Histórico de entregas para ver o resultado. O teste publica webhook.test só para esse destino, com data.report: null.
A URL precisa usar HTTPS com certificado válido. O Mira bloqueia HTTP, endereços privados, loopback e redes especiais, inclusive quando o DNS resolve para elas. O Mira não segue redirects e recusa URLs com usuário, senha ou fragmento. Não coloque segredos na URL. O envio espera até dez segundos pelos headers da resposta.
Eventos
| Evento | Quando | Dados adicionais |
|---|---|---|
report.created | Novo report confirmado | Snapshot do report |
report.updated | Alteração efetiva de campos ou tags | Snapshot final e changes, com before e after por campo |
comment.created | Comentário adicionado | Snapshot do report e o comment |
attachment.created | Link ou arquivo adicionado | Snapshot do report e o attachment |
error.created | Primeira ocorrência de um erro novo | Snapshot do erro em error |
error.regressed | Erro resolvido volta a não resolvido numa release com build posterior | Snapshot do erro em error |
error.spike | Pico de ocorrências marcado no erro | Snapshot do erro em error |
digest.daily | Todo dia às 09:00 no fuso configurado, se o projeto tem reports parados | Resumo em data.digest |
digest.weekly | Segunda-feira, quando o resumo semanal do projeto é gravado | Resumo em data.digest, com kind: "weekly" |
webhook.test | Teste administrativo | report nulo e changes vazio |
Os eventos error.*, digest.daily e digest.weekly só chegam a destinos que os marcam na configuração. No formulário, os digests aparecem como Digest diário e Resumo semanal. Um mesmo erro publica error.spike no máximo uma vez por hora.
Uma atualização de vários campos produz um único report.updated. Repetir uma criação idempotente, ou atualizar com os mesmos valores, não publica evento. Renomear uma tag não publica alteração de report. Excluir uma tag em uso publica report.updated para os reports afetados.
Envelope
Todo evento tem o mesmo envelope:
{
"id": "5f0b9d0e-3c1a-4b8e-9a53-0c4f6a7d2e11",
"type": "report.updated",
"version": 1,
"occurredAt": "2026-11-04T14:32:10.000Z",
"projectId": "b7c4f2d8-1e6a-4f0c-8d3b-9a2e5c7f1a40",
"actor": { "id": "…", "name": "Ana Souza", "email": "ana@empresa.com" },
"data": {
"report": { "id": "…", "number": 42, "type": "bug", "title": "…", "status": "triaged" },
"changes": {
"status": { "before": "new", "after": "triaged" },
"priority": { "before": null, "after": "high" }
}
}
}| Campo | Conteúdo |
|---|---|
id | UUID do evento. É o mesmo em todas as tentativas |
type | Um dos tipos da tabela acima |
version | Sempre 1 |
occurredAt | Data ISO 8601 |
projectId | UUID do projeto |
actor | Quem causou o evento, com id, name e email. Nulo nos eventos error.* e digest.* |
data | report, changes e, conforme o tipo, comment, attachment, error ou digest |
Aceite campos novos dentro do contrato v1 e confira type antes de interpretar data.
Snapshot do report
data.report traz id, projectId, number, type, title, description, priority, status, reporter, assignee, environmentId, environment, pageUrl, tags, attachments, createdAt e updatedAt. Cada item de attachments traz id, kind, name, contentType, sizeBytes, url (só em links) e createdAt. O snapshot não leva o contexto técnico, a metadata dos anexos nem o conteúdo dos arquivos.
Campos de texto e URLs podem conter dados pessoais digitados por quem reportou. Restrinja o acesso e a retenção no receptor.
Ao marcar um duplicado, changes inclui duplicateOfId e o novo status.
Eventos de comentário e anexo
comment.created acrescenta data.comment com id, reportId, author, body, origin, agentBadge, parentId, createdAt e editedAt. attachment.created acrescenta data.attachment com os campos de anexo descritos acima.
Eventos de erro
Nos eventos error.*, actor e data.report são nulos, changes é vazio e data.error traz o estado do erro:
{
"id": "…",
"type": "error.created",
"version": 1,
"occurredAt": "2026-11-04T14:40:02.000Z",
"projectId": "…",
"actor": null,
"data": {
"report": null,
"changes": {},
"error": {
"id": "…",
"environmentId": "…",
"fingerprint": "…",
"type": "TypeError",
"message": "Cannot read properties of undefined (reading 'total')",
"culprit": "src/checkout/summary.ts in computeTotal",
"level": "error",
"status": "unresolved",
"eventCount": 1,
"userCount": 1,
"firstSeenAt": "2026-11-04T14:40:02.000Z",
"lastSeenAt": "2026-11-04T14:40:02.000Z",
"firstRelease": "1.4.0+abc1234",
"lastRelease": "1.4.0+abc1234",
"resolvedAt": null,
"spikeDetectedAt": null,
"regressedAt": null
}
}
}O snapshot também inclui projectId, resolvedBuildTime e regressionEnabled.
Digests
digest.daily e digest.weekly trazem data.digest, com:
kind(dailyouweekly),generatedAt,timezone,project(id,key,name) eurldo projeto.countscom os totais deaging(envelhecendo),unassigned(sem responsável) eurgentStalled(urgentes parados).- as listas
aging,unassignedeurgentStalled, com até 20 reports cada, onde cada item traznumber,title,type,priority,assignee,createdAt,lastActivityAteurl. - No semanal,
periodcomstarteend(a semana anterior, de segunda a segunda) esummarycombody,modeleperiod.
O digest diário só sai quando o projeto tem ao menos um report em alguma das três listas. O semanal sai quando o resumo escrito pelo assistente é gravado, mesmo com as listas vazias.
Verificar a assinatura
Cada POST inclui:
Content-Type: application/json
Mira-Webhook-Id: UUID do evento
Mira-Webhook-Timestamp: Unix timestamp em segundos
Mira-Webhook-Signature: v1=HMAC hexadecimalA chave é o segredo completo (mira_whsec_...), usado como string UTF-8. O HMAC usa SHA-256 sobre id.timestamp.corpo, onde corpo são os bytes exatos recebidos, antes de qualquer parse. Durante uma rotação de segredo, o header traz duas assinaturas separadas por vírgula. Aceite a mensagem se uma delas bater.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhook(
secret: string,
id: string,
timestamp: string,
signatures: string,
rawBody: Buffer,
): boolean {
if (!/^\d+$/.test(timestamp)) return false;
const seconds = Number(timestamp);
if (!Number.isSafeInteger(seconds)) return false;
if (Math.abs(Date.now() / 1000 - seconds) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${id}.${timestamp}.`)
.update(rawBody)
.digest();
return signatures.split(",").some((entry) => {
const candidate = entry.trim();
if (!/^v1=[a-f0-9]{64}$/.test(candidate)) return false;
return timingSafeEqual(Buffer.from(candidate.slice(3), "hex"), expected);
});
}Rejeite headers ausentes antes de chamar a função. A janela de cinco minutos exige relógios sincronizados. Armazene o Mira-Webhook-Id para deduplicar, e só responda 2xx depois de aceitar o trabalho de forma durável. Uma resposta 2xx confirma a entrega, mesmo sem corpo.
Tentativas e histórico
Cada ciclo tem seis tentativas: a inicial e cinco retries, depois de 1 minuto, 5 minutos, 15 minutos, 1 hora e 6 horas. Qualquer resposta fora de 2xx, falha de DNS, conexão, TLS ou timeout conta como falha. O Mira registra início, conclusão, status HTTP, duração e um código de erro de cada tentativa, mas não o corpo nem os headers da resposta.
As entregas podem se repetir e chegar fora de ordem. Não há garantia de execução exatamente uma vez no receptor, então trate o id como chave de idempotência. A nova tentativa usa o mesmo id e o mesmo corpo, com timestamp e assinatura novos.
Depois da última falha, Reenviar inicia outro ciclo. Não há reenvio de entrega já confirmada. O histórico, com payload e tentativas, é visível só para admins. Pausar um destino cancela as pendências, e reativar não reproduz o que aconteceu durante a pausa. Excluir impede novos envios e preserva o histórico.
Rotação do segredo
Em Webhooks, gere um novo segredo. A chave nova aparece uma vez. Atualize o receptor em até 15 minutos: durante essa janela o Mira assina com a chave nova e a anterior, e depois usa só a nova. Outra rotação fica bloqueada até o fim da janela.