> ## Documentation Index
> Fetch the complete documentation index at: https://docs.outkept.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Send real-time phishing events to your own endpoint.

Webhooks let the platform send real-time event notifications to a system of your choice.
Whenever a relevant event occurs, such as a user clicking a simulation link or reporting a
simulation, we send an HTTP POST request containing structured event data to a URL you
configure.

This lets you integrate with your own tooling, such as a SIEM, an HR system, a ticketing tool
or a custom dashboard, without polling an API.

## Supported events

| Event type | When it fires |
| - | - |
| `phishing.opened` | A user opened a simulation email |
| `phishing.clicked` | A user clicked the phishing link in a simulation email |
| `phishing.attachment_opened` | A user opened an attachment of a simulation email |
| `phishing.data_submitted` | A user submitted data on a phishing landing page |
| `phishing.replied` | A user replied to a simulation email |
| `phishing.reported` | A user reported the simulation email via the report button |

All of these are sent to your endpoint. You cannot subscribe to a subset.

<Note>
  Only the first occurrence of an event is sent. If a user clicks the same simulation twice,
  you receive one `phishing.clicked` event. Bot events are never sent.
</Note>

## Before you start

You need:

* Access to the platform with an **Administrator** or **Partner** role
* A publicly accessible HTTPS endpoint that can receive POST requests

<Warning>
  Non-TLS endpoints are not supported. The URL must be `https://`.
</Warning>

## Add a webhook

<Steps>
  <Step title="Open the webhooks settings">
    Go to **Settings** then **Integrations** then **Webhooks**.
  </Step>

  <Step title="Add the webhook">
    Click **Add webhook** and enter your endpoint URL. It must be a valid `https://` URL that
    is publicly reachable.
  </Step>

  <Step title="Confirm">
    Click **Add webhook** to save it.
  </Step>

  <Step title="Copy the signing secret">
    A signing secret is displayed. Copy it now, then click **I've copied the secret**.
  </Step>
</Steps>

<Warning>
  The signing secret is shown once and cannot be retrieved later. Store it securely before
  closing the dialog.
</Warning>

To test the integration, send a test mail under **Emails** then **Tests** and interact with
it.

## Event payload

Events are delivered as JSON in the body of an HTTP POST request.

| Header | Value |
| - | - |
| `Content-Type` | `application/json` |
| `X-Signature` | HMAC-SHA256 signature of the payload, see [Verifying authenticity](#verifying-authenticity) |
| `X-Signature-Timestamp` | Timestamp of when the request was signed |

Example payload:

```json theme={null}
{
  "event_type": "phishing.clicked",
  "email": "test@outkept.com",
  "source": "OutKept",
  "timestamp": "2026-06-09T14:30:45.123Z",
  "nonce": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
```

| Field | What it holds |
| - | - |
| `event_type` | The type of event, from the table above |
| `email` | Email address of the person who performed the action |
| `source` | Always `OutKept` |
| `timestamp` | ISO 8601 date string of when the event happened |
| `nonce` | Random UUID, use it to deduplicate incoming requests |

## Verifying authenticity

Every request includes an `X-Signature` header. This is an HMAC-SHA256 signature computed
over the raw request body using your signing secret as the key.

<Steps>
  <Step title="Read the raw body">
    Take the raw request body, before parsing the JSON.
  </Step>

  <Step title="Compute the signature">
    Compute HMAC-SHA256 over the raw body using your signing secret.
  </Step>

  <Step title="Compare">
    Compare your result to the value in the `X-Signature` header.
  </Step>

  <Step title="Accept or reject">
    If they match, the request is authentic and you can process it. If they do not match,
    reject it with a 401 response.
  </Step>
</Steps>

<Warning>
  Always use a timing-safe comparison function. A plain string comparison leaks information
  through its timing and can be used to forge a signature.
</Warning>

## Retries

If your endpoint does not return an HTTP 2xx response within 10 seconds, delivery is retried
automatically. After 5 failed attempts the delivery is marked as **Failed**.

## Removing a webhook

<Steps>
  <Step title="Open the webhooks settings">
    Go to **Settings** then **Integrations** then **Webhooks**.
  </Step>

  <Step title="Delete it">
    Click **Delete webhook**, then confirm in the dialog.
  </Step>
</Steps>

Removing a webhook stops all future deliveries. Events that occur while no webhook is active
are not queued and are not delivered retroactively.

## Troubleshooting

| Issue | Solution |
| - | - |
| Test event not received | Confirm the endpoint is publicly accessible over HTTPS and returns a 2xx status |
| Signature verification fails | Compute the HMAC over the raw, unparsed request body rather than the parsed JSON |
| Endpoint timing out | Respond with 200 immediately and process the payload asynchronously, to stay within 10 seconds |
| Duplicate events | Use the `nonce` in the body to deduplicate incoming requests |

For anything not listed here, contact [support@outkept.com](mailto:support@outkept.com) with
your organisation name and your endpoint URL.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.