# Webhooks

Use the "Create Webhook Subscriber" endpoint and pass in a `callbackUrl` on your system that will receive a POST request for all of events listed below.&#x20;

Once the webhook subscriber is in place, you can have it choose to act on or disregard each of the following types of events.

### Webhooks Events

**Merchant events**

* `merchant.updated`
* `merchant.reinstall`

**Customer events**

* Account
  * `customer.created`
  * `customer.updated`
  * `customer.credits.updated`&#x20;
* Orders and Payments
  * `customer.ordered`&#x20;
  * `customer.resubscribed`
  * `customer.payment_succeeded`
  * `customer.payment_failed`
  * `customer.payment_methods.updated`
  * `customer_billing.attempt.failed`
* Cancellations **–**[ **more details below.**](#anecdote-cancellation-events)
  * `customer.cancellation_requested`&#x20;
  * `customer.confirmed_cancellation`&#x20;
  * `customer.cancelled`&#x20;
  * `customer.merchant_cancel`

### Create a Webhook Subscriber

**The `token` field in the response body for the Create Webhook Subscriber endpoint is important to take note of.** For security reasons, it only appears here, and in the body of post requests for events forwarded from the Inveterate system to the subscriber's `callbackUrl`. This will allow your 3rd party app to make sure the requests made to your endpoint are legitimate. Simply store the `token` value you get when you create the subscriber, and check for incoming events on your `callbackUrl` for a matching token in the request body.

## Create Webhook Subscriber

<mark style="color:green;">`POST`</mark> `https://public.inveterateapi.com/webhooks`

#### Headers

| Name                                                   | Type   | Description                                                                                            |
| ------------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------ |
| X-Inveterate-Api-Key<mark style="color:red;">\*</mark> | String | Required to access all protected endpoints on this API. Obtained from merchant's Inveterate dashboard. |

#### Request Body

| Name                                          | Type   | Description                                                                                                   |
| --------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------- |
| callbackUrl<mark style="color:red;">\*</mark> | String | The URL which will receive a POST request whenever an event should be passed from Inveterate to a subscriber. |
| name                                          | String | Descriptive name to help identify the subscriber.                                                             |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
    "success": true,
    "data": {
        "subscriber": {
            "merchantId": "inveterate-staging-barefoot",
            "id": "215498bda8d2169cc5db763377c0eda2",
            "token": "fe10df8a669462ee3ae368b0bfe5f910",
            "callbackUrl": "https://ztvfqj22g8.execute-api.us-east-1.amazonaws.com/prod/webhooks/callback-test",
            "name": "Webhook Subscriber Endpoint Test",
            "dateCreated": "2022-03-18T07:45:22.602Z",
            "dateUpdated": "2022-03-18T07:45:22.602Z",
            "createdByApp": "Default"
        }
    },
    "errors": []
}
```

{% endtab %}
{% endtabs %}

### Get Webhook Subscribers

You may have wondered about the `createdByApp` field in the response body for the "Create Webhook Subscriber" request. This is derived from the `app` field which is attached behind the scenes to your API key (the `X-Inveterate-Api-Key` one). For example, let's say you work at a SaaS company called "Smithy", and are developing an integration with Inveterate. Then the API key you are issued to access the Inveterate API will have an `app` field value of `Smithy`, and all the webhook subscriber objects created with your API key will have the same `createdByApp` value.

**TLDR: The "Get Webhook Subscribers" endpoint responds with all the webhook subscribers associated with the `X-Inveterate-Api-Key` included in the request.** Note that this is not necessarily all of the webhook subscribers associated with a particular merchant, but a particular app.

## Get Webhook Subscribers

<mark style="color:blue;">`GET`</mark> `https://public.inveterateapi.com/webhooks`

Get all webhook subscribers associated with this `X-Inveterate-Api-Key` value.

#### Headers

