docs / Platform

Outgoing webhooks

Every event the platform can send, the signed payload, retries, and the SSRF rules every delivery passes through.

A webhook is a URL the platform calls when something happens. Anything the UI would show a person can be delivered: ticket changes, SLA transitions, service status, backups, and every in-app notification. Deliveries are signed, retried, logged for 30 days, and refused to any address that looks like the inside of your network unless you say otherwise.

Webhooks are created on the Integrations page or with the createWebhook mutation, by an admin.

Events

Family Events
ticket ticket.created, ticket.updated, ticket.status_changed, ticket.assigned, ticket.commented, ticket.resolved, ticket.closed, ticket.deleted
workflow workflow.started, workflow.completed, workflow.failed, workflow.step_completed
sla sla.breached, sla.at_risk, sla.met, sla.daily_report
service service.down, service.up, service.degraded
backup backup.success, backup.failed
user user.created, user.updated
notification notification.created, one per in-app notification, with the notification's own type in the payload

webhookEventTypes returns the list the running version supports.

The payload

POST https://hooks.example.com/itops
Content-Type: application/json
X-ITOps-Signature: sha256=3f1a…

{
  "id": "a4c1…",
  "event": "service.down",
  "timestamp": "2026-09-11T13:30:07Z",
  "data": {
    "service": { "id": "…", "name": "notification-service", "path": "meridian/commerce/prod/eu-central-1/notification-service" },
    "previousStatus": "OPERATIONAL", "status": "DOWN", "message": "0/2 replicas ready"
  }
}

data is the object the event is about, in the same shape GraphQL returns it. customPayloadTemplate on the webhook replaces the body with your own template for receivers that want a specific format, such as a chat message.

Signature

When the webhook has a secret, the body is signed with HMAC-SHA256 and the hex digest is sent as X-ITOps-Signature: sha256=<hex>. Verify it before trusting the body:

echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET"

Configuration

Field Meaning
url, method the target; POST unless you say otherwise
secret enables the signature
headers extra headers, for a bearer token the receiver expects
events which of the above to deliver
filters narrow by attributes of the event, for example one SLA group or one ticket priority
timeoutMs per attempt
retryMax, retryDelayMs, retryBackoff how many times and how patiently to retry a non-2xx or a timeout
status ACTIVE, INACTIVE, or ERROR once retries are exhausted repeatedly
customPayloadTemplate replace the default body

testWebhook sends a synthetic event immediately. webhookExecutions lists every attempt with status code, duration and response excerpt for 30 days; webhookStats summarises them.

What the platform refuses to call

Every delivery, and every redirect it is sent through, passes the URL validator:

  • only http and https; file://, gopher://, ldap:// and the rest are rejected;
  • the host is resolved and every address it resolves to is checked: loopback, private ranges (10/8, 172.16/12, 192.168/16), link-local, carrier-grade NAT, multicast and unspecified addresses are refused, and the cloud metadata address 169.254.169.254 is refused by name as well;
  • a DNS failure is a refusal, not a retry;
  • redirects are followed at most five times and each hop is validated again, so a public URL that bounces to an internal one is caught.

This closes the classic server-side request forgery hole where a webhook is pointed at the cluster's own metadata service or an internal admin port. Two settings loosen it, deliberately:

Setting Effect
ITOPS_SECURITY_WEBHOOK_HOST_ALLOWLIST=hooks.example.com,alerts.internal only these hosts may be called, and private addresses are allowed for them
ITOPS_SECURITY_ALLOW_PRIVATE_WEBHOOKS=true allow any private address. Development only.

A receiver inside the cluster is therefore an allowlist entry plus, if the network policy is on, an egress rule for its namespace.

Receiving in Slack, Teams or a chat tool

Point the webhook at the incoming-webhook URL the chat tool gives you and use customPayloadTemplate to produce the message shape it expects. The notification.created event is the convenient one for this: it fires for everything a person would have been notified about in the UI, with the human-readable text already composed.

Documentation for ITOps 4.2 · charts itops 2.0.0, sla-portal 1.4.0 · rendered 2026-09-11