What you will be able to do
- Decide when an outbound webhook fits better than a built-in job notification
- Create a webhook subscription with the right events, jobs and endpoint
- Choose between job.run.completed and job.run.errored based on what the receiving system needs
- Verify that an incoming webhook was really sent by dbt using its secret token
- Explain dbt's retry, timeout and auto-archive behaviour and reactivate an archived webhook
Key concept
Outbound webhook — An HTTP callback that dbt pushes to a URL you own whenever a subscribed job event happens. Your system reacts as soon as the event arrives, so it never has to poll dbt for run status.
1.Why push events instead of polling for run status
A webhook lets dbt tell another system that a job did something, at the moment it happens. You don't need a script that asks the API every minute whether the nightly run has finished. Instead, dbt makes an HTTP POST with a JSON body to an endpoint you register. Because the server pushes the call instead of the client pulling it, webhooks are also called push APIs or reverse APIs.
The receiving side can be anything that accepts an HTTP request: a serverless function, a Zapier trigger, or your own small app. That is where the value comes from. Common uses are posting a Slack or Microsoft Teams message, opening a PagerDuty incident when a job fails, writing one Datadog log entry per node that ran, and refreshing a Mode report as soon as fresh data lands. Each of these reacts to a job event, and dbt itself does not provide that reaction.
Built-in [job notifications](lesson:6.1) cover a narrower need: a status alert sent to a person or a channel. If all you want in Teams is "job succeeded" or "job failed", sending a notification to the channel's email address does the job with no code. Choose a webhook when the receiver has to process the event: read the run ID, call back into the dbt Admin API for logs, filter on status, or start work in a third-party tool.
Checkpoint 1 of 8· Check yourself
A team wants a Microsoft Teams message that lists each failed step of a run along with its error log lines. Which approach fits?
Job notifications send status alerts only. Pulling step-level error details means processing the event payload and calling back into the API, and that is what a webhook receiver is for.
“and don’t need to process webhook payloads, you can use Job notifications instead”Source: docs.getdbt.com
2.Creating a webhook subscription
A webhook subscription lives at the account level and is set up under Account settings. Before you start, check four prerequisites:
- The account is on the Starter or Enterprise-tier plan. - On Enterprise, you (or the API service token) hold the Account Admin, Admin, or Developer permission set. On Starter, UI access requires a Developer license. - The account runs on a multi-tenant or AWS single-tenant deployment. - The destination system accepts Authorization headers.
You can create a webhook in the dbt UI or through the dbt API, and both use the same fields. Events lets you subscribe to more than one event. Jobs decides which jobs fire the webhook, and its default catches many people out: if you leave Jobs empty, the webhook fires for every job in the account. For a Mode report refresh, that means every CI and development job would refresh the dashboard too. List only the jobs whose completion should trigger the downstream action.
| Field | What it controls |
|---|---|
| Webhook name | A name for the outbound webhook |
| Description | A free-text description of the webhook |
| Events | One or more events that trigger the webhook |
| Jobs | Which jobs the webhook fires for. Left empty, it fires for all jobs in the account |
| Endpoint | The URL of your application where dbt sends the events |
When you click Save, dbt shows a secret token for the webhook. Store it straight away in your receiver's secret store, because it is the key your endpoint uses to prove that a request really came from dbt.
Checkpoint 2 of 8· Put it in order
Put the steps for creating a webhook subscription in the UI in order
- 1.Copy the secret token dbt provides into the receiver's secret store
- 2.Enter the name, description, events, jobs and endpoint URL
- 3.Click Save
- 4.Open Account settings by clicking your account name in the left side panel
- 5.Go to the Webhooks section and click Create webhook
Webhooks are configured from Account settings → Webhooks. dbt issues the secret token only once the webhook has been saved.
“Go to the Webhooks section and click Create webhook.”Source: docs.getdbt.com
3.Choosing between started, completed and errored events
dbt offers three job events, and the important difference is timing, not just meaning. job.run.started fires when a run begins. job.run.completed fires when a run ends, whether it succeeded or failed. job.run.errored fires only when a run errors, so it is a subset of job.run.completed. If you subscribe to both completed and errored, a failed run produces two deliveries, and a Teams channel gets the same failure message twice.
job.run.completed waits until the run's metadata and artifacts have been ingested, meaning dbt has processed that record of the run so its APIs can serve it. Two APIs matter here. The Admin API gives access to things like the logs from a run, and the Discovery API gives model-by-model statuses and other per-node information. Until ingestion finishes, calls to them for that run may come back empty, which is exactly the gap job.run.errored does not wait out.
Receivers that need that detail therefore subscribe to Run completed. Three example receivers show the pattern, each calling back into dbt after the event arrives. A Zapier-based Slack integration extracts run results from the Admin API and posts a summary with error details. A small PagerDuty app checks for failed nodes and creates an alarm through the PagerDuty Events API. A Datadog app writes a log entry per node containing what the Discovery API reports about it. Each branches on runStatus or runStatusCode from the payload. Choose job.run.errored only when speed matters more than detail. If you do, write the receiver to cope with API calls that return nothing for a short while after the event.
| Event | Fires when | Admin/Discovery API data ready? | Best for |
|---|---|---|---|
| job.run.started | A run starts | Not applicable | Tracking that a run has begun |
| job.run.completed | A run finishes, failed or succeeded, after artifacts are ingested | Yes | Integrations that fetch logs or node results, filtering on runStatus |
| job.run.errored | A run errors, sent immediately | Maybe not yet | Fast alerts that need nothing beyond the payload |
Cancelled runs have no webhook event at all. If your on-call channel needs to hear about cancellations, keep the native Slack job notification installed alongside the webhook integration, so it adds to that alert rather than replacing it. The payload below is what a completed run sends. The data object carries the job, run, environment and project identifiers that a receiver needs to call back into dbt.
"eventType": "job.run.completed",
"webhookName": "test",
"data": {
"jobId": "123",
"jobName": "Daily Job (dbt build)",
"runId": "12345",
"environmentId": "1234",
"environmentName": "Production",
"dbtVersion": "1.0.0",
"projectName": "Snowflake Github Demo",
"projectId": "167194",
"runStatus": "Success",
"runStatusCode": 10,
"runStatusMessage": "None",
"runReason": "Kicked off from the UI by test@test.com",
"runStartedAt": "2023-01-31T19:28:07Z",
"runFinishedAt": "2023-01-31T19:29:32Z"
}
}Checkpoint 3 of 8· Check yourself
A webhook is subscribed to both job.run.completed and job.run.errored. A production run fails. How many deliveries does the endpoint receive for that run?
A failed run is both completed and errored, so each subscribed event sends its own delivery, at different times.
“If you subscribe to both, you will receive two notifications when your job encounters an error.”Source: docs.getdbt.com
Checkpoint 4 of 8· Exam question
A platform team wants PagerDuty to open an incident the instant a job run fails in dbt Cloud, and they accept that some run metadata may not yet be available through the Discovery API at that moment. Which webhook trigger event should they subscribe to for the fastest possible failure alert?
Correct answer: A — `job.run.errored` fires the instant dbt Cloud detects a failed run, without waiting for run artifacts to finish ingesting, minimizing alert delay.
- A. This event is designed for immediate error alerting and fires as soon as dbt Cloud detects the failure, before any post-run artifact processing happens. That makes it the correct choice when the priority is speed rather than having enriched run data available right away.
- B. This event only fires once run artifacts have finished ingesting, which is useful for querying the Discovery or Admin API afterward but introduces exactly the delay the team wants to avoid on a failure page.
- C. This event fires at the beginning of a run, before dbt Cloud knows whether the run will succeed or fail, so it cannot be used to detect a failure at all.
- D. There is no such trigger event in dbt Cloud's webhook system; the only available events relate to a run starting, completing, or erroring, not queuing.
4.Verifying that a webhook came from dbt
An endpoint URL is just a public address, so anyone who learns it can POST a fake "run succeeded" to it. Imagine a Mode refresh or a PagerDuty resolve being triggered by a spoofed request. dbt defends against this by signing every delivery. The Authorization header carries an HMAC SHA256 hash of the request body, keyed with the webhook's secret token. Your receiver recomputes that hash from the raw body and compares it with the header.
auth_header = request.headers.get('authorization', None)
app_secret = os.environ['MY_DBT_CLOUD_AUTH_TOKEN'].encode('utf-8')
signature = hmac.new(app_secret, request_body, hashlib.sha256).hexdigest()
return signature == auth_headerHash the raw body exactly as it arrived. If you parse the JSON and re-serialize it before hashing, the bytes change and the signature will never match. Zapier, a no-code automation service that can receive webhooks, offers two trigger types for this: Catch Hook and Catch Raw Hook. Choose Catch Raw Hook, because only the raw variant gives the code step the untouched body and the Authorization header. Catch Hook is acceptable only if you don't intend to validate authenticity, which is not recommended. Because the signature travels in that header, the destination must accept Authorization headers at all. You can confirm this before wiring anything up with a simple curl request:
curl -H 'Authorization: 123' -X POST https://<your-webhook-endpoint>Request inspectors such as RequestBin and Requestly show the headers and body of each test delivery, which helps when a signature check keeps failing. One sequencing detail applies to receivers you host yourself, such as the Python apps on fly.io that dbt's PagerDuty and Datadog guides walk through: store the webhook secret in the app before you click Test Endpoint, the button on the webhook's settings in dbt that sends a test event to your endpoint. Until the app has the key, every test delivery fails the signature check.
Checkpoint 5 of 8· Fill the gap
Which hash function completes dbt's signature check?
auth_header = request.headers.get('authorization', None)
app_secret = os.environ['MY_DBT_CLOUD_AUTH_TOKEN'].encode('utf-8')
signature = hmac.new(app_secret, request_body, hashlib. ? ).hexdigest()
return signature == auth_headerdbt puts a SHA256 hash of the request body, keyed with the secret token, in the Authorization header, so the receiver has to compute the same SHA256 HMAC.
Source: docs.getdbt.comCheckpoint 6 of 8· Exam question
Which of the following are valid dbt Cloud webhook trigger events? (Select all that apply.)(Select 3)
Correct answers: A, B, C — `job.run.started`; `job.run.completed`; `job.run.errored`
- A. This is a valid trigger event that fires when a job run begins executing, before its outcome is known.
- B. This is a valid trigger event that fires once a run finishes and its artifacts have been ingested into the Discovery and Admin APIs.
- C. This is a valid trigger event that fires as soon as dbt Cloud detects a run has failed, ahead of artifact ingestion.
- D. This is not a real webhook trigger event in dbt Cloud; runs entering a queue do not generate a webhook notification on their own.
5.Retries, timeouts and archived webhooks
Delivery is at-least-best-effort, and the receiver has to be built with that in mind. dbt retries each event five times. Each webhook keeps a delivery log for 7 days in its own Recent Deliveries section, which shows at a glance whether each delivery succeeded or failed. That log is the first place to look when an integration goes quiet.
The 10-second timeout catches slow receivers. When the endpoint does its work before responding, it can finish happily and log success while dbt marks the delivery as failed. Both sides then disagree about what happened, and retries may deliver the same event again. Keep the work done before the response short, or make the downstream action safe to repeat. One way is to key the downstream action on the run ID in the payload: a PagerDuty receiver that deduplicates its events per run ID turns a repeated delivery for the same run into the same event rather than a second alarm.
A webhook that keeps failing doesn't fail forever. dbt archives it automatically after 1,000 consecutive failed deliveries or 7 consecutive days of failed deliveries, whichever comes first. On Account settings → Webhooks, a dismissible banner warns that a subscription is archived, and the status column shows an Archived badge instead of an HTTP status. To bring it back:
- In the dbt platform UI, change the webhook's endpoint URL. A URL change reactivates the webhook automatically.
- Through the REST API, send a PUT to Update a webhook with active set to true, or with a new client_url.
- With the Terraform provider, set active = true and update client_url in the webhook resource.
Checkpoint 7 of 8· Match them up
Match each delivery behaviour to its value or effect
Tap a term, then the definition that fits it.
These are dbt's fixed delivery rules. Retries and the timeout govern each event, while the archive thresholds govern the subscription as a whole.
“dbt automatically archives a webhook after 1,000 consecutive failed deliveries or 7 consecutive days of failed deliveries, whichever comes first.”Source: docs.getdbt.com
Checkpoint 8 of 8· Exam question
How does a receiving endpoint verify that an incoming webhook request actually originated from dbt Cloud and was not tampered with in transit?
Correct answer: A — It recomputes an HMAC-SHA256 hash of the request body using the webhook secret as key, then compares it to the Authorization header value dbt Cloud sent.
- A. dbt Cloud signs every webhook request by hashing the raw body with the webhook's secret token and placing the result in the Authorization header, so recomputing and comparing that hash on the receiving end is the documented way to confirm authenticity and integrity.
- B. dbt Cloud does not publish a fixed IP allowlist for webhook verification, so building authentication around source IP filtering is not the supported mechanism and would be fragile if delivery infrastructure changes.
- C. The `eventId` field helps a receiver deduplicate repeated deliveries of the same event, but its presence or novelty says nothing about whether the request body was tampered with, so it cannot serve as a tamper check on its own.
- D. dbt Cloud webhook delivery does not involve issuing or requiring client-side TLS certificates; authenticity is established through the HMAC signature in the Authorization header instead.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Leaving the Jobs field empty means the webhook fires for no jobs until you add some.Why is that wrong?
An empty Jobs field subscribes the webhook to every job in the account, CI and development jobs included.
Covered in Creating a webhook subscription
2.Subscribing to both Run completed and Run errored gives the most reliable failure alerting.Why is that wrong?
Errored is a subset of completed, so a failed run sends two deliveries and the channel receives duplicate messages. Pick one of the two.
Covered in Choosing between started, completed and errored events
3.A webhook-based Slack integration can fully replace the native Slack notification, including alerts for cancelled runs.Why is that wrong?
There is no webhook event for a cancelled run, so the native notification is still needed to hear about cancellations.
Covered in Choosing between started, completed and errored events
4.If the receiver logs a 200 response, dbt also records the delivery as successful.Why is that wrong?
dbt gives up after 10 seconds. A receiver that responds later logs a success while dbt counts the delivery as a failure.
Covered in Retries, timeouts and archived webhooks
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://docs.getdbt.com/docs/deploy/webhooksOfficial docs
“Without it, you would need to make API calls repeatedly to check if there are any updates that you need to account for (polling).”
↩︎ Why push events instead of polling for run status“You can send a Slack notification, a Microsoft Teams notification, open a PagerDuty incident when a dbt job fails.”
↩︎ Why push events instead of polling for run status“You, or the API service token, must have the Account Admin, Admin, or Developer permission set.”
↩︎ Creating a webhook subscription“You have a multi-tenant or an AWS single-tenant deployment model in dbt.”
↩︎ Creating a webhook subscription“Or, you can leave this field empty for the webhook to trigger on all jobs in your account.”
↩︎ Creating a webhook subscription“dbt provides a secret token that you can use to check for the authenticity of a webhook.”
↩︎ Creating a webhook subscription“The job.run.errored event is a subset of the job.run.completed events.”
↩︎ Choosing between started, completed and errored events“This event only fires once the job’s metadata and artifacts have been ingested and are available from the dbt Admin and Discovery APIs.”
↩︎ Choosing between started, completed and errored events“use the job.run.completed event and filter on runStatus or runStatusCode.”
↩︎ Choosing between started, completed and errored events“Discovery API (accessing model-by-model statuses)”
↩︎ Choosing between started, completed and errored events“Official webhooks will include the Authorization header that contains a SHA256 hash of the request body and uses the secret token as a key.”
↩︎ Verifying that a webhook came from dbt“Note that the destination system must support Authorization headers for the webhook to work correctly.”
↩︎ Verifying that a webhook came from dbt“dbt retries sending each event five times. dbt keeps a log of each webhook delivery for 7 days.”
↩︎ Retries, timeouts and archived webhooks“if the endpoint doesn't respond within 10 seconds, the webhook processor will time out.”
↩︎ Retries, timeouts and archived webhooks“Update the webhook's endpoint URL. dbt automatically reactivates the webhook when the URL changes.”
↩︎ Retries, timeouts and archived webhooks“Send a PUT request to Update a webhook and set active to true, or update client_url to a new endpoint URL.”
↩︎ Retries, timeouts and archived webhooks“dbt sends a JSON payload to your application's endpoint URL when your webhook is triggered.”
↩︎ Key concept“Or, you can leave this field empty for the webhook to trigger on all jobs in your account.”
↩︎ Exam trap 1“if the endpoint doesn't respond within 10 seconds, the webhook processor will time out.”
↩︎ Exam trap 4“Go to the Webhooks section and click Create webhook.”
↩︎ Checkpoint“This event fires immediately so the job’s metadata and artifacts might not have been ingested.”
↩︎ Prediction“If you subscribe to both, you will receive two notifications when your job encounters an error.”
↩︎ Checkpoint“the dbt webhooks system will interpret this as a failure”
↩︎ Prediction“dbt automatically archives a webhook after 1,000 consecutive failed deliveries or 7 consecutive days of failed deliveries, whichever comes first.”
↩︎ Checkpoint - 2.https://docs.getdbt.com/guides/zapier-ms-teamsOfficial docs
“and don’t need to process webhook payloads, you can use Job notifications instead”
↩︎ Why push events instead of polling for run status“Choose either Run completed or Run errored, but not both, or you'll get double messages when a run fails.”
↩︎ Exam trap 2 - 3.https://docs.getdbt.com/guides/serverless-datadogOfficial docs
“when a dbt job completes it will create a log entry for each node that was run”
↩︎ Why push events instead of polling for run status - 4.
“you need to change the Jobs list to only contain any jobs whose completion should trigger a report refresh.”
↩︎ Creating a webhook subscription - 5.https://docs.getdbt.com/guides/serverless-pagerdutyOfficial docs
“create a PagerDuty alarm based on those nodes by calling the PagerDuty Events API.”
↩︎ Choosing between started, completed and errored events“Do not test the endpoint; it won't work until you have stored the auth keys (next step)”
↩︎ Verifying that a webhook came from dbt“Events are deduplicated per run ID.”
↩︎ Retries, timeouts and archived webhooks - 6.https://docs.getdbt.com/guides/zapier-slackOfficial docs
“Because there is not a webhook for Run Cancelled, you may want to keep the standard Slack integration installed to receive those notifications.”
↩︎ Choosing between started, completed and errored events“If you don't intend to validate the authenticity of your webhook (not recommended!) then you can choose Catch Hook instead.”
↩︎ Verifying that a webhook came from dbt“Because there is not a webhook for Run Cancelled, you may want to keep the standard Slack integration installed to receive those notifications.”
↩︎ Exam trap 3