# Webhook Notifications

## Receive event notifications with webhooks

VGS now uses webhooks when an event happens in your organization. Available webhook events today are particularly useful for tracking events like when someone changes or updates a live route, or tracking proxy upstream error occurrences.

Begin using webhooks with VGS integration in just three steps:

1. Create a webhook endpoint on your server
2. Add an endpoint in the VGS Dashboard
3. Complete your integration by adding events

### Set up instructions

<details>

<summary>Setting up Notifications in the Vault Dashboard </summary>

To set up a webhook, go to the **Administration** section in the left side navigation > **Organization Settings** > **Notifications** dashboard page, click **Add Notifications** on the right to reveal a form to add an endpoint for receiving events. &#x20;

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

You can enter any valid and existing URL as the destination for events. After you have added an endpoint, you’ll be able to add/select individual events.&#x20;

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

Pick events from the dropdown list, and they will immediately appear in the **Events List**. Once an event is added to the list, you can set up resources (vaults, users) associated with it. If none is selected, notifications will be triggered for all of them.&#x20;

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

</details>

<details>

<summary>Setting up Notifications in the CMP Dashboard (preferred)</summary>

To set up Notifications in the CMP Dashboard, go Settings > Organization > Notifications.&#x20;

* If your Organization **is not** enabled for notifications, the dashboard will display a message with guidance to contact support.&#x20;
* If your Organization **is** enabled for Notifications, the dashboard will display a table view of any existing notifications. Click the "Create new" call to action to kick of the new notification flow.

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

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

Clicking into a notification will surface a detailed view of the notification, along with capabilities to edit, delete, and alter the active/inactive status of the notification.

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

When creating a new notification, or editing an existing event listed for a notification, the user will be prompted to define both the event, as well as its source. Events are sorted into categories according to the product or service it relates to.&#x20;

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

Once an event is selected, the user can define the source of the event. Event sources can include CMP accounts, individual vaults/tenants (for which one exists for every CMP account), users, or organizations. Each eligible event source will also denote whether the source exists in a Sandbox, or Live environment.

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

</details>

### List of available notifications events

<table><thead><tr><th width="178.65234375">Event</th><th width="128.03125">Scope</th><th width="162.2578125">Grouping</th><th>Details</th></tr></thead><tbody><tr><td>route.created</td><td>VAULT</td><td>EVERY_SINGLE</td><td><ul><li>route_id</li><li>route_name</li><li>environment</li></ul></td></tr><tr><td>route.updated</td><td>VAULT</td><td>EVERY_SINGLE</td><td><ul><li>route_id</li><li>route_name</li><li>environment</li></ul></td></tr><tr><td>route.delete</td><td>VAULT</td><td>EVERY_SINGLE</td><td><ul><li>route_id</li><li>route_name</li><li>environment</li></ul></td></tr><tr><td>vault.created</td><td>ORGANIZATION</td><td>EVERY_SINGLE</td><td><ul><li>vault_id</li><li>vault_name</li><li>environment</li></ul></td></tr><tr><td>au_card.updated</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>old_account_number</li><li>old_expiration_date</li><li>new_expiration_date</li><li>occurred_at</li></ul></td></tr><tr><td>au_card.expired</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>old_account_number</li><li>old_expiration_date</li><li>new_expiration_date</li><li>occurred_at</li></ul></td></tr><tr><td>au_card.closed</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>old_account_number</li><li>occurred_at</li></ul></td></tr><tr><td>au_card.non_participating</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>occurred_at</li></ul></td></tr><tr><td>au_card.contact_cardholder_advice</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>old_account_number</li><li>old_expiration_date</li><li>occurred_at</li></ul></td></tr><tr><td>au_card.unknown</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>occurred_at</li></ul></td></tr><tr><td>au_card.enrolled</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>occurred_at</li></ul></td></tr><tr><td>au_card.opt_out</td><td></td><td>EVERY_SINGLE</td><td><ul><li>card_id</li><li>occurred_at</li></ul></td></tr><tr><td>user.permissions_updated</td><td>USER</td><td>EVERY_SINGLE</td><td><ul><li>user_email</li><li>org_id</li><li>list&#x3C;permission></li></ul></td></tr><tr><td>user.permissions_deleted</td><td>USER</td><td>EVERY_SINGLE</td><td><ul><li>user_email</li><li>org_id</li><li>list&#x3C;permission></li></ul></td></tr><tr><td>user.logged_in</td><td>USER</td><td>EVERY_SINGLE</td><td><ul><li>user_ip</li><li>user_email</li></ul></td></tr><tr><td>user.password_updated</td><td>USER</td><td>EVERY_SINGLE</td><td><ul><li>user_ip</li><li>user_email</li></ul></td></tr><tr><td>user.mfa_created</td><td>USER</td><td>EVERY_SINGLE</td><td><ul><li>user_ip</li><li>user_email</li></ul></td></tr><tr><td>user.mfa_deleted</td><td>USER</td><td>EVERY_SINGLE</td><td><ul><li>user_ip</li><li>user_email</li></ul></td></tr><tr><td>proxy.upstream_error</td><td>VAULT</td><td>EXP_REPEAT</td><td><ul><li>upstream_host</li><li>upstream_error</li><li>environment</li></ul></td></tr><tr><td>alias.reveal_failed</td><td>VAULT</td><td>EXP_REPEAT</td><td><ul><li>environment</li><li>storage</li></ul></td></tr></tbody></table>

