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 Elva 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. Elva 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
Preview: version pinning, custom delivery headers, verification challenges, and the 2026-10-01 payload require compatible Elva service support. Use the saved payloadVersion for your endpoint; the version label is not a promised release date. Existing endpoints can remain on v1.
v1 | 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 — Preview | 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. |
On a compatible service, webhook configuration exposes payloadVersion, events, and optional headers. Pin payloadVersion to the receiver implementation and subscribe to mcp.tool_call. Custom header values can supply receiver authentication; they cannot replace Content-Type or Elva 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 versioned test event — Preview
{
"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: Elva-Webhooks/1.0. With a secret configured, X-Elva-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-Elva-Timestamp in Unix seconds, X-Elva-Event-Id, and X-Elva-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_elva(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-elva-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-elva-event-version") != version:
return False
stamp = headers.get("x-elva-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-Elva-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 — Preview
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 Elva's payload or transforms it into the destination's required format.
On this page
- MCP webhooks