Configurar un webhook
Avisar a su propio sistema de cada entrega, rebote, acuse y constancia, con una petición firmada.
Esta es la receta que el servidor MCP entrega a los asistentes de IA, tal cual. Está en inglés porque los modelos la siguen mejor así; el asistente le responde en su idioma.
Un asistente la lee con docs({uri:"docs://recipes/set-up-a-webhook"})
docs://recipes/set-up-a-webhook — push the firm's delivery events to another system, signed
Use when the lawyer wants their ERP, CRM or spreadsheet told when a notification is sent,
delivered, bounced, opened, downloaded, acknowledged, or gets its constancia — instead of polling.
Needs a token with the webhooks:manage scope held by an OWNER (anyone else: X_FORBIDDEN).
1. whoami({})
→ orgId, role, scopes
role is not "owner", or scopes lack "webhooks:manage"? Stop: an owner issues such a token.
skip if you already hold orgId
2. Ask for the receiving URL. It must be https, on the default port 443 (no ":8443"), and a public
host. localhost, *.local, *.internal and any host resolving to a private, link-local or CGNAT
address are refused (X_WEBHOOK_URL_PRIVATE / _PORT / _NOT_HTTPS). Do not try to work around it.
3. manage_resource({resource:"webhooks", action:"createWebhookEndpoint", params:{orgId:"<orgId>", url:"<https URL>", description:"ERP", eventTypes:["delivery.delivered","delivery.bounced","constancia.issued"]}})
eventTypes: 1 to 10 of notification.sent, delivery.failed, delivery.delivered,
delivery.bounced, delivery.complained, recipient.opened, recipient.downloaded,
recipient.acknowledged, recipient.replied, constancia.issued — ask which ones they want.
→ secret ("whsec_…", shown ONCE) and endpoint {id, status, …}
Hand the secret to the person setting up the receiver now; it is never shown again. If lost:
manage_resource({resource:"webhooks", action:"rotateWebhookSecret", params:{orgId:"<orgId>", endpointId:"<endpoint.id>"}})
(both secrets sign for 24 hours).
4. manage_resource({resource:"webhooks", action:"sendTestWebhook", params:{orgId:"<orgId>", endpointId:"<endpoint.id>"}})
→ ok, statusCode, durationMs, error (timeout | connect | url_refused | http_status)
ok false? Tell the user what the receiver answered; nothing else changes.
5. The receiver verifies each POST:
Notificado-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, t + "." + raw body)>
Refuse when t is more than 300 s off; accept when any v1 matches. Deduplicate on the
Notificado-Event-Id header (the evidence event id). Answer 2xx within 10 s.
Done when the endpoint exists and a test answered ok, or you told the user why it did not.
Tips
- Only events from registration on are sent; history is not replayed.
- Failed deliveries retry up to 8 times over about 24 h. Each one is in
manage_resource({resource:"webhooks", action:"listWebhookDeliveries", params:{orgId:"<orgId>", endpointId:"<endpoint.id>"}})
A day of nothing but failures disables the endpoint and mails the owners;
manage_resource({resource:"webhooks", action:"updateWebhookEndpoint", params:{orgId:"<orgId>", endpointId:"<endpoint.id>", state:"active"}})
resumes it and delivers what waited. state:"paused" pauses it on purpose.
- recipient.opened is an indicio, never proof of reading — see docs://recipes/what-counts-as-delivered.
- The surtida date is not an event: read docs://recipes/track-deadlines after
recipient.acknowledged or recipient.downloaded.
- A webhook is a notice, not evidence. The proof stays in the constancia and the evidence zip.
- The full guide, with Node and Python verification code: https://www.notificado.co/docs/webhooks