Pular para o conteúdo principal

Notificações por webhook

Um webhook avisa o seu sistema quando um estudo, um pedido ou uma execução muda de estado. Com ele, o seu sistema pode, por exemplo, começar uma análise assim que os resultados de um cenário ficarem prontos, sem consultar a API em intervalos.

Cada organização tem uma configuração de webhook, gerida por GET/PUT/DELETE /api/v1/webhook. Consultar exige webhooks:read; configurar, desligar, trocar o segredo e refazer entregas exigem webhooks:write.

Esta página trata do webhook configurado pela API. Configurar por PUT /webhook substitui a URL e o segredo cadastrados em Perfil e desliga os avisos escolhidos lá.

Configurar​

Informe uma URL HTTPS pública e a lista de eventos:

curl -sS -X PUT "$MYRIA_API/api/v1/webhook" \
-H "Authorization: Bearer $MYRIA_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: configurar-webhook-001" \
-d '{"url":"https://hooks.example.test/myria","events":["execution.completed","execution.failed","operation.failed"]}'

O corpo aceita só url e events. events não pode ser vazio nem repetir eventos; um evento desconhecido retorna 400 invalid_webhook_event.

Todo PUT gera um segredo novo, mesmo quando só a lista de eventos muda, e cancela as entregas pendentes. A resposta traz a configuração (url, events, enabled, configured_at, secret_last_rotated_at), o segredo em secret, secret_one_time: true e pending_deliveries_cancelled, com um aviso em pending_deliveries_warning quando alguma entrega foi cancelada. Guarde o segredo na hora: GET /webhook nunca o devolve.

Repetir a mesma requisição com a mesma Idempotency-Key devolve byte a byte a primeira resposta, inclusive o mesmo segredo. Uma chave nova com o mesmo corpo é um novo PUT e gera outro segredo.

GET /webhook devolve a configuração atual, ou 404 se não houver webhook configurado.

Para trocar só o segredo, use POST /api/v1/webhook/rotate-secret com uma nova Idempotency-Key. A troca também cancela as entregas pendentes e responde 404 se não houver webhook configurado. Atualize o segredo no seu sistema antes de refazer entregas: uma nova entrega é assinada com o segredo atual.

Eventos​

EventoQuando é enviado
study.updatedO nome, a descrição ou a versão de referência de um estudo mudou.
study.deletedUm estudo foi marcado como excluído.
operation.succeededUm pedido (Operation) terminou com sucesso.
operation.failedUm pedido falhou.
operation.cancelledUm pedido foi cancelado.
operation.attention_requiredUm pedido precisa de análise.
execution.startedUma execução começou.
execution.completedUma execução terminou com sucesso.
execution.failedUma execução falhou.
execution.stoppedUma execução foi parada.
execution.attention_requiredUma execução precisa de análise.

execution.completed, execution.failed e execution.stopped só são enviados depois que o encerramento foi confirmado. execution.attention_required indica que a execução precisa de análise, por exemplo porque um resultado obrigatório não foi coletado; não o trate como autorização para iniciar outra execução.

Formato da notificação​

Cada notificação é um POST com corpo JSON:

{
"id": "4dfb20e7-94d9-4232-a510-0f0efef401b5",
"type": "execution.completed",
"created_at": "2026-09-30T13:20:00+00:00",
"organization": {"id": 12, "slug": "minha-org", "name": "Minha Organização"},
"data": {
"execution_id": "exec_0123456789abcdef0123456789abcdef",
"study_key": "minha-org-191",
"operation_id": "op_0123456789abcdef0123456789abcdef",
"status": "succeeded",
"status_url": "/api/v1/executions/exec_0123456789abcdef0123456789abcdef",
"reason_codes": []
}
}

id é o identificador do evento e created_at, o momento em que ele aconteceu. O conteúdo de data depende do tipo:

EventosCampos de data
execution.*execution_id, study_key, operation_id, status (running, succeeded, failed, cancelled ou attention_required), status_url, reason_codes
operation.*operation_id, status, status_url, kind, domain
study.updatedstudy_key, changed_fields (name, description ou baseline), operation_id
study.deletedstudy_key, name, deleted_at, operation_id

