Comis-side webhooks vs. channel webhooks. This page describes the daemon’s own HTTP endpoints under
/hooks/*. Some chat platforms (Telegram, Discord, Slack) also expose their own webhook callbacks. The Comis adapters for those platforms typically connect via long-poll or socket APIs by default; see Channels if you specifically need to expose your bot to a platform’s webhook callback.Configuration
Top-Level Webhooks Config
Configure the webhook subsystem under thewebhooks key in your config file.
Source:
WebhooksConfigSchema in packages/core/src/config/schema-webhooks.tsWebhook Mapping Config
Each entry inwebhooks.mappings[] defines a routing rule.
Source:
WebhookMappingConfigSchema in packages/core/src/config/schema-webhooks.tsMatch Conditions
Thematch object controls which incoming requests hit this mapping.
If both
path and source are provided, both must match (AND logic). If
neither is provided, the mapping matches all requests (catch-all).
Source:
WebhookMappingMatchSchema in packages/core/src/config/schema-webhooks.tsStrict Webhook Endpoint
Route
POST /hooks/webhook
The strict endpoint requires HMAC signature verification and validates the
request body against a fixed schema. Use this when you control the sending
service and can format payloads to the expected structure.
Request
The request body must conform toWebhookPayloadSchema:
POST /hooks/webhook
Responses
Source:
createWebhookEndpoint() in packages/gateway/src/webhook/webhook-endpoint.tsMapped Webhook Endpoint
Route
POST /hooks/:path
The mapped endpoint accepts any JSON payload and routes it to the first matching
webhook mapping’s action handler. HMAC verification is optional (applied only
when webhooks.token is configured).
Path Routing
Incoming request paths are matched against thematch.path field of each
mapping in order. The first matching mapping wins.
Normalization: Both the request path and the mapping path are normalized by
stripping leading/trailing slashes and converting to lowercase.
- If a mapping has no
matchconditions, it matches all requests (catch-all) - If
match.pathis set, the normalized request path must equal it - If
match.sourceis set, thesourcefield from the payload must equal it - If both are set, both must match (AND logic)
- First match wins — mappings are evaluated in array order
Actions
"agent" (default): Renders the messageTemplate and sessionKey
templates using the template engine, then invokes the agent
with the rendered message and session key.
"wake": Triggers a daemon heartbeat. The wakeMode controls timing:
"now"(default): Fire the heartbeat immediately"next-heartbeat": Wait for the next scheduled heartbeat cycle
Responses
Source:
createMappedWebhookEndpoint() in packages/gateway/src/webhook/webhook-endpoint.tsHMAC Verification
Webhook signatures are verified using HMAC with constant-time comparison to prevent timing attacks.Configuration
The HMAC middleware accepts the following configuration:Source:
HmacMiddlewareConfig in packages/gateway/src/webhook/hmac-verifier.tsSignature Computation
The sender computes the signature as a hex-encoded HMAC of the raw request body:Signature generation (Python example)
Signature generation (bash)
Verification Process
- Read the signature from the configured header (default:
x-webhook-signature) - If no signature header is present, return
401 - Compute the expected HMAC of the raw request body using the shared secret
- Compare using
crypto.timingSafeEqual(constant-time to prevent timing attacks) - If lengths differ, reject immediately (lengths cannot match for valid signatures)
- If signatures do not match, return
401
Timestamp Freshness
If a timestamp header is present (default:x-webhook-timestamp):
- Parse the header value as a Unix timestamp (seconds)
- Compare against the current time
- If the absolute difference exceeds
maxAgeSec(default: 300 seconds), return401
requireTimestamp is true, requests without a timestamp header are rejected
with 401. When false (default), missing timestamps are allowed — the HMAC
signature alone provides tamper protection.
Source:
verifyHmacSignature() and createHmacMiddleware() in packages/gateway/src/webhook/hmac-verifier.tsTemplate Engine
Mapped webhook endpoints use a template engine to transform incoming payloads into agent messages and session keys. Templates use{{expression}} syntax.
Expression Types
Expressions without a recognized prefix (
payload., headers., query.) are
resolved against the payload object. This means {{repository.full_name}} is
equivalent to {{payload.repository.full_name}}.
Context Object
The template engine receives a context object with the following properties:Unresolved Expressions
Expressions that cannot be resolved (missing fields, null values) are replaced with an empty string. No error is thrown for unresolved expressions.Source:
resolveTemplateExpr() and renderTemplate() in packages/gateway/src/webhook/webhook-mapping.tsPresets
Comis includes built-in webhook mapping presets for common services. Enable presets in your config:Gmail Preset
Receives Gmail push notifications and routes them as agent messages. The session
key uses the first message ID for deduplication.
GitHub Preset
Receives GitHub webhook events and routes them as agent messages. The session
key uses the delivery ID from the
x-github-delivery header for deduplication.
Source:
GMAIL_PRESET and GITHUB_PRESET in packages/gateway/src/webhook/webhook-presets.tsExample Configuration
Full webhook config example
End-to-end: a GitHub webhook
A complete walkthrough that takes a real GitHubpush event and turns it into an agent message.
1
Enable the webhook subsystem
Add the Reload config (
github preset and enable HMAC verification.comis config patch ... or restart) — the daemon now mounts POST /hooks/github.2
Generate the shared secret
Use a 32+ character random string. Store it both in Comis (via
comis secrets set WEBHOOK_SHARED_SECRET) and in GitHub’s webhook UI as the Secret field.3
Configure the GitHub webhook
Repository → Settings → Webhooks → Add webhook:
- Payload URL:
https://your-host.example.com/hooks/github - Content type:
application/json - Secret: the value from the previous step
- SSL verification: enabled
- Select the events you want (e.g. just
push)
4
Verify the signature flow
Trigger a test event from GitHub’s webhook page. Comis will compute HMAC-SHA256 over the raw body using your secret and compare against the
x-hub-signature-256 header. A successful 200 response carries {"received": true, "mapping": "github"}.Tail logs while testing:5
Troubleshoot 401s
If GitHub shows a 401: re-check the secret matches on both sides, that the header name is
x-hub-signature-256 (configurable via headerName), and that no proxy strips signature headers in front of the daemon.hook:github:<delivery-id> so retries are deduplicated automatically by GitHub’s redelivery machinery.
Related
Security Model
HMAC verification and security architecture
HTTP Gateway
Full gateway endpoint reference
Hot Reload
Config reloading and skill discovery
Defense in Depth
User-friendly security overview
