Webhooks And Notifications

The backend has a notification worker that sends SMTP messages and signed webhook deliveries from the notifications.to_send queue. Invitations and billing export delivery metadata are currently the most visible email flows; certificate webhooks are emitted for certificate.issued.

Webhook Routes

ActionRouteRole
List subscriptionsGET /api/v1/webhooksCLIENT_ADMIN
Create subscriptionPOST /api/v1/webhooksCLIENT_ADMIN
Delete subscriptionDELETE /api/v1/webhooks/{id}CLIENT_ADMIN

Example:

  {
  "url": "https://hooks.example.com/wipe",
  "events": ["certificate.issued"]
}
  

The subscription secret is returned once on creation. Store it in the receiving system immediately.

Delivery Security

ControlBehavior
SSRF policyWebhook URLs pass the outbound URL policy and reject private, loopback, link-local, credential-bearing, and unsupported URLs by default.
SignatureWebhook body is signed with X-Wipe-Signature: sha256=<hmac>.
Secret custodyThe webhook secret is generated by the backend and returned once.
RetryNotification worker retries with backoff and writes DLQ metadata after exhaustion.

Use SECURITY_OUTBOUND_ALLOWED_HOSTS for known webhook hosts. Use private-network overrides only in controlled internal deployments.

Notification Worker

The worker command is:

  /app/worker notifications
  

Relevant variables:

VariableUse
NOTIFICATION_TRANSPORTdisabled or SMTP/webhook-capable delivery mode.
NOTIFICATION_HTTP_TIMEOUTOutbound webhook timeout.
NOTIFICATION_SMTP_HOST, NOTIFICATION_SMTP_PORTSMTP endpoint.
NOTIFICATION_SMTP_USERNAME, NOTIFICATION_SMTP_PASSWORDOptional SMTP credentials.
NOTIFICATION_SMTP_FROMEnvelope/display sender.
NOTIFICATION_SMTP_TLSnone, starttls, or provider-specific TLS mode.
NOTIFICATION_SMTP_TIMEOUTSMTP timeout.

Local compose sends SMTP to Mailpit on port 1025.

Email Templates

Invitation email templates are organization branding fields:

FieldPurpose
email_templates.user_invited_subjectSubject line.
email_templates.user_invited_textPlain-text body.
email_templates.user_invited_htmlHTML body.

The backend renders variables such as {{app_name}}, {{logo_url}}, {{accept_url}}, {{email}}, {{role}}, and {{organization_name}}.

Current Event Coverage

EventState
certificate.issuedEmitted from certificate finalization.
Billing export delivery updatesRecorded by the notification worker.
Invitation emailsRendered from branding templates and sent through notification delivery.
proof.* state eventsSubscribable direction exists, but producers for failed/rejected/awaiting-license transitions remain a tracked backend gap.

Triage

SymptomCheck
Invitation email missingNOTIFICATION_TRANSPORT, SMTP settings, notifications.to_send queue, Mailpit/SMTP logs.
Webhook not deliveredURL policy rejection, HTTP timeout, target status code, DLQ reason.
Receiver rejects signatureConfirm receiver uses the one-time secret and raw request body.
Repeated retriesFix target dependency first; do not replay DLQ until root cause is understood.