> For the complete documentation index, see [llms.txt](https://developer.musehub.com/muse-partners-help/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.musehub.com/muse-partners-help/resources/web-hooks.md).

# Web Hooks

{% hint style="info" %}
Applies only to subscription-based products that rely upon a server component.
{% endhint %}

The Web Hooks solution complements the Muse SDK. This feature allows our system to proactively notify your server component about any changes in subscription status in real-time. When a subscription status update occurs, our system will send a POST request to a predetermined endpoint on your server, containing the updated information, including the UUID associated with the subscription. This approach not only ensures that you receive timely updates, but also provides an additional layer of verification. By cross-referencing the UUID received through the Web Hook with the one obtained via the Muse SDK, you can confidently confirm the subscription status changes.

## Creating web hooks

Web Hooks are configured in the [Muse Partner Portal](https://partner.musehub.com).

In the left pane, head to Developer > Web Hooks. By clicking **Add** you can add a Customer Subscription Updated event and specify the endpoint on your server, for instance `https://example.com:4005/my-endpoint`.

The web hook can be activated or deactivated anytime via the **Active** toggle switch on this page.

After saving the web hook, you'll receive a `secret key` that is needed to securely authenticate the received data on your server. This secret can be rotated at your convenience via the dedicated **Rotate secret** button, for added security.

From this page, you can also test the endpoint implementation on your server via the handy **Send test event** button. Just fill in the message data and click **Send**.

## Events and payloads

Each web hook event carries data for one of more subscription changed events and is protected by an authenticated signature.

Currently, two types of webhooks can be configured: "Customer subscription updated" and "Product purchase/refund".

### Notification format

Here are example of webhook POST requests in JSON format.

#### Type: Customer subscription updated

```
POST xxxxxx HTTP/1.1
Host: yyyyyy
Content-Type: application/json
Content-Length: zzz
Authorization: HMAC-SHA256 Signature=9cbc49c036ec7e8358bef8483ab0d51c6619f3e7a1a9f0e75838d41ff368f728

[
  {
    "UUID": "342c682c42084803abeee6641e085859",
    "product_id": "4979b080-ff1b-4dc9-8804-b37c8b8ad59a",
    "SKU": "optional-partner-given-id"
    "subscription_option": "optional-partner-given-id"
    "event_type": <EVENT_TYPE_SUB>,
    "event_time": "2024-04-19T10:30:00Z",
    "next_event_time": "2024-05-18T10:30:00Z",
  }
]
```

#### Type: Product purchase/refund

```
POST xxxxxx HTTP/1.1
Host: yyyyyy
Content-Type: application/json
Content-Length: zzz
Authorization: HMAC-SHA256 Signature=9cbc49c036ec7e8358bef8483ab0d51c6619f3e7a1a9f0e75838d41ff368f728

[
  {
  "UUID": "3cbaa6860d2aa3b5adb357fa324442b9",
  "product_id": "133e845a-18e7-46d8-a3fd-69e84bb2d08e",
  "SKU": "optional-partner-given-id",
  "event_type": <EVENT_TYPE_PROD>,
  "event_time": "2025-05-06T10:38:52.813Z",
  "product_version_type": <PROD_TYPE>
  }
]
```

Body parameters:

* `UUID`: Unique User ID, as returned by the Muse SDK (max 32 bytes).
* `product_id` of the product for which the notification was triggered, visible in the product page on the Muse Partner Portal.
* `SKU` is a custom mnemonic field for your convenience. You can optionally provide it in the product page on the Muse Partner Portal.
* `subscription_option` is also an optional text field associated with a subscription type that you can provide for each subscription on the Muse Partner Portal. This will allow you to discern the plan and also upgrades / downgrades if you have more than one type.
* `event_type` depends on the trigger and can be any string of:
  * \<EVENT\_TYPE\_SUB>: `TrialStarted`, `TrialExpired`, `SubscriptionRenewed` (also for first subscription start), `SubscriptionCancelled` (continue until original renewal date), `SubscriptionRefunded` (terminate immediately, also comprises any other immediate cancellations related to failed or disputed payment).
  * \<EVENT\_TYPE\_PROD>: `Purchased` or `Refunded`&#x20;
* `product_version_type` can be `Release` or `Beta` according to the product type triggering the event. This allows to simulate purchases and refunds by adding or removing users to/from private Beta groups and discerning them from non-test events.
* `event_time` always refers to the instant the notification triggered (UTC).
* `next_event_time` is also a datetime UTC that depends on `event_type`:
  * `TrialStarted` : future time of trial expiry
  * `TrialExpired` : n.a.
  * `SubscriptionRenewed` : next renewal time
  * `SubscriptionCancelled` : original expiry time of current subscription plan (continue till the end)
  * `SubscriptionRefunded` : same as `event_time` (immediate suspension)

Additionally, for subscribing or purchasing users who have opted-in to sharing their contact with developers, the two webhook notifications above may contain the following additional fields: `email` , `name` , `picture_url`.

### Message validation

HTTP POST payloads that are delivered to your configured URL endpoint will contain an `Authorization` header like this one:

```
Authorization: HMAC-SHA256 Signature=9cbc49c036ec7e8358bef8483ab0d51c6619f3e7a1a9f0e75838d41ff368f728
```

This is the HMAC base64 digest of the request body, and is generated using the SHA-256 hash function and the previously described `secret key` displayed in the Partner Portal as the HMAC key. By calculating the HMAC on the request body and comparing your result against the `Signature` value, you ensure the authenticity of the received data. It is possible to calculate the HMAC using common functions provided by server-side languages, for instance node.js’ `crypto.createHmac`.

### Delivery

Whenever a subscription event happens, our system will send a POST notification to the endpoint you specified. Your server shall reply with a 200 status code to confirm reception.

In case of failed deliveries (i.e. your server is busy or unreachable), our system will retry sending with an [exponential backoff time](https://exponentialbackoffcalculator.com/) configured as follows: interval=10s, max retries=40, exponential=1.25 (\~20 attempts in the first hour, stops after \~3 days).
