# Guide to Databricks Webhooks: Features and Best Practices

Databricks can notify you when a job starts, succeeds, fails or runs too long, and when a SQL alert changes state. Email is the default, but workspace admins can add webhook notification destinations, which turn those notifications into JSON requests your own systems can act on, for example by opening an incident when a pipeline fails.

This guide covers how Databricks notification destinations work, the job and SQL alert payloads, how to check Databricks' HTTP Basic credentials, and what to plan for given what Databricks doesn't document about delivery.

## What are Databricks webhooks?

A notification destination is a workspace-level setting that a workspace admin creates once and every user can then pick in their job or alert notifications. Destination types include email, Slack, Microsoft Teams, PagerDuty and **Webhook**.

The service that sends the notification decides the payload. Jobs and tasks send a small JSON object with an `event_type`, and Databricks SQL alerts send a larger object describing the alert and its evaluation.

Databricks doesn't sign webhook payloads. A webhook destination authenticates in one of two ways:

* **HTTP Basic**, with a username and password you set on the destination. Databricks sends `Authorization: Basic <base64(username:password)>`.
* **OAuth client credentials** (Beta), where Databricks gets an access token from your identity provider and sends that. This mode can't be combined with Basic authentication.

## Databricks webhook features

| Feature | Details |
| --- | --- |
| Configuration | Workspace admin: **Settings** > **Notifications** > **Manage** > **+Add destination** > **Webhook** |
| Authentication | HTTP Basic, or OAuth client credentials (Beta) |
| Signature | None |
| Event field | `event_type` for jobs, `event` for SQL alerts |
| Requirements | HTTPS with a certificate signed by a trusted CA |
| Source IPs | Workspace control plane and data plane outbound IPs, which can change as often as every 30 days |
| Limits | Up to three system destinations per notification event type for each job or task |
| Testing | **Test** in the destination's three-dot menu |
| Retries, timeout, delivery ID | Not documented |

## Common events

### Jobs and tasks

| `event_type` | Sent when |
| --- | --- |
| `jobs.on_start` | A run starts |
| `jobs.on_success` | A run completes successfully, or in a **Succeeded with failures** state |
| `jobs.on_failure` | A run stops in an unsuccessful state |
| `jobs.on_duration_warning_threshold_exceeded` | A run exceeds its configured duration threshold |

The same events can be set on a job or on individual tasks. A task notification adds `task.task_key` and a `parent_run_id` for the job run:

```json
{
  "event_type": "jobs.on_success",
  "workspace_id": "your_workspace_id",
  "task": {
    "task_key": "task_name"
  },
  "run": {
    "run_id": "run_id_of_task",
    "parent_run_id": "run_id_of_parent_job_run"
  },
  "job": {
    "job_id": "job_id",
    "name": "job_name"
  }
}

```

The payload holds IDs only. Use the Jobs API with `run.run_id` if you need the run's state, duration or error message.

### SQL alerts

When a Databricks SQL alert changes state, the destination receives `"event": "alert_state_change"`, a `url_base` for the workspace, and an `alert` object. Inside it, `alert.alert` is a snapshot of the alert: its name, owner, query text, warehouse, schedule and `evaluations`. Each evaluation's `state` is `TRIGGERED`, `OK` or `ERROR`, and reflects the change that caused the notification. Timestamps such as `last_evaluated_at` are in milliseconds since the Unix epoch, and fields without values are left out. Legacy alerts use a different payload.

Notification destinations also receive Unity Catalog access requests, such as privilege requests from workspace users. Databricks doesn't publish a payload for these.

## Setting up Databricks webhooks

1. As a workspace admin, click your username, select **Settings**, then open the **Notifications** tab under **Workspace admin**.
2. Click **Manage**, then **+Add destination**, and select **Webhook**.
3. Enter the HTTPS URL and a username and password for HTTP Basic authentication. Use a different password for each destination, as Databricks recommends, so you can revoke one without affecting others.
4. Click **Create**, then use **Test** in the destination's three-dot menu to send a sample notification.
5. On a job, open **Job notifications** > **Edit notifications** > **Add notification**, select the destination, and tick **Start**, **Success**, **Failure** or **Duration warning**. For SQL alerts, select the destination in the alert's notification settings.

If your endpoint restricts inbound traffic, allowlist both the control plane IPs and the data plane outbound IPs for your workspace's region. Databricks publishes the data plane ranges in an `ip-ranges.json` file and may update them every 30 days, with new addresses active as soon as 60 days after publication.

## Securing Databricks webhooks

Decode the `Authorization` header and compare the username and password with your stored values in constant time:

