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

# Integrate Outgoing Webhooks with Phare

> Receive alerts by webhooks on third-party tools or build your own integration.

Want to build your own custom integration or connect Phare to a tool that isn’t officially supported? Outgoing webhooks are your best friend! They send HTTP callbacks with your alert data to any endpoint you specify, perfect for DIY integrations, logging or connecting to specialized systems.

## Configuration

Setting up an outgoing webhook is straightforward, you just need two pieces of information:

* **Callback URL**: Where should the data be sent? This could be an endpoint in your own application or a third-party service that accepts webhooks.
* **Signing secret**: A secret key that proves the data came from Phare and wasn't tampered with along the way.

A secure signing secret is automatically generated for you, but you're welcome to use your own if you prefer.

<Frame>
  <img src="https://mintcdn.com/phare/Jh1uiqHFo-IwwO2b/images/integrations/outgoing-webhook/outgoing-webhook-installation.webp?fit=max&auto=format&n=Jh1uiqHFo-IwwO2b&q=85&s=5d5c7ec3e14be651a8a6a3e15737006c" alt="Outgoing webhooks installation" width="2526" height="1882" data-path="images/integrations/outgoing-webhook/outgoing-webhook-installation.webp" />
</Frame>

## Using the integration

Once your webhook is set up, you can connect it to alert rules in your projects. A default JSON payload based on the alert event you've chosen will be generated, customize it as needed to fit your specific use case.

<Frame>
  <img src="https://mintcdn.com/phare/PK6OfMSytvlvN_a7/images/integrations/outgoing-webhook/outgoing-webhook-alert-rule.webp?fit=max&auto=format&n=PK6OfMSytvlvN_a7&q=85&s=f97358c7d1f3ad0d4d93e59ec06055c8" alt="Outgoing webhooks alert rule" width="2622" height="2876" data-path="images/integrations/outgoing-webhook/outgoing-webhook-alert-rule.webp" />
</Frame>

## Delivery details

Outgoing webhooks are sent as `POST` requests with a 30-second timeout. Each request includes:

* `Content-Type: application/json`
* `User-Agent: Phare 1.0 (+https://phare.io)`
* `X-Phare-Request-Id`: Unique delivery ID
* `X-Phare-Request-Event`: Alert event key
* `X-Phare-Request-Timestamp`: Unix timestamp
* `X-Phare-Request-Signature`: HMAC-SHA256 signature

The request body is the rendered JSON schema from the Payload customization section below.

## Payload customization

<Frame>
  <iframe width="704" height="396" frameborder="0" title="Outgoing webhook editor" src="https://player.mediadelivery.net/embed/238508/f523918c-9885-42e1-8b8b-01b1f001e42d?autoplay=false&loop=false&muted=false&preload=true&responsive=true" loading="lazy" allow="accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture;fullscreen" allowfullscreen />
</Frame>

You can customize the payload that will be sent to your webhook endpoint by defining a valid JSON schema with placeholders like `{{ incident.slug }}`. When the webhook is sent, Phare swaps the placeholders with real values.

<Info>JSON keys do not support placeholders.</Info>

### Example template

```json theme={null}
{
  "event": "uptime.monitor.created",
  "monitor": {
    "id": "{{ monitor.id }}",
    "name": "{{ monitor.name }}",
    "status": "{{ monitor.status }}",
    "protocol": "{{ monitor.protocol }}",
    "request": "{{ monitor.request }}",
    "regions": "{{ monitor.regions }}",
    "interval": "{{ monitor.interval }}",
    "incident_confirmations": "{{ monitor.incident_confirmations }}",
    "recovery_confirmations": "{{ monitor.recovery_confirmations }}"
  },
  "project": {
    "id": "{{ project.id }}",
    "name": "{{ project.name }}",
    "slug": "{{ project.slug }}"
  }
}
```

### Looping over list of entities

To define a schema for an entity that is part of a list, use `$each` and `$item`. The loop name should match the entity list name you want to iterate, such as `affected_monitors`. In the entity schema, use the `item` variable to access the current item properties.

```json theme={null}
{
  "affected_monitors": {
    "$each": "affected_monitors",
    "$item": {
      "id": "{{ item.id }}",
      "name": "{{ item.name }}"
    }
  }
}
```

Nested loops are not currently supported.

### Available entities by event

<AccordionGroup>
  <Accordion title="Monitor Created">
    * `monitor`
    * `project`
  </Accordion>

  <Accordion title="Monitor Deleted">
    * `monitor`
    * `project`
  </Accordion>

  <Accordion title="Monitor Certificate Discovered">
    * `certificate`
    * `monitor`
    * `project`
  </Accordion>

  <Accordion title="Monitor Certificate Expiring">
    * `certificate`
    * `monitor`
    * `project`
  </Accordion>

  <Accordion title="Incident Created">
    * `incident`
    * `project`
    * `affected_monitors` (list)
  </Accordion>

  <Accordion title="Incident Recovered">
    * `incident`
    * `project`
    * `affected_monitors` (list)
  </Accordion>

  <Accordion title="Incident Propagated">
    * `incident`
    * `project`
    * `propagated_monitor` (the newly affected monitor)
    * `affected_monitors` (list)
  </Accordion>

  <Accordion title="Incident Partially Recovered">
    * `incident`
    * `project`
    * `recovered_monitor` (the recovered monitor)
    * `affected_monitors` (list)
  </Accordion>

  <Accordion title="Incident Comment Created">
    * `comment`
    * `creator`
    * `incident`
    * `project`
    * `affected_monitors` (list)
  </Accordion>

  <Accordion title="Incident Update Published">
    * `update`
    * `creator`
    * `incident`
    * `project`
    * `affected_monitors` (list)
  </Accordion>
