---
title: "Webhooks - Workshop Docs"
description: "Webhooks - Enterprise control plane for Santa. Manage rules, approvals, telemetry, and policies across your macOS fleet."
doc_version: "1"
last_updated: "2026-08-18"
canonical: "https://northpole.security/docs/workshop/webhooks"
---
# Webhooks

Workshop can POST events to an HTTPS endpoint of your choosing as they happen, letting you forward Workshop activity into your own systems — SIEMs, ticketing, chat, or custom automation. Each delivery is signed following the [Standard Webhooks](https://www.standardwebhooks.com) specification so your receiver can verify it genuinely came from Workshop.

## Event sources

There are three independent webhook sources. Each has its own destination URL, signing secret, and delivery filters, so you can send different event types to different endpoints (or the same one).

Source

Fires when

Filter

Audit events

Any change is made to Workshop (rules, settings, tags, etc.)

Event type

Signal reports

A detection signal report is first received, and again on each triage state change

Report state

Software approvals

A piece of software is approved through an approval workflow for the first time

None

### Audit events

Fires once for every audit event — the same record of every change made to Workshop that appears in the audit log, whether the change was made through the UI or the API. See the [Audit documentation](./audit) for the full list of audit event types.

By default every audit event is delivered. You can narrow delivery to specific event types; leave the filter empty to deliver all of them.

### Signal reports

Fires when a detection signal report is first received from a host (state `NEW`) and again each time a report’s triage state changes (for example when it moves to `ACKNOWLEDGED` or `REMEDIATED`).

The state filter applies to both cases — receipt counts as the `NEW` state. Leave it empty to deliver for every state. The available states are `NEW`, `ACKNOWLEDGED`, `INVESTIGATING`, `REMEDIATED`, and `DISMISSED`.

### Software approvals

Fires once, the first time a given piece of software (a binary or a bundle) is approved through any approval workflow — self-service, designated approver, or social voting. Subsequent approvals of the same software do **not** fire. See the [Approval Workflows documentation](./approval-workflows) for how approvals work.

This source has no filter. The payload’s `requesting_user` field identifies who requested the software for self-service and designated-approver workflows; it is empty for social voting, which has no single requester.

## Configuring webhooks

Navigate to Settings → Webhooks. Each source is configured in its own section with the following fields:

1.  **Enable toggle** — turns delivery on or off. Disabling a source stops delivery but keeps its URL and secret so you can re-enable it later without re-entering them.
2.  **Destination URL** — the HTTPS endpoint that receives deliveries.
3.  **Signing secret** — the key used to sign every delivery (see Verifying signatures). It must be at least 24 bytes.
4.  **Filter** — event types (audit events) or states (signal reports), where applicable.
5.  **Custom headers** (audit events) — additional HTTP headers sent with every delivery, e.g. an `Authorization` header your receiver expects.

Click **Save Changes** to apply. Saving requires the `write:settings` permission.

## Delivery format

Each delivery is an HTTP `POST` with a `Content-Type` of `application/json`. The body is a JSON object with exactly one field set, identifying the source:

Field

Source

`audit_event`

Audit events

`signal_report`

Signal reports

`software_approval`

Software approvals

Payloads are serialized from Workshop’s protobuf definitions, so:

-   Field names are `snake_case` and enum values are their string names (not numbers).
-   Every field is emitted even when empty — an unset string is `""`, a number is `0`, a boolean is `false`, an unset nested object is `null`, and an empty list is `[]`. Don’t assume a missing field; assume an empty one.

The examples below are trimmed to the fields worth highlighting; a real delivery includes the remaining fields at their empty values as described above.

<pre class\="astro\-code github\-dark" style\="background\-color:#24292e;color:#e1e4e8;overflow\-x:auto" tabindex\="0" data\-language\="json"\><code\><span class\="line"\><span style\="color:#E1E4E8"\>{</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;audit\_event&quot;</span\><span style\="color:#E1E4E8"\>: {</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;id&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;0f9c2e6a\-1d3b\-4a7e\-9c2f\-8b1a5e6d7c40&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;transaction\_id&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;3a1b8f22\-6c4d\-4e19\-8f2a\-1b7c9d0e5a63&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;timestamp&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;2026\-07\-20T15:04:05Z&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;actor&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;user:rah@northpole.security&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;event&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;AUDIT\_EVENT\_RULE\_UPSERT&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;resource&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;outcome&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;OUTCOME\_SUCCESS&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;details&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;{</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>policy</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>:</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>ALLOWLIST</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>,</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>rule\_type</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>:</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>BINARY</span\><span style\="color:#79B8FF"\>\\&quot;</span\><span style\="color:#9ECBFF"\>}&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;previous\_value&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;ai\_chat\_conversation\_id&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;via\_mcp&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>false</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> }</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\>}</span\></span\></code\></pre\><p\>The <code\>host</code\> object carries the full set of host fields; only a few are shown here (see the <span\>Hosts documentation</span\>).</p\><pre class\="astro\-code github\-dark" style\="background\-color:#24292e;color:#e1e4e8;overflow\-x:auto" tabindex\="0" data\-language\="json"\><code\><span class\="line"\><span style\="color:#E1E4E8"\>{</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;signal\_report&quot;</span\><span style\="color:#E1E4E8"\>: {</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;id&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;6d2f1c88\-9a7e\-4b31\-8c05\-2e9f4a1b7d63&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;host&quot;</span\><span style\="color:#E1E4E8"\>: {</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;uuid&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;A14A8806\-5878\-45A2\-81E4\-DAB36020B560&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;serial&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;C02XL0ZYJGH5&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;hostname&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;workstation.example.com&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;os\_version&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;15.5&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;primary\_user&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;jane@example.com&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;os\_type&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;OS\_TYPE\_MACOS&quot;</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> },</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;name&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;suspicious\_persistence&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;severity&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;SEVERITY\_CRITICAL&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;description&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;A launch agent was written by an unsigned binary.&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;event\_ids&quot;</span\><span style\="color:#E1E4E8"\>: \[</span\><span style\="color:#9ECBFF"\>&quot;execution:8f0a1b2c\-3d4e\-5f60\-7182\-93a4b5c6d7e8&quot;</span\><span style\="color:#E1E4E8"\>\],</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;reported\_at&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;2026\-07\-20T15:04:05Z&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;processed\_time&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>null</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;state&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;SIGNAL\_REPORT\_STATE\_NEW&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;assignee&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;resolved\_by&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;labels&quot;</span\><span style\="color:#E1E4E8"\>: \[</span\><span style\="color:#9ECBFF"\>&quot;persistence&quot;</span\><span style\="color:#E1E4E8"\>, </span\><span style\="color:#9ECBFF"\>&quot;unsigned&quot;</span\><span style\="color:#E1E4E8"\>\],</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;os\_type&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;OS\_TYPE\_MACOS&quot;</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> }</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\>}</span\></span\></code\></pre\><p\>For a bundle approval, the <code\>binary</code\> field is replaced by a <code\>bundle</code\> object that nests its constituent binaries.</p\><pre class\="astro\-code github\-dark" style\="background\-color:#24292e;color:#e1e4e8;overflow\-x:auto" tabindex\="0" data\-language\="json"\><code\><span class\="line"\><span style\="color:#E1E4E8"\>{</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;software\_approval&quot;</span\><span style\="color:#E1E4E8"\>: {</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;blockable\_id&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;app\_name&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;Example.app&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;approving\_user&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;approver@example.com&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;requesting\_user&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;requester@example.com&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;binary&quot;</span\><span style\="color:#E1E4E8"\>: {</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;sha256&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;cdhash&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;signing\_id&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;com.example.app&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;team\_id&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;EQHXZ8M8AV&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;file\_name&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;Example&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;signed\_by&quot;</span\><span style\="color:#E1E4E8"\>: \[</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> {</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;sha256&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;common\_name&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;Developer ID Application: Example Inc (EQHXZ8M8AV)&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;organization&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;Example Inc&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;organizational\_unit&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;valid\_from&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>null</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;valid\_until&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>null</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;signed\_by&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;first\_seen&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>null</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> }</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> \],</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;entitlements&quot;</span\><span style\="color:#E1E4E8"\>: \[\],</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;first\_seen&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>null</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;malicious\_state&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;MALICIOUS\_STATE\_BENIGN&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;bundle\_relative\_path&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;&quot;</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;signing\_timestamp&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>null</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;secure\_signing\_timestamp&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#79B8FF"\>null</span\><span style\="color:#E1E4E8"\>,</span\></span\> <span class\="line"\><span style\="color:#79B8FF"\> &quot;signing\_status&quot;</span\><span style\="color:#E1E4E8"\>: </span\><span style\="color:#9ECBFF"\>&quot;SIGNING\_STATUS\_PRODUCTION&quot;</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> }</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\> }</span\></span\> <span class\="line"\><span style\="color:#E1E4E8"\>}</span\></span\></code\></pre\>

### Request headers

Every delivery includes the Standard Webhooks headers plus a Workshop `User-Agent`:

Header

Description

`webhook-id`

Unique identifier for the delivery (see below). Use it to deduplicate.

`webhook-timestamp`

Delivery time as a Unix timestamp in seconds.

`webhook-signature`

The signature over the payload (see Verifying signatures).

`Content-Type`

Always `application/json`.

`User-Agent`

`Workshop/<version> (+https://northpole.security)`

Any custom headers you configure are added on top of these.

The `webhook-id` is namespaced by source so you can tell deliveries apart:

Source

`webhook-id` format

Audit events

`audit:<event-id>`

Signal reports

`signal:<uuid>`

Software approvals

`software-approval:<uuid>`

The same `webhook-id` is reused across retries of a delivery, so it’s safe to deduplicate on it.

## Verifying signatures

Workshop signs every delivery using the [Standard Webhooks](https://www.standardwebhooks.com) scheme. The signature is an HMAC-SHA256 over the string `{webhook-id}.{webhook-timestamp}.{body}`, base64-encoded, and sent in the `webhook-signature` header as `v1,<signature>`.

The simplest way to verify is with one of the [Standard Webhooks libraries](https://www.standardwebhooks.com), which handle the signature construction and comparison for you — construct a verifier with your configured secret and pass it the raw request body and headers. Note that the standard verification also enforces a **5-minute timestamp tolerance** to guard against replay, so your receiver’s clock should be reasonably in sync.

## Delivery behavior

Deliveries happen asynchronously, off the request path — a webhook failure never blocks or fails the underlying action (for example, an audited change still succeeds even if its webhook can’t be delivered). Delivery is best-effort: failures are logged server-side, but there is no delivery dashboard or manual re-drive.

-   **Retries** — after the initial attempt, a failed delivery is retried up to 5 more times (six attempts in total) with exponential backoff (starting at 100ms, capped at 30s). Retries happen on connection errors, HTTP 429, and 5xx responses.
-   **Success** — any 2xx response is treated as success. Redirects are not followed.
-   **Timeouts** — each attempt has its own 30s timeout, and the whole delivery — loading settings, building the client, and all attempts with their backoffs — is bounded to 60s.
-   **Payload size** — deliveries are capped at 1MB. A payload larger than that is dropped rather than sent.

Your endpoint should acknowledge quickly with a 2xx and do any heavy processing asynchronously.

## Security

-   Destination URLs must use **HTTPS** (plain HTTP is only permitted for `localhost`, for local testing).
-   URLs may not contain embedded credentials (`user:pass@host`).
-   URLs that resolve to private, internal, or link-local addresses are rejected to guard against SSRF. Redirects are never followed.
-   The signing secret is write-only: it is never returned by the API or shown in the UI, and it is stripped from audit log entries.

## Configuring via the API

Webhook settings can also be managed through the API:

-   `GetWebhookSettings` retrieves the current configuration for every source. Requires the `read:settings` permission. The write-only signing secret is never included in the response.
-   `UpdateWebhookSettings` replaces the configuration for every source. Requires the `write:settings` permission.

Because `UpdateWebhookSettings` **replaces** the entire configuration, include every source you want to keep in each call — omitting a source clears it. Sending an empty secret for a source keeps its existing secret rather than clearing it.

## Sitemap

- [Home](https://northpole.security/index.md)
- [Workshop](https://northpole.security/workshop.md)
- [Santa](https://northpole.security/santa.md)
- [Features](https://northpole.security/features.md)
- [Cookbook](https://northpole.security/cookbook.md)
- [Docs](https://northpole.security/docs.md)
- [Blog](https://northpole.security/blog.md)
- [Glossary](https://northpole.security/glossary.md)
- [About](https://northpole.security/about.md)
- [Contact](https://northpole.security/contact.md)
