Skip to content

Notifications

dbward can notify external systems when events occur — new requests, approvals, failures, emergency access. This page covers how to set up the delivery mechanisms. For controlling which events fire on which databases, see Notification Policies.


[[webhooks]]
url = "https://your-service.com/dbward-events"
format = "generic"
secret = "${WEBHOOK_SECRET}"
events = ["request_created", "request_completed", "break_glass"]
FieldTypeDefaultDescription
urlStringDelivery endpoint (HTTPS required in production)
formatString"generic"Payload format: generic or slack
secretStringHMAC-SHA256 signing key
eventsString[][] (all)Filter events (empty = all events)
{
"event": "request_created",
"timestamp": "2025-01-15T10:30:00Z",
"request": {
"id": "req_abc123",
"operation": "execute_dml",
"database": "app",
"environment": "production",
"requester": "alice",
"risk_level": "medium"
}
}

When secret is set, every delivery includes:

X-Webhook-Signature: sha256=<hex-encoded HMAC-SHA256 of body>

Verify in your receiver:

import hmac, hashlib
expected = hmac.new(secret.encode(), request.body, hashlib.sha256).hexdigest()
actual = request.headers["X-Webhook-Signature"].removeprefix("sha256=")
assert hmac.compare_digest(expected, actual)
  • Deliveries are persisted before sending (no lost events on crash)
  • Failed deliveries retry up to 10 times with exponential backoff
  • Timeout: 10 seconds per attempt
  • Redirects are disabled (SSRF protection)
  • Internal network addresses are blocked

SQL in webhook payloads is redacted by default:

[audit]
redaction = "literals" # Replace literals with ? (default)
# redaction = "none" # Send full SQL
# redaction = "full" # Send hash only

Slack integration uses a dedicated [slack] config section for richer formatting and approve/reject buttons.

  1. Go to https://api.slack.com/appsCreate New AppFrom scratch
  2. Add Bot Token Scopes: chat:write, channels:join (recommended)
  3. Install to Workspace → copy Bot Token (xoxb-...)
  4. Copy Signing Secret from Basic Information
[slack]
bot_token = "${SLACK_BOT_TOKEN}"
signing_secret = "${SLACK_SIGNING_SECRET}"
channel = "C02C1EUJ0EN" # Default channel ID

Per-environment channels (optional):

[slack.channels]
production = "C02C1EUJ0EN"
staging = "C03D2FKJ1FO"

Slack messages include:

  • Requester, database, environment, operation
  • Risk level (🔴 High / 🟡 Medium / 🟢 Low)
  • Required approvers (with mentions)
  • Review Request button

Messages update in-place as the request progresses through its lifecycle.

Security: SQL is never shown in channel messages — only in the approval Modal (after authorization check).


Slack buttons enable one-click approve/reject directly from Slack.

  1. In your Slack App → Interactivity & Shortcuts → toggle On
  2. Set Request URL: https://your-server.com/api/slack/interactions
  3. Save

See Slack: Handling user interaction for details.

  1. Approver clicks Review Request
  2. Modal opens with: full SQL, risk details, EXPLAIN output
  3. Approver selects Approve/Reject + adds comment
  4. Request state updates, Slack message updates

Slack approval flow — requester view

The demo above shows the requester’s perspective (create → approve → resume → result). Approvers follow the same flow via the Review Request button in their notifications.

Create requests directly from Slack without CLI.

Setup:

  1. In your Slack App → Slash CommandsCreate New Command
  2. Command: /dbward
  3. Request URL: https://your-server.com/api/slack/commands
  4. Short Description: Execute SQL via approval workflow
  5. Usage Hint: execute | help
  6. Save (reinstall app if prompted)

See Slack: Implementing slash commands for details.

Commands:

CommandAction
/dbward executeOpen SQL submission modal
/dbward helpShow usage

Authentication: No Bearer token required — both /api/slack/commands and /api/slack/interactions are verified using the Slack Signing Secret (HMAC-SHA256). Only requests signed by Slack are accepted.

Each user links their Slack account:

Terminal window
dbward user update --slack-user-id U02CR3TMKKJ

Find your Member ID: Profile → Copy member ID.

Users without linked accounts can still approve via CLI/API.


IssueSolution
No notifications sentCheck [[webhooks]] or [slack] config + env vars
not_in_channelInvite bot: /invite @dbward or add channels:join scope
Signature mismatchVerify secret matches between config and receiver
Button click errorCheck Interactivity URL is correct and publicly accessible
”Account not linked”Run dbward user update --slack-user-id YOUR_ID
/dbward shows “not a valid command”Register Slash Command in Slack App settings
”No databases available”User needs request.query or request.execute permission

Webhook vs Interactive: Choosing the Right Integration

Section titled “Webhook vs Interactive: Choosing the Right Integration”

dbward offers two independent Slack notification paths. They can be used alone or together.

Webhook (format = "slack")Interactive ([slack])
Setup[[webhooks]] + Incoming Webhook URL[slack] + Bot Token + Signing Secret
DeliveryIncoming Webhook (passive)Bot Token API (chat.postMessage)
Approve/Reject❌ CLI only✅ Buttons + Modal
SQL in message✅ Shown directly (redacted)❌ Only in Review Modal
Thread replies❌ Single message per event✅ Thread + message updates
Mentions✅ @user notifications

Both enabled? Both fire simultaneously. This is safe — webhook delivers a passive summary while interactive provides full button support.

The format = "slack" webhook displays redacted SQL directly in the channel message. This is by design — since Incoming Webhooks don’t support buttons, the approver needs to see what they’re approving from the notification alone.

If you don’t want SQL visible in the Slack channel:

  1. Use Interactive only — SQL is shown only inside the Review Modal (after authorization check)
  2. Use format = "generic" — receive the raw JSON and format it yourself, omitting detail
  3. Set redaction = "full" — replaces SQL with a hash (applies to both formats)
# Option 3: Hash-only redaction
[audit]
redaction = "full"