EVOKORE-MCP Webhook Envelope v1
This document is the authoritative specification for the webhook payload format emitted by WebhookManager. The envelope is frozen: additive extensions are allowed, but field removal or type changes require a new v2 envelope.
What this covers
- The frozen v1 envelope shape and required fields
- HTTP request headers and how the timestamp/nonce fit into signing
- Signature verification with Node.js sample code
- All 10 event types and per-event payload shapes
- Retry policy, redaction rules, and
mcp.config.jsonreference - Versioning and changelog
Version: 1 (stable)
Status: Frozen — additive extensions allowed; field removal or type changes require v2
Overview
EVOKORE emits signed webhook events to configured HTTP endpoints when
EVOKORE_WEBHOOKS_ENABLED=true. Events are delivered fire-and-forget with up to 3
retries and exponential backoff. Each delivery is a single HTTP POST with a JSON body
conforming to the Envelope v1 schema.
Envelope Schema
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-01-01T20:07:27.968Z",
"event": "tool_call",
"data": { ... }
}
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | UUIDv4. Unique per delivery attempt. Repeated on retry. |
timestamp | string | Yes | ISO 8601 UTC. Time of event emission (not delivery). |
event | string | Yes | Event type (see Event Types). |
data | object | Yes | Event-specific payload (see Event Payloads). |
Versioning note: The absence of an envelopeVersion field means v1. Future v2+
envelopes will include "envelopeVersion": 2.
HTTP Request
POST {endpoint_url}
Content-Type: application/json
X-EVOKORE-Signature: <hmac-sha256-hex>
X-EVOKORE-Timestamp: <unix-epoch-seconds>
X-EVOKORE-Nonce: <uuid-v4>
X-EVOKORE-Signatureis always present. See Signature Verification.X-EVOKORE-Timestampis present when the webhook config includes asecret. Its value matches the timestamp factor used in the HMAC.X-EVOKORE-Noncecarries the envelopeid(UUIDv4). Subscribers may use it for idempotency checks alongside the signed timestamp.
Signature Verification
When a webhook endpoint is configured with a secret, the payload is signed with
HMAC-SHA256:
signature = HMAC-SHA256(secret, "${timestamp_sec}.${json_body}")
Where:
timestamp_secis the Unix epoch seconds value sent inX-EVOKORE-Timestampjson_bodyis the raw POST body string (before any parsing)
Subscriber verification steps:
- Extract
X-EVOKORE-Timestampheader. Reject if absent or unparseable. - Check
|Math.floor(Date.now() / 1000) - timestamp_sec| <= 300(5-minute replay window). Reject if stale. - Compute
expected = HMAC-SHA256(your_secret, "${timestamp_sec}.${body}"). - Compare
X-EVOKORE-Signaturewithexpectedusing timing-safe equality. - Accept only if signatures match.
Node.js example:
const crypto = require('crypto');
function verifyEvokoreWebhook(body, headers, secret) {
const sigHeader = headers['x-evokore-signature'];
const tsHeader = headers['x-evokore-timestamp'];
if (!sigHeader || !tsHeader) return false;
const ts = parseInt(tsHeader, 10);
if (isNaN(ts) || Math.abs(Math.floor(Date.now() / 1000) - ts) > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.${body}`, 'utf8')
.digest('hex');
const sigBuf = Buffer.from(sigHeader, 'hex');
const expectedBuf = Buffer.from(expected, 'hex');
if (sigBuf.length !== expectedBuf.length) return false;
return crypto.timingSafeEqual(sigBuf, expectedBuf);
}
Data Redaction
Before signing and delivery, data fields whose keys match any of the following
patterns are replaced with "[REDACTED]":
token,secret,password,key,credential
Redaction is case-insensitive and applies recursively to nested objects.
Note: Argument redaction for sensitive keys is planned but not yet implemented in
WebhookManager.ts. Until implemented, raw tool arguments are forwarded in the payload.
Event Types
EVOKORE emits 10 event types:
| Event | Trigger |
|---|---|
tool_call | A tool was invoked and returned a result |
tool_error | A tool invocation returned an error |
session_start | A new session was created (HTTP transport) |
session_end | A session ended (TTL expiry or explicit close) |
session_resumed | An existing session was reattached via HTTP |
approval_requested | A restricted tool requested HITL approval |
approval_granted | An approval token was submitted |
plugin_loaded | A plugin was loaded successfully |
plugin_unloaded | A plugin was unloaded |
plugin_load_error | A plugin failed to load |
Subscribe to specific event types in mcp.config.json:
{
"webhooks": [
{
"url": "https://your-endpoint.example.com/evokore",
"events": ["tool_call", "tool_error", "session_start"],
"secret": "${WEBHOOK_SECRET}"
}
]
}
Event Payloads
tool_call
{
"toolName": "resolve_workflow",
"sessionId": "abc123",
"durationMs": 42,
"result": "truncated-result-string"
}
tool_error
{
"toolName": "execute_skill",
"sessionId": "abc123",
"error": "Timeout after 30s"
}
session_start
{
"sessionId": "abc123",
"remoteAddress": "127.0.0.1"
}
session_end
{
"sessionId": "abc123",
"reason": "ttl_expired"
}
Reason values: ttl_expired, explicit_close, connection_lost.
session_resumed
{
"sessionId": "abc123",
"remoteAddress": "127.0.0.1"
}
approval_requested
{
"toolName": "execute_skill",
"sessionId": "abc123",
"approvalToken": "[REDACTED]"
}
approval_granted
{
"toolName": "execute_skill",
"sessionId": "abc123"
}
plugin_loaded
{
"pluginId": "my-plugin",
"toolCount": 3
}
plugin_unloaded
{
"pluginId": "my-plugin"
}
plugin_load_error
{
"pluginId": "my-plugin",
"error": "Cannot find module './my-plugin'"
}
Retry Policy
Failed deliveries (non-2xx response, connection timeout, network error) are retried:
| Attempt | Delay |
|---|---|
| 1st retry | 500 ms |
| 2nd retry | 1,000 ms |
| 3rd retry | 2,000 ms |
After 3 failures the delivery is abandoned and logged to stderr. Delivery timeout per attempt: 10 seconds.
The id field is identical across all attempts for the same event, allowing subscribers
to implement idempotency checks.
mcp.config.json Reference
{
"webhooks": [
{
"url": "https://example.com/hooks/evokore",
"events": ["tool_call", "tool_error", "session_start", "session_end"],
"secret": "${WEBHOOK_SECRET}"
}
]
}
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | Yes | Must be https:// or http:// (no other schemes) |
events | string[] | Yes | Subset of the 10 defined event types |
secret | string | No | Enables HMAC signing. Supports ${ENV_VAR} interpolation. |
EVOKORE_WEBHOOKS_ENABLED=true must be set; otherwise the webhook subsystem is
disabled regardless of config.
Changelog
| Version | Change |
|---|---|
| v1 | Initial frozen spec — id, timestamp, event, data envelope; 10 event types; HMAC-SHA256 + replay protection |
See also
- Webhook Guide - configuration, security, and integration samples
- Plugin Authoring - emitting custom events from plugins
- OAuth Setup - authenticating receivers
- HTTP Deployment - HTTP transport that emits session lifecycle events
Last verified: 2026-05-20