</AccordionGroup>

### Available fields by object

<AccordionGroup>
  <Accordion title="incident">
    | Field         | What it means        |
    | :------------ | :------------------- |
    | `id`          | Unique incident ID   |
    | `slug`        | Short incident code  |
    | `title`       | Incident title       |
    | `description` | Incident description |
    | `state`       | Current state        |
    | `status`      | Current status       |
    | `impact`      | Impact level         |
  </Accordion>

  <Accordion title="monitor">
    | Field                    | What it means                                 |
    | :----------------------- | :-------------------------------------------- |
    | `id`                     | Unique monitor ID                             |
    | `name`                   | Monitor name                                  |
    | `status`                 | Current status                                |
    | `protocol`               | Protocol type                                 |
    | `request`                | Request details                               |
    | `regions`                | Regions used for checks                       |
    | `interval`               | Check interval (seconds)                      |
    | `incident_confirmations` | Confirmations required to open an incident    |
    | `recovery_confirmations` | Confirmations required to resolve an incident |
  </Accordion>

  <Accordion title="project">
    | Field  | What it means |
    | :----- | :------------ |
    | `id`   | Project ID    |
    | `name` | Project name  |
    | `slug` | Project slug  |
  </Accordion>

  <Accordion title="certificate">
    | Field                       | What it means             |
    | :-------------------------- | :------------------------ |
    | `serial_number`             | Certificate serial number |
    | `subject_common_name`       | Common name               |
    | `subject_alternative_names` | Alternative names         |
    | `issuer_common_name`        | Issuer name               |
    | `issuer_organization`       | Issuer organization       |
    | `not_before`                | Valid from                |
    | `not_after`                 | Valid until               |
  </Accordion>

  <Accordion title="comment">
    | Field     | What it means   |
    | :-------- | :-------------- |
    | `id`      | Comment ID      |
    | `content` | Comment content |
  </Accordion>

  <Accordion title="update">
    | Field     | What it means  |
    | :-------- | :------------- |
    | `id`      | Update ID      |
    | `state`   | Update state   |
    | `content` | Update content |
  </Accordion>

  <Accordion title="creator">
    | Field   | What it means |
    | :------ | :------------ |
    | `type`  | Creator type  |
    | `label` | Display label |
  </Accordion>
</AccordionGroup>

## Webhook security

Trust but verify! Phare outgoing webhooks come with built-in security through HMAC-SHA256 signatures. This ensures the payloads you receive:

1. Actually came from Phare
2. Haven't been tampered with in transit
3. Aren't being replayed from previous requests

To verify a webhook's authenticity, compute the HMAC-SHA256 of this concatenated string:

```text theme={null}
{version}:{timestamp}:{payload}
```

The version is always `v0` (this will be bumped if the algorithm ever changes). You'll find the timestamp in the `X-Phare-Request-Timestamp` header and the signature in the `X-Phare-Request-Signature` header.
You can also use `X-Phare-Request-Id` and `X-Phare-Request-Event` to correlate deliveries.

Here are some code examples to help you implement verification:

<CodeGroup>
  ```javascript Node.js theme={null}
  const crypto = require('crypto');

  app.post('/webhook', (req, res) => {
      const secret = 'your-signing-secret';
      const version = 'v0';
      const timestamp = req.headers['x-phare-request-timestamp'];
      const signature = req.headers['x-phare-request-signature'];

      const payload = version + ':' + timestamp + ':' + JSON.stringify(req.body);

      const hash = crypto.createHmac('sha256', secret)
          .update(payload)
          .digest('hex');

      if (hash === signature) {
          console.log('Signature verified');
      } else {
          console.log('Signature verification failed');
      }
  })
  ```

  ```php PHP theme={null}
  <?php

  $secret = 'your-signing-secret';
  $version = 'v0';
  $timestamp = $_SERVER['HTTP_X_PHARE_REQUEST_TIMESTAMP'];
  $signature = $_SERVER['HTTP_X_PHARE_REQUEST_SIGNATURE'];

  $payload = $version . ':' . $timestamp . ':' . file_get_contents('php://input');

  $hash = hash_hmac('sha256', $payload, $secret);

  if ($hash === $signature) {
      echo 'Signature verified';
  } else {
      echo 'Signature verification failed';
  }
  ```
</CodeGroup>

<Tip>
  Pro security tip: Always check that the timestamp isn't too old, it is recommended
  to reject a webhook older than 5 minutes to prevent replay attacks.
</Tip>

## Retry policy

Network hiccups happen, which is why Phare outgoing webhooks don't give up easily. If your endpoint doesn't respond with a 2xx status code within 30 seconds, it will be retried up to four times (five attempts total) using an exponential backoff strategy:

1. First attempt: Immediate delivery
2. First retry: After 1 minute
3. Second retry: After 5 minutes
4. Third retry: After 10 minutes
5. Final retry: After 1 hour

This means that from first attempt to last retry, your webhook could arrive anytime within a 1 hour and 18 minute window (including timeouts).

## Debugging

Webhooks not working as expected? Logs are available with the request and response details making troubleshooting a breeze, you only need to click the row you would like to inspect.

<Frame>
  <img src="https://mintcdn.com/phare/Jh1uiqHFo-IwwO2b/images/integrations/outgoing-webhook/outgoing-webhook-logs.webp?fit=max&auto=format&n=Jh1uiqHFo-IwwO2b&q=85&s=bbd76fe67086e85daa5afe22a37acde9" alt="Outgoing webhooks logs" width="2598" height="1824" data-path="images/integrations/outgoing-webhook/outgoing-webhook-logs.webp" />
</Frame>