execution.stopped chega com status: "cancelled", o mesmo estado que GET /api/v1/executions/{execution_id} mostra. reason_codes é sempre uma lista, vazia quando não há causa a informar, e contém só códigos estáveis.

Os cabeçalhos de cada notificação são:

Content-Type: application/json
User-Agent: Myria-Webhooks/2.0
X-Myria-Event: execution.completed
X-Myria-Event-ID: 4dfb20e7-94d9-4232-a510-0f0efef401b5
X-Myria-Delivery: 9507c9e7-291b-4e56-98c7-f307906554c6
X-Myria-Timestamp: 1790774400
X-Myria-Signature-256: sha256=<hex>

X-Myria-Event-ID é igual ao id do corpo. X-Myria-Delivery identifica a entrega.

Validar a assinatura​

A assinatura é um HMAC-SHA256 do timestamp, um ponto e os bytes originais do corpo, com o segredo do webhook como chave:

HMAC-SHA256(secret, timestamp + "." + raw_body)

O resultado, em hexadecimal, vem em X-Myria-Signature-256 depois de sha256=. Ao receber uma notificação:

  1. calcule a assinatura sobre o corpo bruto, antes de interpretar o JSON, com o valor de X-Myria-Timestamp;
  2. compare com X-Myria-Signature-256 usando uma comparação de tempo constante;
  3. confira se o id do evento (X-Myria-Event-ID) já foi processado e ignore repetições;
  4. grave o evento e só então responda com 2xx.

Cada tentativa de envio, inclusive as novas tentativas automáticas, leva um X-Myria-Timestamp novo e uma X-Myria-Signature-256 calculada com esse timestamp. O corpo, X-Myria-Delivery e X-Myria-Event-ID não mudam entre as tentativas. Recuse notificações com timestamp de mais de 5 minutos em relação ao seu relógio e deduplique pelo id do evento: juntos, os dois bloqueiam repetições maliciosas sem perder as novas tentativas legítimas.

Falhas e novas tentativas​

Uma entrega é bem-sucedida quando o seu endpoint responde 2xx em até 3 segundos para conectar e 10 segundos para responder. Qualquer outra resposta, inclusive redirecionamentos 3xx, conta como falha: o Myria não segue redirecionamentos.

Depois de uma falha, o Myria tenta de novo até completar 8 tentativas, com intervalos de 60 segundos, 5 minutos, 15 minutos, 1 hora, 4 horas, 12 horas e 24 horas.

GET /api/v1/webhook/deliveries lista as entregas, com paginação por cursor. Cada item traz id (o id da entrega, o mesmo de X-Myria-Delivery), event_id, event, status, attempts, created_at e delivered_at:

statusSignificado
pendingAguarda envio.
processingEstá sendo enviada agora.
retryingFalhou e aguarda nova tentativa automática.
deliveredO seu endpoint respondeu com sucesso.
failedAs tentativas automáticas acabaram sem sucesso.
cancelledFoi cancelada, por exemplo, ao trocar a configuração ou o segredo.

Para enviar um evento de novo:

curl -sS -X POST \
"$MYRIA_API/api/v1/webhook/deliveries/$DELIVERY_ID/redeliver" \
-H "Authorization: Bearer $MYRIA_TOKEN" \
-H "Idempotency-Key: refazer-entrega-001"

redeliver cria uma entrega nova, com outro X-Myria-Delivery, timestamp e assinatura novos (calculada com o segredo atual) e o mesmo id de evento e o mesmo corpo. A resposta é 202 Operation. Se o evento não estiver mais entre os eventos configurados, ou o webhook estiver desligado, a resposta é 409 webhook_event_disabled.

Desligar​

DELETE /api/v1/webhook, com webhooks:write e Idempotency-Key, apaga a URL, os eventos e o segredo, cancela as entregas pendentes e mantém o histórico de entregas. A resposta é 200 com enabled: false e pending_deliveries_cancelled, também quando o webhook já estava desligado.

Requisitos da URL​

A URL precisa usar HTTPS e estar acessível pela internet. O Myria valida o destino no cadastro e antes de cada entrega. Endereços de rede privada, redirecionamentos para rede privada, DNS rebinding e falha na revalidação bloqueiam o envio. Isso impede que um webhook seja usado para alcançar serviços internos.