> For the complete documentation index, see [llms.txt](https://docs.material.security/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.material.security/integrations/integration-fundamentals/custom-webhooks.md).

# Custom Webhooks

## Overview

Use a custom webhook when your integration destination is not specifically a SIEM, SOAR, or messaging/ticket tool (for example, a BI tool, a custom app built internally, etc).

Custom webhooks:

* Trigger outbound delivery based on a specific event like a change in severity, specific detection, user action in a message, and more.
* Send events to your app as a JSON payload (see [example payload ](#example-payload)below)

If your destination has a URI endpoint and accepts JSON payloads over HTTPS, you can integrate with it via a custom webhook.

<figure><img src="/files/U48R6D6yZlISstd8u1M1" alt=""><figcaption><p>Integrations Page</p></figcaption></figure>

## How to create a webhook

{% hint style="warning" %}
A Super Admin or Tenant Admin [role](/learn-more/administration/admin-roles.md) is required to create webhooks.
{% endhint %}

### One: Create an event destination in the third-party tool

In the tool you'll receive webhook events in:

1. Create and secure a webhook endpoint handler to receive event data requests (most often `POST`). Refer to your tool's documentation for instructions on how to do this.
2. Copy the endpoint URI and any required headers for use in the next section.

### Two: Configure the webhook in Material

1. In Material, click **Integrations** (the puzzle icon in the top tool bar).
2. Click **Create New Integration**.
3. Scroll to **Custom**, then click **Webhook**. Complete the required fields:

<figure><img src="/files/NNh5YDJikpY0KZF3i4iq" alt=""><figcaption></figcaption></figure>

{% tabs %}
{% tab title="Method" %}
This is most often `POST` , however refer to your tool's documentation for the required method for their destination.
{% endtab %}

{% tab title="URI" %}
The full HTTPS destination or endpoint URL for your tool; check their documentation or support. Create a unique custom webhook for each endpoint you need to deliver to.
{% endtab %}

{% tab title="Headers" %}
Include any required headers for your destination. Sometimes this includes an auth header with a password; check your destination's documentation to confirm.
{% endtab %}

{% tab title="Event" %}
Choose the event(s) that will trigger your webhook. Use the form to build the trigger based on multiple filters.

* The most common event used is `Issue Change` . It covers most needs.
* Other common filters are:
  * **Severities**: trigger based on an issue severity change
  * **Detection**: trigger based on a specific detection
  * **Classifications**: trigger based on an issue's classification (for example, route only issues that auto-classification left as Unknown)
* Choose the option to **Include events for messages being added / removed / interacted with in a phishing case** if you want to also trigger based on individual message level changes (rather than just issue level changes).

***

* While you can include multiple events in one integration, you can only include one of each event type (i.e. one issue change, etc.) All the filters must match to trigger the event. If you need to trigger webhook using OR logic, create multiple integrations with different event parameters.

<figure><img src="/files/gcmSUUqhfuYzcEZh6NMc" alt="" width="336"><figcaption></figcaption></figure>

* Toggle events off/on at any time in the integration view.
* Once your webhook starts firing, the last time an event triggered is included in the integration view, under **Events**:

<figure><img src="/files/duhYtIhHFCG1fnrA2LyH" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

4. Add as many events as desired. You can clone events rather than filling out the form each time. Open the actions menu (⋯) on the event row, then click **Clone.**
5. Integrations are toggled on by default, so if you want to test it first, toggle the integration **Off**, then **save** the Integration.
6. Open an event, then click **Send Test Event** to test your connection. (You need to save the integration first to see this option).

* All webhooks send payloads with the same fields in JSON format. Event Descriptions are in app; click the **API** icon in the toolbar then click **Events**.
* Material verifies SSL certificates when delivering payloads.
* Once you have saved the integration, you can click **Copy Test Event** to view an example payload.

<details>

<summary>Example Payload</summary>

{% code overflow="wrap" %}

```json
{
  "eventId": "abc123",
  "timestamp": "YYYY-MM-DDTHR:MIN:SEC",
  "orgId": "demo",
  "uDomainId": "google://C029fz6xs/",
  "requestor": {
    "system": true
  },
  "tenant": null,
  "account": null,
  "group": null,
  "file": null,
  "message": {
    "uDomainId": "google://abc123",
    "messageId": "<demo@acme.com>",
    "msgDate": "YYYY-MM-DDTHR:MIN:SEC",
    "msgSender": "demo@acme.com"
  },
  "app": null,
  "type": "CREATE",
  "issue": {
    "entityType": "MESSAGE",
    "uDomainId": "google://abc123",
    "messageId": "<demo@acme.org>",
    "msgDate": "YYYY-MM-DDTHR:MIN:SEC",
    "id": "abc123",
    "entityId": "MESSAGE:{\"messageId\":\"<demo@acme.org>\",\"msgDate\":\"YYYY-MM-DDTHR:MIN:SEC\",\"uDomainId\":\"google://abc123/\"}",
    "detectionId": "phishing-attack-user-report",
    "detectionType": "PHISHING_EVENT_BASED",
    "categories": [
      "EMAIL_SECURITY"
    ],
    "tactics": [],
    "severity": "03-MEDIUM",
    "status": "OPEN",
    "lastStateChangeTimestamp": "YYYY-MM-DDTHR:MIN:SEC",
    "lastOpenedTimestamp": "2YYYY-MM-DDTHR:MIN:SEC",
    "lastResolvedTimestamp": null,
    "snoozedUntilTimestamp": null,
    "ignoreReason": null,
    "resolveReason": null,
    "classification": "MALICIOUS",
    "analysis": [],
    "scopes": [
      {
        "type": "count",
        "countType": "num_accounts",
        "countValue": 1
      },
      {
        "type": "count",
        "countType": "num_messages",
        "countValue": 1
      },
      {
        "type": "count",
        "countType": "num_links",
        "countValue": 0
      },
      {
        "type": "count",
        "countType": "num_attachments",
        "countValue": 0
      }
    ],
    "associatedEntityIds": [
      "ACCOUNT:{\"uAcctId\":\"google://abc123/\",\"uDomainId\":\"google://abc123/\"}",
      "MESSAGE:{\"messageId\":\"<demo@acme.org>\",\"msgDate\":\"YYYY-MM-DDTHR:MIN:SEC\",\"uDomainId\":\"google://abc123/\"}"
    ],
    "numAccounts": 1,
    "numMessages": 1,
    "numAttachments": 0,
    "numLinks": 0,
    "fixes": [
      {
        "type": "tag",
        "label": "Speedbump"
      },
      {
        "type": "tag",
        "label": "Warning Banner"
      },
      {
        "type": "tag",
        "label": "Spam"
      }
    ],
    "extra": {
      "caseId": "20260309_KrTmx0YJ"
    },
    "caseId": "20260309_KrTmx0YJ",
    "comments": [],
    "ownerUAcctId": null,
    "createdAt": "YYYY-MM-DDTHR:MIN:SEC",
    "updatedAt": "YYYY-MM-DDTHR:MIN:SEC",
    "lastCheckedAt": "YYYY-MM-DDTHR:MIN:SEC",
    "entityDisplayLabel": null,
    "detectionName": "Message flagged by user",
    "nameSearchValue": "",
    "nameSearchValueUpdatedAt": null,
    "dedupKey": "20260309_KrTmx0YJ",
    "dedupTimestamp": "YYYY-MM-DDTHR:MIN:SEC",
    "isMock": false,
    "issueName": "Message flagged by user",
    "issueLink": "https://acme.com"
  },
  "after": null,
  "before": null,
  "getMaterialBaseUrl": {
    "url": "https://acme.com"
  }
}
```

{% endcode %}

</details>

You can view a test event to help you build a receiver for JSON events without having to send test events and log the payload.

From the event, click **View Test Event** to open a window with the full JSON payload and copy it to your clipboard.

You need to save your integration first for this option to appear. See step 6 above.

<figure><img src="/files/T4D64venjrWw9MWZjXfl" alt=""><figcaption><p>From an integration, open an event row to view a test event</p></figcaption></figure>

### Disable a webhook

1. From the **Integrations** page, open the webhook.
2. On the top right, toggle **Off**.

***

## Event Delivery Behavior

### Monitoring and Latency

* **Latency, retries, and time-outs**
  * For the best experience, your endpoint needs to be able to keep up with incoming webhook events. We automatically retry failed deliveries, but if an endpoint continues to fail, we may disable the associated events. We recommend checking your event subscriptions from time to time to ensure they remain active.
* **Deduplication**
  * Payloads aren't always deduplicated. It's best practice to implement deduplication logic at your destination.

***

## Troubleshooting

{% hint style="success" %}
**Test your endpoint**

We recommend you always test your webhook in Material using the [**Send Test Event**](#two-configure-the-webhook-in-material) button.
{% endhint %}

#### Common Errors

| Error Code | Description  | Solution                                                                                                                     |
| ---------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| 400        | Bad Request  | Confirm your payload fields are formatted correctly for your tool's requirements                                             |
| 401        | Unauthorized | Verify token                                                                                                                 |
| 403        | Forbidden    | Check your headers in the payload to confirm correct permissions and roles including bearer tokens                           |
| 404        | Not found    | Verify your endpoint URL                                                                                                     |
| 408        | Timeout      | Double check your endpoint and tool requirements                                                                             |
| 502        | Bad Gateway  | Verify your endpoint URL, any intermediate infrastructure such as a load balancer or proxy, and your webhook implementation. |
| 504        | Timeout      | Double check your endpoint and tool requirements                                                                             |

## FAQ

#### **Why is my webhook or event subscription auto-disabling?**

This is often because the delivery failed multiple times and triggered the auto-disable. Review your webhook configuration and endpoint to confirm everything is still set up correctly.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.material.security/integrations/integration-fundamentals/custom-webhooks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