## Managing Webhooks

Existing webhook endpoints can be updated or deleted in the Dashboard’s Notifications section. You also have the option of disabling a webhook endpoint temporarily. VGS does not retry any notifications that are generated while the endpoint has been disabled. For every webhook integration, a unique secret is generated, it is used to verify requests on the client-side.

### Webhook Delivery Schedule

VGS will attempt to deliver a webhook 8 times. If all webhook delivery attempts receive non-200 responses, the webhook delivery will be cancelled. The delivery attempt schedule is detailed below:

* Immediately
* 5 seconds
* 5 minutes
* 30 minutes
* 2 hours
* 5 hours
* 10 hours
* 10 hours (in addition to the previous)

### Webhook Grouping

Some frequent events like `proxy.upstream_error` or `record.reveal_failed` are grouped to prevent spamming the delivery server. In this case, only 1st, 10th, 100th, 1000th, etc, are being sent for 5-minute time windows. The `occurrence` field in a webhook payload contains a number of those grouped events.

### Webhook Signatures

Each webhook request contains a unique signature within the HTTP header `vgs-signature` to verify the request’s VGS origin.

Example:

```vgs-signature
vgs-signature:t=1623691785,v0=9370aa8513da0a14d21478f11f039708f7e664bfffebb939f574e57818ce08de
```

`t` - UTC timestamp, it must be within a typical 60s delivery time window\
`v0` - request signature itself<br>

The signature is a sha256 hash of a raw request body. Python verification example below:

```python
import hashlib
import hmac
import logging
import time\n\n
TIMSTAMP_DIFF_TOLERANCE = 60\n\n
def check_signature(secret: str, signature: str, body: bytes) -> bool:
    chunks = dict(p.split("=") for p in signature.split(","))
    if abs(int(chunks["t"]) - time.time()) > TIMSTAMP_DIFF_TOLERANCE:
        logging.warning("Timestamp mismatch")
        return False\n
    msg = chunks["t"].encode() + b"." + body
    mac = hmac.new(secret.encode("utf-8"), msg=msg, digestmod=hashlib.sha256)
    if mac.hexdigest() != chunks["v0"]:
        logging.warning("Signature mismatch")
        return False\n
    return True

```

Please note: the 60s timestamp tolerance is very strict. If you require flexibility and/or want to decrease the chance the likelihood of webhook request rejection, it is possible to increase this limit to the allowance you are comfortable with.&#x20;

Additionally, upon retry, the webhook request will be sent again with the same timestamp. If you have very narrow timestamp 60s, a retry that happens after 30 minutes will be rejected.

### IP List

The following is a list of IPs that VGS notifications will be arriving from:

* 44.228.126.217
* 50.112.21.217
* 52.24.126.164
* 54.148.139.208
* 2600:1f24:64:8000::/52
* 54.164.207.221
* 54.90.7.123
* 2600:1f28:37:4000::/52

### Webhook Integration Limits

Currently, we support only the webhook delivery method, and there is a general limit of **20 integrations** per organization. This number can be increased by sending a request to the VGS support team.


---

# Agent Instructions: 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:

```
GET https://docs.verygoodsecurity.com/enterprise-platform/developer-resources/webhook-notifications.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
