Webhooks
Webhooks let DriftWise receive events from your VCS / CI system and post analysis results back as merge-request comments. For endpoint shapes on webhook config management, see the webhooks tag of the API reference.
This page covers inbound webhooks — an external system sending events to DriftWise. For outbound notifications (DriftWise alerting you when scans complete), see Scheduled Scans.
For synchronous plan analysis from your CI pipeline
(GitHub Actions, GitLab CI script: steps, Atlantis run: steps),
use POST /api/v2/orgs/:id/analyze with an API key — see
GitHub Actions,
GitLab CI, and
Atlantis. The /webhooks/ endpoint
described below is a separate integration path for
event-driven flows: Atlantis plan events and GitLab
merge-request / comment events.
Supported sources
Two webhook sources are registered at the /webhooks/ endpoint:
| Source | URL segment | Auth model | Signature header |
|---|---|---|---|
| Atlantis | atlantis | HMAC-SHA-256 over body | X-Atlantis-Signature (or X-Hub-Signature-256) |
| GitLab native webhook | gitlab | Constant shared secret | X-Gitlab-Token |
Any other :provider segment returns 404 unknown provider.
The two auth models differ:
- Atlantis signs each request with HMAC-SHA-256 of the raw body
using the webhook secret. Either header carries an
sha256=<hex>value;X-Atlantis-Signaturetakes priority. - GitLab sends the shared secret verbatim in
X-Gitlab-Token— exactly the behavior of GitLab's native "Secret token" field in Settings → Webhooks. DriftWise does a constant-time compare against the stored secret. This is not an HMAC; do not hash the secret before sending it.
How it works
- The source system POSTs an event to DriftWise.
- DriftWise validates the signature / token, parses the event, and
returns
202 Acceptedimmediately. - For Atlantis
planevents, DriftWise runs analysis and posts a comment back to the PR via the GitHub App. - For GitLab merge-request events, DriftWise observes the MR and
comments via the stored API token when
/driftwisecommands arrive.
Comment-back requires an installed GitHub App (Atlantis path) or a stored GitLab API token (GitLab path). See each source's setup guide.
Creating a webhook config
Create webhook configs through the dashboard Settings → Webhooks
page or via the API. On creation you'll get a raw_secret field in
the response — save this immediately. It's shown once and
required to sign inbound requests from the source.
Creation is gated to owner/admin because a viewer setting up a webhook with a known-bad secret could effectively DoS real integrations that share the org's webhook gateway.
GitLab webhook configs additionally accept an API token that DriftWise live-validates against the target GitLab instance before saving. Expired tokens, wrong-scope tokens, and tokens for unreachable instances fail at save time with a specific 400 message — no token ever lands in the DB as "active but actually broken." Tokens without an expiration date are rejected: create a Personal or Group Access Token with an explicit expiry.
Webhook URL
Your source posts events to:
https://app.driftwise.ai/webhooks/<source>/<org_id>
For example:
https://app.driftwise.ai/webhooks/atlantis/1478fe16-d292-4f0f-95fa-d249dda3d7ab
This is not a /api/v2/ endpoint — it lives outside the v2 auth
middleware by design, because the source authenticates with the
webhook secret, not an API key or OIDC token.
Source setup
Atlantis
Add a webhook in your Atlantis server config pointing to DriftWise:
webhooks:
- event: plan
workspace-regex: ".*"
url: "https://app.driftwise.ai/webhooks/atlantis/<org_id>"
secret: "<raw_secret from webhook config>"
Atlantis computes an HMAC-SHA-256 of the body with the shared secret
and sends it in X-Atlantis-Signature: sha256=<hex>. DriftWise
validates this against every enabled atlantis config for the org
and accepts if any secret matches.
See Atlantis Integration for the full workflow setup.
GitLab native webhook
In your GitLab project, open Settings → Webhooks. Add a new webhook:
- URL:
https://app.driftwise.ai/webhooks/gitlab/<org_id> - Secret token: paste the
raw_secretreturned when you created the webhook config in DriftWise (value starts withwhsec_) - Trigger: enable Merge request events and Comments
- Save
GitLab sends the secret token verbatim in X-Gitlab-Token on each
delivery. DriftWise compares it (constant-time) against every
enabled gitlab config for the org.
See GitLab CI Integration for the full setup including the optional API token for comment-back.
Updating a webhook config
The PATCH endpoint accepts {enabled, api_token} — at least one
field is required (empty body returns 400, not a no-op). Token
rotations re-validate against the target GitLab instance before
persisting, same as on create. Failures return a 400 with an
actionable message that never contains the raw token itself.
api_token is only supported on gitlab configs.
Security
- Secrets are encrypted at rest with AES-256-GCM. Only a 14-char
identifier prefix (
whsec_+ 8 hex chars) is stored in plaintext for lookup / display. - Signature validation on every inbound webhook. Atlantis:
HMAC-SHA-256 over the raw body. GitLab: constant-time compare of
X-Gitlab-Tokenagainst the stored secret. Both fail closed — missing headers, DB errors, decrypt failures, or zero enabled configs all reject with 401. - Body size cap of 5 MB at the gateway. Larger payloads are rejected with 413 before any validation work runs.
- Replay protection — duplicate payloads (same body hash) are deduplicated to prevent double-processing.
- Repo cross-check — when a matching webhook config has a
repo_pathset, the event body's repo must match it or the request is rejected with 403. Prevents a tenant member with a valid token from forging events from a different repo in the same org. - Async processing — webhooks return 202 immediately, so the source doesn't block on analysis.
provider_base_urlis SSRF-validated (must be public HTTPS) before save, for GitLab configs pointing at self-hosted instances.
Endpoint reference
Webhook config CRUD is under the webhooks tag of the API reference.