```javascript
const crypto = require('crypto');
const express = require('express');

function safeEqual(a, b) {
  const received = Buffer.from(a, 'utf8');
  const expected = Buffer.from(b, 'utf8');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

function verifyDatabricksBasicAuth(authHeader, username, password) {
  if (!authHeader || !username || !password || !authHeader.startsWith('Basic ')) return false;
  const decoded = Buffer.from(authHeader.slice(6), 'base64').toString('utf8');
  const separator = decoded.indexOf(':');
  if (separator === -1) return false;
  const user_ok = safeEqual(decoded.slice(0, separator), username);
  const pass_ok = safeEqual(decoded.slice(separator + 1), password);
  return user_ok && pass_ok;
}

const app = express();

app.post('/webhooks/databricks', express.raw({ type: '*/*' }), (req, res) => {
  const username = process.env.DATABRICKS_WEBHOOK_USERNAME;
  const password = process.env.DATABRICKS_WEBHOOK_PASSWORD;
  if (!username || !password) return res.sendStatus(500); // fail closed

  if (!verifyDatabricksBasicAuth(req.get('authorization'), username, password)) {
    return res.sendStatus(401);
  }

  const notification = JSON.parse(req.body.toString('utf8'));
  // Route on notification.event_type (jobs) or notification.event (SQL alerts), then process async.
  res.sendStatus(200);
});

```

The same check in Python:

```python
import base64
import binascii
import hmac

def verify_databricks_basic_auth(auth_header, username: str, password: str) -> bool:
    if not auth_header or not username or not password or not auth_header.startswith("Basic "):
        return False
    try:
        decoded = base64.b64decode(auth_header[6:]).decode("utf-8")
    except (binascii.Error, UnicodeDecodeError):
        return False
    received_user, sep, received_pass = decoded.partition(":")
    if not sep:
        return False
    user_ok = hmac.compare_digest(received_user.encode("utf-8"), username.encode("utf-8"))
    pass_ok = hmac.compare_digest(received_pass.encode("utf-8"), password.encode("utf-8"))
    return user_ok and pass_ok

```

> **Make Databricks webhooks production-ready.** [Hookdeck Event Gateway](/event-gateway) checks Databricks' Basic credentials, records every job and alert notification, and retries delivery to your service, which Databricks doesn't document doing.

In Hookdeck, create a Databricks source and enter the same username and password as the destination. Hookdeck supports the HTTP Basic mode only, so don't use OAuth on a destination that points at Hookdeck. Verification is optional on the source.

## Databricks webhook limitations and pain points

### Basic auth, no signature

**The Problem:** The credentials prove who sent the request, but nothing protects the body or its age.

**Why It Happens:** Databricks sends a static `Authorization` header. There is no HMAC and no timestamp.

**Workarounds:**

* Use a long random password per destination, and keep the endpoint HTTPS-only.
* Allowlist Databricks' control plane and data plane IPs as a second check.

**How Hookdeck Can Help:** Only Hookdeck needs the Databricks credentials. Your services receive events from Hookdeck and don't need to store or compare them.

### No documented retries

**The Problem:** A failure notification sent during an outage may never arrive.

**Why It Happens:** Databricks documents no retry policy, timeout or delivery ID for webhook destinations.

**Workarounds:**

* Return `2xx` quickly and process asynchronously.
* Reconcile important runs through the Jobs API rather than relying on notifications alone.

**How Hookdeck Can Help:** Hookdeck stores every notification it receives and retries delivery to your service on a schedule you set, so your own downtime doesn't drop a failure alert.

### Retries and job-level notifications

**The Problem:** A task fails, Databricks retries it, and the job-level notification doesn't tell you.

**Why It Happens:** Job-level notifications aren't sent when failed tasks are retried. Tasks retry three times by default before failing fully.

**Workarounds:**

* Add task-level notifications for failures you need to see on every attempt.
* Use **Mute notifications until the last retry** on tasks if you only want the final result.

**How Hookdeck Can Help:** Hookdeck filters can route task notifications, which carry `task.task_key`, separately from job notifications, so each goes to the right handler.

### Thin job payloads

**The Problem:** Job notifications don't say why a run failed.

**Why It Happens:** The payload carries only `event_type`, `workspace_id`, run IDs and the job's ID and name.

**Workarounds:**

* Fetch the run from the Jobs API using `run.run_id`.

**How Hookdeck Can Help:** Hookdeck keeps the full history of notifications per source, so you can see which runs fired which events when you investigate.

## Best practices

### Deduplicate on `event_type` and `run.run_id`

There is no event ID, so combine the event type with the run ID (and `task.task_key` for tasks). See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Ignore unknown fields

Databricks says SQL alert payloads vary with alert configuration and omit empty fields. Parse defensively and don't reject payloads with extra keys.

### Mute what you don't need

Use **Mute notifications for skipped runs** and **Mute notifications for canceled runs** to cut noise. These job-level settings don't affect task notifications.

### Treat alert payloads as sensitive

SQL alert payloads include the query text and the owner's name and email. Restrict access to wherever you store them.

## Conclusion

Databricks webhook destinations deliver job and SQL alert notifications as JSON, authenticated with HTTP Basic credentials rather than a signature. Check the credentials in constant time, deduplicate on the event type and run ID, and use the Jobs API for detail the payload doesn't carry.

[Hookdeck Event Gateway](https://hookdeck.com) records every notification and retries delivery so a failed job alert isn't lost.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Databricks webhooks reliably in minutes.