Skip to content

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.

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.

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.

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.

This is the most common HMAC setup — pointing GitHub’s pull_request (or any other event) webhook at a workflow.

  1. In the dashboard: open the workflow’s webhook trigger, set Auth to HMAC, save.

  2. Reveal the secret for that trigger and copy it.

  3. 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/json
    Secret the secret from step 2
    Which events the events you want — typically “Let me select individual events” → just “Pull requests”
    Active
  4. Save. GitHub sends an immediate ping event to confirm the URL is reachable. After that, every matching event fires a signed delivery.

Three quick checks, in order:

Terminal window
# 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 body

If 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).