MCP webhooks
Send MCP tool-call events to your own receiver for alerts, automation, and reporting. Configure webhooks per workspace and verify each delivery before processing it.
A webhook carries recorded MCP activity to your systems. Delivery retries are limited, so use logs alongside webhook-driven workflows when investigating a missing event.
How it works
You register an HTTPS endpoint you own, and Evlio delivers event notifications to it as JSON POST requests as activity occurs on your MCP server. Your endpoint acknowledges with a 2xx, and whatever automation you attach — a chat message, a ticket, a row in the warehouse — runs from there.
Set up a receiver
A workspace admin can open Settings → Webhooks, choose the workspace, and select Add webhook. Enter a public HTTPS Webhook URL and a signing secret, then save. Configure the same secret in your receiver's secret store. Evlio sends mcp.tool_call events; workspace audit events are not included.
Use Send test event and inspect Last delivery and Status. A receiver must acknowledge with 2xx within five seconds. Validate and durably accept or queue the event before responding, then perform slower processing asynchronously.
The Secret field is optional, but a receiver using the verification example below requires it. Saved secrets are encrypted and are not returned in full. Leaving the field unchanged preserves the existing secret; removing it stops signature generation.
Payload versions
New webhooks use the 2026-10-01 payload by default. v1 is the legacy format: existing v1 endpoints keep working until you switch them. Pin payloadVersion to the version your receiver implements.
v1 (legacy) | clientName identifies the agent, durationMs measures the upstream call, and timestamp is delivery time. test appears only for test events. The signature covers the raw body. |
2026-10-01 (default) | Adds id, version, agent and caller identity, an error field, and separate timing fields. timestamp is tool-call completion time. The signature covers timestamp + '.' + raw body. |
Webhook configuration exposes payloadVersion, events, and optional headers. Pin payloadVersion to the receiver implementation and subscribe to mcp.tool_call. Custom header values are stored encrypted and can supply receiver authentication; they cannot replace Content-Type or Evlio signature, timestamp, event-ID, or version headers.
Example v1 test event
{
"type": "mcp.tool_call",
"test": true,
"workspace": "example-workspace",
"deploymentId": "example-workspace/orders-mcp",
"mcpSlug": "orders-mcp",
"tool": "get_order",
"clientName": "Example Agent",
"statusCode": 200,
"durationMs": 42,
"timestamp": "2026-09-14T10:00:00.000Z"
}
Example 2026-10-01 test event
{
"version": "2026-10-01",
"id": "9d1f4c2a-6b18-4c0e-9f2a-7c5b1e0a3d44",
"type": "mcp.tool_call",
"test": true,
"workspace": "example-workspace",
"deploymentId": "example-workspace/orders-mcp",
"mcpSlug": "orders-mcp",
"tool": "get_order",
"agentId": null,
"agentName": "Example Agent",
"agentVersion": null,
"clientId": null,
"userId": null,
"sessionId": null,
"callerFingerprint": null,
"statusCode": 200,
"error": null,
"durationMs": 46,
"upstreamMs": 42,
"gatewayMs": 51,
"timestamp": "2026-09-14T10:00:00.000Z"
}
In the versioned event, durationMs measures the tool handler end to end, upstreamMs measures the upstream call, and gatewayMs measures the HTTP request through response. Identity and timing values can be null. statusCode is null when no upstream HTTP response arrived; error then describes the failure. Agent attribution is not proof of a verified human identity.
Verify the signature
Deliveries use Content-Type: application/json and User-Agent: Evlio-Webhooks/1.0. With a secret configured, X-Evlio-Signature is sha256= followed by the HMAC-SHA256 hex digest. Preserve the exact raw request bytes before parsing JSON.
For v1, calculate the HMAC over the raw body only. Newer versions also send X-Evlio-Timestamp in Unix seconds, X-Evlio-Event-Id, and X-Evlio-Event-Version; calculate the HMAC over the timestamp, a period, and the raw body. Reject a timestamp more than 300 seconds from your clock and use a constant-time comparison.
import hashlib
import hmac
import time
def verify_evlio(raw_body, headers, secret, version):
# Capture raw_body as bytes before parsing JSON.
# Normalize header names to lowercase.
# Read version from your saved webhook configuration.
if not isinstance(raw_body, bytes) or not secret:
return False
if version not in ("v1", "2026-10-01"):
return False
signature = headers.get("x-evlio-signature", "")
if not isinstance(signature, str) or len(signature) != 71:
return False
if not signature.startswith("sha256="):
return False
if any(char not in "0123456789abcdef" for char in signature[7:]):
return False
signed = raw_body
if version != "v1":
if headers.get("x-evlio-event-version") != version:
return False
stamp = headers.get("x-evlio-timestamp", "")
if not isinstance(stamp, str) or not stamp.isascii() or not stamp.isdigit():
return False
if abs(int(time.time()) - int(stamp)) > 300:
return False
signed = (stamp + ".").encode("ascii") + raw_body
digest = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest("sha256=" + digest, signature)
Read the expected payload version from your saved configuration. After signature verification, parse JSON and validate the event type. Deduplicate versioned tool events by the signed body id and confirm that it matches X-Evlio-Event-Id. For a verification request, compare the signed challenge to that header. Record the deduplication key atomically when accepting the work.
v1 has no signed delivery timestamp or event ID, so it cannot provide the same replay and deduplication checks. Make processing idempotent and plan a tested migration to a supported versioned payload.
Verification and migration
A verification request has type webhook.verify, a challenge UUID, workspace, and an ISO timestamp. Verify its signature with the endpoint's configured version and return 2xx; echoing the challenge is not required. Use the verification operation available for your endpoint, then send test tool events, including a failure sample when supported.
Before switching payloadVersion, deploy the new parser and signature handling together. Confirm statusCode:null and null identity fields are handled, test duplicate deliveries, and preserve the old receiver until the new version succeeds.
Retries and delivery failures
2xx | Accepted; no retry. |
429, 5xx, or a network failure | Up to three attempts, with delays of 0, 500, and 2000 milliseconds before the attempts. |
Other 4xx | Rejected without automatic retry. |
Timeout | Each attempt has a five-second budget. |
Private or reserved destination; redirect | Private/reserved network destinations are blocked, and redirects are not followed. |
Do not assume exactly-once delivery or event ordering. For signature failures, check the configured version, current secret, raw-body capture, and server clock. For connection failures, check public DNS, TLS, firewall rules, and the receiver path. For 429 or 5xx, check capacity and accept events quickly into a queue.
Classic Slack incoming webhook URLs receive a Slack message representation instead of the raw event; Slack Workflow Builder URLs receive the event JSON. For other systems, use a receiver that understands Evlio's payload or transforms it into the destination's required format.
Other webhook sources
Workspace webhooks deliver only mcp.tool_call events, plus the webhook.verify challenge. MCP Monitor alerts are configured per monitor and send their own signed events (incidents and tool-inventory changes). See MCP Monitor.
On this page
- MCP webhooks