Webhook secrets
Every workflow with a webhook trigger has its own per-workflow webhook secret. The secret is mandatory — a webhook with no secret configured returns 401 and never starts the workflow. Secrets are generated when the trigger is created, stored KMS-encrypted at rest, and surfaced through the Triggers page in the dashboard.
Auth modes
Section titled “Auth modes”A workflow’s webhook trigger has an auth mode that controls how the caller proves it knows the secret. The default mode works for any HTTP client; the HMAC mode is for systems like GitHub that sign requests.
| Mode | How the caller authenticates | When to use |
|---|---|---|
secret (default) |
Authorization: Bearer <secret> header or ?token=<secret> query param |
Internal services, manual curl, any client that can set a header |
hmac |
X-Hub-Signature-256: sha256=<HMAC-SHA256(body, secret)> header — POST only |
GitHub webhooks (and any GitHub-style signed delivery) |
HMAC mode verifies the signature against the raw request body, so a tampered payload fails verification even with a valid header. Wrong secret, missing signature, or non-POST all return 401.
Reveal vs rotate
Section titled “Reveal vs rotate”Because the secret is stored KMS-encrypted, the dashboard offers two distinct actions per webhook row in AI Workflows → Triggers:
| Action | What it does | When to use |
|---|---|---|
| Reveal 👁 | Decrypts and shows the current secret. Does not change it. Safe to click anytime; the value stays valid in every existing integration. | You need to copy the secret into a new integration, or you’ve lost track of where you stored it. |
| Rotate 🔄 | Generates a new secret and invalidates the previous one. Always behind a confirmation prompt that spells out the impact. | The secret has leaked, or you’re cycling secrets on a schedule. After rotating, update every integration that uses the secret — anything still presenting the old one will start failing. |
Customer-facing URL
Section titled “Customer-facing URL”The URL surfaced on the Triggers page is a Portal URL (the dashboard host), not the backend AI API. Webhooks must always go through Portal — it verifies routing, injects organisation context, and forwards the raw body and signature header to the workflow engine.
Format:
https://dash.quantgov.cloud/webhooks/ai/workflows/<workflowId>External services (GitHub, Stripe, your own scripts) only ever see Portal URLs. The backend AI API is never exposed directly.
Configure a GitHub webhook (HMAC)
Section titled “Configure a GitHub webhook (HMAC)”This is the most common HMAC setup — pointing GitHub’s pull_request (or any other event) webhook at a workflow.
-
In the dashboard: open the workflow’s webhook trigger, set Auth to HMAC, save.
-
Reveal the secret for that trigger and copy it.
-
In GitHub: on the target repository, go to Settings → Webhooks → Add webhook and enter:
Field Value Payload URL the Portal webhook URL from the Triggers page Content type application/jsonSecret the secret from step 2 Which events the events you want — typically “Let me select individual events” → just “Pull requests” Active ✓ -
Save. GitHub sends an immediate
pingevent to confirm the URL is reachable. After that, every matching event fires a signed delivery.
Verifying it works
Section titled “Verifying it works”Three quick checks, in order:
# 1. The Portal URL is reachable and CSRF-exempt (no secret needed — expect 404 because the workflow id is fake)curl -sS -X POST https://dash.quantgov.cloud/webhooks/ai/workflows/nope \ -H 'Content-Type: application/json' -d '{}'# → 404 {"error":"Workflow webhook not configured"}
# 2. A wrong-secret HMAC delivery is rejected at the backend (proves the chain — Portal forward, org injection, HMAC verify)BODY='{"smoke":true}'SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac 'WRONG' | awk '{print $NF}')"curl -sS -X POST <your-portal-webhook-url> \ -H 'Content-Type: application/json' -H "X-Hub-Signature-256: $SIG" --data-binary "$BODY"# → 401 {"error":"Unauthorized"}
# 3. A valid signature returns 202 and creates a workflow execution.SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac '<your-real-secret>' | awk '{print $NF}')"# → 202 with executionId in the bodyIf GitHub later shows red ❌ deliveries on the webhook settings page, click any delivery → Response tab to see the exact body the workflow returned (typically a clear 401 reason or 404 if the workflow was deleted).
See also
Section titled “See also”- Workflow triggers — the trigger node shape, including scheduled and webhook modes
- Supply-chain agent — the canonical HMAC consumer (GitHub
pull_request→ review)
