CertSafari
    DBT Architect· Lessons

    Domain 6 · Lesson 34/39

    dbt webhooks for event-driven job integrations

    Using Webhooks for event-driven integrations with other systems

    16 min read
    2.56% of exam
    6 sources
    Published 13 Oct 2026
    Docs as of 12 Oct 2026

    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?

    Sources123

    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.

    Fields on the Create webhook form
    FieldWhat it controls
    Webhook nameA name for the outbound webhook
    DescriptionA free-text description of the webhook
    EventsOne or more events that trigger the webhook
    JobsWhich jobs the webhook fires for. Left empty, it fires for all jobs in the account
    EndpointThe 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. 1.Copy the secret token dbt provides into the receiver's secret store
    2. 2.Enter the name, description, events, jobs and endpoint URL
    3. 3.Click Save
    4. 4.Open Account settings by clicking your account name in the left side panel
    5. 5.Go to the Webhooks section and click Create webhook

    Sources14

    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.

    The three webhook events compared
    EventFires whenAdmin/Discovery API data ready?Best for
    job.run.startedA run startsNot applicableTracking that a run has begun
    job.run.completedA run finishes, failed or succeeded, after artifacts are ingestedYesIntegrations that fetch logs or node results, filtering on runStatus
    job.run.erroredA run errors, sent immediatelyMaybe not yetFast 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.

    Body of a job.run.completed payload for a successful runjson
    "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?

    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?

    Sources156

    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.

    Checking a webhook signature in Pythonpython
    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_header

    Hash 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:

    Testing whether an endpoint accepts an Authorization headerbash
    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_header

    Checkpoint 6 of 8· Exam question

    Which of the following are valid dbt Cloud webhook trigger events? (Select all that apply.)(Select 3)

    Sources165

    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.

    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?

    Sources15

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 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. 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. 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. 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. 1.
      “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. 2.
      “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. 3.
      “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. 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. 5.
      “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. 6.
      “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

    Spotted a mistake, or was something unclear? Tell us.