Hi! A few things have to line up before notification webhooks fire — and the most likely cause is #1, which is probably also *why* you ended up in the Django admin: the UI page is hidden while the feature is off.
*1. Turn the feature on:* Configuration → System Settings → "Enable Webhook notifications" (off by default). While it's off:
• the "Notification Webhooks" menu entry is hidden and `/notifications/webhooks` returns a 404
• the *webhooks* column is hidden on the notification settings pages
• nothing is ever sent, even though your endpoint exists in the DB — the dispatcher checks this flag *before* it looks at your endpoints, so you get no log line at all. Exactly the silence you're seeing.
*2. Create (or re-save) the endpoint from the UI, not the Django admin:* Configuration → Notification Webhooks → Add (`/notifications/webhooks/add`). Saving there sends a test `ping` POST and only stores the endpoint if it returns 2xx, so you immediately see *why* it failed (DNS, TLS, 401, timeout…). It also resets `status` / `first_error` / `last_error` to a clean *Active*. The Django admin skips the ping entirely and `status` isn't editable there, so a broken endpoint just sits silently. If you already created one in admin, open it in the UI and hit save.
*3. Subscribe events to the webhooks channel* — the most commonly missed step. On Configuration → Notifications (`/notifications/system` for system-level, `/notifications` for personal), every event has a checkbox per channel and the default is *alert only*. Tick *webhooks* for the events you want.
*4. Match the endpoint's Owner to the notification scope:*
• Owner *empty* = system webhook → receives system notifications, driven by the *system* notification settings page.
• Owner *= a user* = personal webhook → receives only that user's personal notifications, driven by that user's own settings page.
The endpoint lookup is an exact match on owner, so a system endpoint never receives personal notifications and vice versa. Setting yourself as owner but only configuring the system page (or the reverse) is a silent no-op.
*5. Check the endpoint status* on the webhook list page. DefectDojo deactivates endpoints on failure:
• 4xx or any exception → `inactive_permanent` immediately, no retries
• 5xx / 429 / timeout → `inactive_tmp`, auto-retried after 60s by a Celery task, and permanently deactivated after ~24h of continuous failures
The `note` field holds the last error. Editing + saving in the UI reactivates it (and re-pings).
*Where to look for logs / tasks*
The logger is `dojo.notifications.helper` (plus `dojo.notifications.tasks`). Set `DD_LOG_LEVEL=DEBUG` and watch *both* uwsgi and celeryworker — some events are dispatched to Celery (`async_create_notification`), others run inline in the request thread:
```
docker compose logs -f celeryworker uwsgi
# k8s: kubectl logs -f deploy/defectdojo-celeryworker
```
Grep for these in order — the first one you *don't* see tells you which step above is missing:
• `Sending Webhooks Notification` → flag is on *and* the event is subscribed to webhooks (steps 1 + 3 OK)
• `URLs for Webhooks not configured: skipping system notification` → no endpoint matched the owner scope (step 4)
• `URL for Webhook '<name>' is not active: …` → endpoint is deactivated (step 5)
• `Sending webhook message to endpoint '<name>'` then `Message sent to endpoint '<name>' successfully.` → request actually made
• `Error when sending message to Webhooks '<name>' (status: NNN): <body>` → your endpoint rejected it
Celery also needs to be running for the retry/cleanup tasks (`webhook_reactivation`, `webhook_status_cleanup` in the beat schedule) — `docker compose exec celeryworker celery -A dojo inspect active` / `scheduled` is a quick sanity check. Failures that raise exceptions also show up as *Alerts* in the bell menu.
Fastest end-to-end test: re-save the endpoint in the UI (that's the `ping`), then subscribe `product_type_added` to webhooks and create a product type. Per-event payloads are documented under Docs → Automation → API → Notification Webhooks: <docs.defectdojo.com/automation/api/notification_webhooks>
One extra gotcha if you ever script the endpoint via the API instead of the UI: omitting `header_name` stores an empty string rather than null, and DefectDojo then tries to send a header with an empty name, which `requests` rejects — the endpoint gets permanently deactivated with `Exception: Invalid leading whitespace, reserved character(s), or return character(s) in header name: ''` in its note. Send `"header_name": null` or a real header name. (The UI and admin forms convert blank to null, so they're unaffected.)