Saltar al contenido principal
Proton v5

Webhooks

Proton envía un POST firmado a la dirección de la empresa cuando algo pasa en las automatizaciones: una ejecución termina, un lote termina o no se ejecuta, una ocurrencia se abre, se asigna o se resuelve, un runner se cae o vuelve. Es el camino para la herramienta de monitoreo, de tickets o de mensajes de la empresa. Para leer los mismos eventos bajo demanda, usa GET /v1/events.

Los webhooks forman parte del plan Enterprise. El registro está en Configuraciones > Alertas y webhooks, en la pestaña Webhooks: el nombre, la dirección y los eventos (o todos).

Lo que llega​

El envío sigue el estándar Standard Webhooks:

POST https://monitoreo.ejemplo.com/proton
content-type: application/json
user-agent: Proton-Webhooks/1.0
webhook-id: evt_1532
webhook-timestamp: 1760886000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
{
"type": "run.finished",
"timestamp": "2026-10-19T15:00:00.000Z",
"version": "1",
"data": {
"organizationId": 12,
"link": "https://app.protoncloud.com.br/automations/center/dataset/40/cycle/7/dataset-run/981/details",
"run": {
"id": 981,
"status": "Failed",
"endReason": "technical_error",
"startedAt": "2026-10-19T14:58:00.000Z",
"finishedAt": "2026-10-19T15:00:00.000Z",
"durationMs": 120000,
"correlationId": "7f6c2d2e-5f0b-4a4a-9b9b-3c1f2a9e0d11",
"retryScheduled": false,
"link": "https://app.protoncloud.com.br/automations/center/dataset/40/cycle/7/dataset-run/981/details"
},
"automation": { "id": 5, "name": "Registro de proveedor" },
"dataset": { "id": 40, "name": "Sucursal 01" },
"group": { "id": 3, "name": "Operación", "purpose": "operation" },
"cycle": { "id": 7, "name": "Octubre" },
"environment": { "id": 2, "name": "Producción", "production": true },
"runner": { "id": 9, "name": "rpa-01", "hostname": "rpa-01" }
}
}

Los eventos​

TipoCuándoCampos en data
run.finishedUna ejecución terminórun (estado, motivo del fin, inicio, fin, duración, Correlation ID), automation, dataset, group, cycle, environment, runner
batch.finishedUn lote terminóbatch (total, passed, failed, stopped), group, cycle, environment, runner
batch.not_runUna programación no salióbatch (con el reason), group, cycle, environment, runner
exception.opened, exception.assigned, exception.resolvedUna ocurrencia de la operación se abrió, se asignó o se resolvióexception (kind, status, occurrences, reason, errorSummary, assignee), automation, dataset, group, cycle, environment
runner.offline, runner.onlineUn runner se cayó (después de 2 minutos fuera) o volviórunner (name, owner, hostname, offlineSince, shared)
webhook.testLa prueba desde la pantallaorganizationId, message

El motivo del fin de la ejecución (endReason) es uno de estos: passed, business_error, technical_error, environment_error, stopped, not_dispatched, runner_lost.

La versión del cuerpo (version) solo cambia si un campo sale o cambia de sentido. Un campo nuevo puede aparecer en cualquier momento: trata lo que no conoces como opcional.

Verificar la firma​

El secreto del webhook (whsec_...) aparece una sola vez, en el registro y en el cambio. La firma es el HMAC-SHA256 de webhook-id.webhook-timestamp.cuerpo, con la clave en base64 que viene después de whsec_, codificado en base64 y precedido de v1,. Verifica siempre el cuerpo exactamente como llegó, antes de interpretar el JSON, y rechaza el mensaje con un webhook-timestamp muy antiguo (más de 5 minutos).

import base64
import hashlib
import hmac
import time


def firma_valida(secreto: str, encabezados: dict, cuerpo: bytes, tolerancia: int = 300) -> bool:
clave = base64.b64decode(secreto.removeprefix("whsec_"))
id_del_mensaje = encabezados["webhook-id"]
momento = encabezados["webhook-timestamp"]
if abs(time.time() - int(momento)) > tolerancia:
return False

contenido = f"{id_del_mensaje}.{momento}.".encode() + cuerpo
esperada = base64.b64encode(hmac.new(clave, contenido, hashlib.sha256).digest()).decode()

for firma in encabezados["webhook-signature"].split(" "):
version, _, valor = firma.partition(",")
if version == "v1" and hmac.compare_digest(valor, esperada):
return True
return False

Las bibliotecas de Standard Webhooks (para Python, Node.js, Java, Go, C# y otras) hacen esta verificación.

Entrega y nuevos intentos​

  • Respuesta: cualquier 2xx en hasta 10 segundos cuenta como entregado. Responde rápido y procesa después.
  • Nuevos intentos: sin 2xx, Proton intenta de nuevo con espera creciente (30 segundos, 1 minuto, 2 minutos... hasta 1 hora entre uno y otro), hasta 8 intentos.
  • Repetición: el webhook-id es el mismo en todos los intentos del mismo evento. Guarda los ids recibidos y descarta el repetido.
  • Dirección: solo https, en una dirección pública. Las redirecciones no se siguen.
  • Seguimiento: la pestaña Historial de Alertas y webhooks muestra cada entrega, con el resultado, el estado HTTP y el error, y permite reenviar la que falló. El botón de prueba envía un webhook.test.