Appearance
Workflow Webhook Triggers
Workflow webhook triggers let external systems fire a Z.E.N. workflow with a signed JSON event.
YAML
yaml
name: transcript-ingest
description: Ingest transcript readiness events
triggers:
webhook:
- event: meeting.transcript.ready
slug: meeting-transcript-ready
secret_env: CADENCE_WEBHOOK_SECRET
idempotency_key: transcript_id
dedupe_window_sec: 86400
filter:
source: cadence-core
nodes:
- id: ingest
bash: echo "$TRIGGER_PAYLOAD"The public endpoint is:
text
POST /webhooks/workflow/:slugIf you run Z.E.N. behind Cloudflare Tunnel, ngrok, or another HTTPS tunnel, point the producer at the public tunnel origin plus that path. Z.E.N. still verifies the raw body signature locally.
Signature
The producer signs the exact raw JSON body with HMAC-SHA256. Do not canonicalize or reformat JSON between signing and sending.
text
x-zen-signature-256: sha256=<hex hmac>secret_env names an environment variable, not a literal secret. Z.E.N. looks it up first in workspace env vars, then in process env.
Request
bash
body='{"event":"meeting.transcript.ready","transcript_id":"tx-123","source":"cadence-core","store_ref":"sqlite|path|url"}'
sig="$(printf '%s' "$body" | openssl dgst -sha256 -hmac "$CADENCE_WEBHOOK_SECRET" -hex | awk '{print $2}')"
curl -X POST "https://zen.example.com/webhooks/workflow/meeting-transcript-ready" \
-H "content-type: application/json" \
-H "x-zen-signature-256: sha256=$sig" \
--data "$body"Responses
| Status | Body | Meaning |
|---|---|---|
202 | { "accepted": true, "run_id": "..." } | Accepted and workflow run created |
200 | { "skipped": "duplicate", ... } | Duplicate retry inside dedupe window |
200 | { "skipped": "event_mismatch" } | Signature valid, event ignored |
200 | { "skipped": "filter_mismatch" } | Signature valid, filter ignored |
400 | { "error": "invalid_idempotency_key" } | Missing or non-scalar key |
401 | { "error": "invalid_signature" } | Missing or invalid HMAC |
404 | { "error": "webhook_trigger_not_found" } | No active trigger for slug |
409 | { "error": "idempotency_key_conflict" } | Same key, different payload |
409 | { "error": "workflow_not_available" } | Trigger row points at removed workflow |
413 | { "error": "payload_too_large" } | Body exceeds 256 KiB |
503 | { "error": "webhook_secret_missing" } | secret_env is not configured |
Webhook runs receive these environment variables:
| Env var | Value |
|---|---|
TRIGGER_TYPE | webhook |
TRIGGER_PAYLOAD | Raw JSON payload string |
WEBHOOK_EVENT | Matched event name |
WEBHOOK_IDEMPOTENCY_KEY | Extracted idempotency key |
WEBHOOK_RECEIVED_AT | ISO timestamp when Z.E.N. accepted payload |