# Webhooks

> Register outbound webhooks that POST signed event notifications to external URLs—for example when a test run completes—with a signing secret, test/redeliver, and a delivery log.

Source: https://totalshiftleft.ai/help-center/product-documentation/webhooks

## Overview

**Outbound webhooks** POST event notifications (for example when a test run completes, or when someone is @mentioned in a comment) to a URL you control. Each payload is signed with the subscription's secret so your receiver can verify it came from Shift-Left Studio. This guide walks the **Outbound Webhooks** settings screen exactly as it appears in the app. Explore the capability at [webhooks](/features/collaboration-security/webhooks).

**Availability:** Professional and Enterprise.

## Before you begin

- Webhooks are administrator-only. You need the **manage_webhooks** permission; without it the screen is not available to you.
- Webhooks live under **Settings → Integrations → Webhooks**. The screen header reads **Outbound Webhooks** with the subtitle *"Send signed event notifications to external systems."*
- Your **Payload URL** must be a public `http(s)` endpoint the engine can reach. URLs are validated to block server-side request forgery (SSRF), so internal or disallowed hosts are rejected.

## Step 1 — Enable outbound webhooks

1. At the top of the screen, find the **Enable Outbound Webhooks** card. It carries a switch on the right.
2. When webhooks are off, the card reads *"Turn on to send signed event notifications to external systems."* and the panel below shows *"Outbound webhooks are currently disabled. Use the switch above to enable them, then create a subscription."*
3. Click the switch. On success you'll see the toast *"Outbound webhooks enabled"*, the description changes to *"Outbound webhooks are active. Create and manage subscriptions below."*, and the **Create webhook** form and subscription list appear.
4. Flipping the switch off shows *"Outbound webhooks disabled"* and hides the management UI. This master toggle is stored in system settings, so you don't need to edit any environment variable.

## Step 2 — Create a webhook

In the **Create webhook** form, fill in these fields and click **Create**:

| Field | What it does |
|-------|--------------|
| **Payload URL** | The `https://…` endpoint that receives the POST. Placeholder: `https://example.com/webhooks/shiftleft`. Required. Must start with `http://` or `https://`; disallowed/unreachable hosts are rejected with **URL_NOT_ALLOWED**. |
| **Events** | Which event types to subscribe to. Enter `*` (the default) for every event, or a comma-separated list such as `test.run.completed`. |
| **Project ID (optional)** | Scope the subscription to one project. Placeholder: `all projects`. Leave blank to receive events from every project. |

The **Create** button is disabled until **Payload URL** has a value; while submitting it reads **Creating…**. On success you'll see the toast *"Webhook created"* and the new subscription appears in the list.

## Step 3 — Copy the signing secret (shown once)

As soon as the webhook is created, an orange **Copy your signing secret now** banner appears with your secret (a value beginning `whsec_`). The banner warns: *"This secret is shown only once and cannot be retrieved later. Store it securely."*

1. Click **Copy** to copy the secret (the button briefly reads **Copied**).
2. Store it in your secret manager or receiver configuration.
3. Click **Dismiss** to close the banner.

If you navigate away without copying it, the secret is gone — you must **Rotate** (Step 4) to get a new one.

## Step 4 — Manage a subscription

Each subscription row shows the **Payload URL**, an **Enabled**/**Disabled** badge, its subscribed event tags, and either the project ID or *"All projects"* plus the created timestamp. The row's action buttons are:

| Control | What it does |
|---------|--------------|
| **Disable** / **Enable** | Pauses or resumes deliveries. The label reflects the current state; toasts read *"Webhook disabled"* / *"Webhook enabled."* |
| **Test** | Fires a synthetic `webhook.test` event (payload *"Test event from Shift-Left Studio"*) to the Payload URL. Toast: *"Test event fired."* Use it to confirm your receiver is reachable and your signature check works. |
| **Deliveries** | Expands an inline log of recent attempts (see Step 5). |
| **Rotate** | Generates a **new** signing secret. The old secret stops working immediately. The new secret is shown once in the same orange banner as Step 3 — copy it right away. Toast: *"Signing secret rotated."* |
| **Trash icon** | Deletes the subscription. You're asked to confirm *"Delete webhook for [url]? This cannot be undone."* Toast: *"Webhook deleted."* |

## Step 5 — Read the deliveries log

Click **Deliveries** on a subscription to expand its recent delivery attempts (most recent first). Each entry shows:

- A green check (delivered) or red X (failed).
- The **event** name and the **attempt** number.
- The HTTP **status code** the receiver returned, an **error** message if the attempt failed, and the delivery timestamp.

If there are none yet you'll see *"No deliveries yet."* Fire a **Test** event or trigger a real event to populate it.

## Step 6 — Verify the signature on your receiver

Every payload is signed with the subscription's secret. On your server, recompute the signature from the raw request body using your stored `whsec_…` secret and compare it to the signature header before trusting the payload. Reject any request whose signature doesn't match. Rotate the secret (Step 4) if you suspect it has leaked, and update your receiver with the new value.

## Troubleshooting / Notes

- **URL rejected on create (URL_NOT_ALLOWED / INVALID_URL)** — the Payload URL isn't a valid public `http(s)` URL, or it points at a disallowed host (SSRF protection). Use a reachable public HTTPS endpoint.
- **Screen shows only the disabled notice** — outbound webhooks are turned off. Use the **Enable Outbound Webhooks** switch (Step 1).
- **Deliveries failing** — check the status code and error in the **Deliveries** log; confirm your endpoint returns a 2xx and that your signature check accepts the current secret.
- **Lost the secret** — it's shown only once; use **Rotate** to issue a new one, then update your receiver.
- Webhooks are also a notification channel for [dashboard alerts](/help-center/product-documentation/scheduled-reports-and-alerts) and surface [comment](/help-center/product-documentation/comments) @mentions.

## Related articles

- [Scheduled reports and alerts](/help-center/product-documentation/scheduled-reports-and-alerts)
- [Public API](/help-center/product-documentation/public-api)
- [API keys](/help-center/product-documentation/api-keys)
- [Audit Logs](/help-center/product-documentation/audit-logs)