| Name                                                   | Type   | Description                                                                                            |
| ------------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------ |
| X-Inveterate-Api-Key<mark style="color:red;">\*</mark> | String | Required to access all protected endpoints on this API. Obtained from merchant's Inveterate dashboard. |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
    "success": true,
    "data": {
        "subscribers": [
            {
                "merchantId": "inveterate-staging-barefoot",
                "id": "215498bda8d2169cc5db763377c0eda2",
                "callbackUrl": "https://ztvfqj22g8.execute-api.us-east-1.amazonaws.com/prod/webhooks/callback-test",
                "name": "Webhook Subscriber Endpoint Test",
                "dateCreated": "2022-03-18T07:45:22.602Z",
                "dateUpdated": "2022-03-18T07:45:22.602Z",
                "createdByApp": "Default"
            }        
        ]
    },
    "errors": []
}
```

{% endtab %}
{% endtabs %}

## Delete Webhook Subscriber

<mark style="color:red;">`DELETE`</mark> `https://public.inveterateapi.com/webhooks`

#### Headers

| Name                                                   | Type   | Description                                                                                            |
| ------------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------ |
| X-Inveterate-Api-Key<mark style="color:red;">\*</mark> | String | Required to access all protected endpoints on this API. Obtained from merchant's Inveterate dashboard. |

#### Request Body

| Name                                 | Type   | Description        |
| ------------------------------------ | ------ | ------------------ |
| id<mark style="color:red;">\*</mark> | String | The subscriber id. |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
    "success": true,
    "data": {
        "subscriber": {}
    },
    "errors": []
}
```

{% endtab %}
{% endtabs %}

### Anecdote: Cancellation Events

Customer cancellation events follow different patterns depending on the cause of cancellation (payment failure or customer request) and, for the latter, also the store's cancellation policy.&#x20;

Please take a look at the trees of events below to see which cancellation lifecycle should be expected for a given customer, given the circumstances of their cancellation.

#### Event Timeline: Cancellation due to payment failure

Let's say the customer's payment was due on Jan 10, it failed then. Two more follow-up attempts will be performed over the next two days. If these two fail as well, then the customer will be automatically cancelled and will lose access to their benefits.

**Jan 10**

* **Event trigger:** `customer_billing.attempt.failed`&#x20;
  * If enabled in merchant's messaging, user also gets an e-mail communicating them of the payment failure and informing them that they have three days to update their payment methods.

**Jan 11**

* **Event trigger:** `customer_billing.attempt.failed`&#x20;

**Jan 12**

* **Event trigger:** `customer_billing.attempt.failed` , `customer.payment_failed`
* Customer gets cancelled and loses access to their benefits
  * if cancellation policy is immediately,  customer `status`: `ACTIVE` -> `CANCELLED`. Then `customer.cancelled`  event is triggered.&#x20;
  * &#x20;if cancellation policy is end of billing cycle,  customer `status`: `ACTIVE` -> `PENDING_CANCELLATION` . Then on the same day or the next day, customer gets cancelled and `customer.cancelled` event is triggered.&#x20;

#### Event Timelines: Cancellation due to customer request

*For stores with cancellation policy set to **immediate:***

* Customer requests cancellation
  * Event trigger: `customer.cancellation_requested`
* Confirmation e-mail is sent, asking customer to confirm
  * If user confirms e-mail
    * Event trigger: `customer.confirmed_cancellation`
    * Customer gets cancelled and loses access to their benefits
      * `status`: `ACTIVE` -> `CANCELLED`
    * Email gets sent: `Confirmed cancellation`
    * Event trigger: `customer.cancelled`

*Stores with cancellation policy set to **end of billing cycle:***

* Customer requests cancellation
* Event trigger: `customer.cancellation_requested`
* Confirmation e-mail is sent, asking customer to confirm
* If user confirms e-mail
  * Event trigger: `customer.confirmed_cancellation`
  * Customer update: `status` from `ACTIVE` -> `PENDING_CANCELLATION`
  * Email gets sent: `Confirmed cancellation`
  * When customer's billing cycle ends
    * Customer gets cancelled and loses access to their benefits
      * `status` from `PENDING_CANCELLATION` -> `CANCELLED`
    * Event trigger: `customer.cancelled`


---

# 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://help.inveterate.com/dev/api-reference-docs/legacy-public-api-reference/webhooks.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.
