# Webhook Events

## Available topics

{% hint style="info" %}
**Total Events Available:** 19 webhook topics covering all major loyalty program interactions
{% endhint %}

Joy Loyalty supports the following webhook topics for real-time event notifications:

| Topic                            | Description                      | Trigger                                                     |
| -------------------------------- | -------------------------------- | ----------------------------------------------------------- |
| `points/earned`                  | Customer earns points            | When points are added to customer account                   |
| `points/redeemed`                | Customer redeems points          | When points are used to redeem rewards                      |
| `points/expired`                 | Points expired                   | When customer's points expire and are deducted              |
| `points/about_to_expire_3_days`  | Points about to expire (3 days)  | 3 days before points expiration                             |
| `points/about_to_expire_7_days`  | Points about to expire (7 days)  | 7 days before points expiration                             |
| `points/about_to_expire_30_days` | Points about to expire (30 days) | 30 days before points expiration                            |
| `customer/status_changed`        | Customer status changed          | When customer joins, rejoins, or leaves loyalty program     |
| `reward/coupon_used`             | Coupon used                      | When customer uses a Joy coupon to place an order           |
| `tier/upgraded`                  | Customer tier upgraded           | When customer is upgraded to a higher tier                  |
| `tier/downgraded`                | Customer tier downgraded         | When customer is demoted to a lower tier                    |
| `tier/reset`                     | Tier reset                       | When customer's tier is reset according to scheduled reset  |
| `tier/about_to_reset_4_weeks`    | Tier about to reset (4 weeks)    | 4 weeks before tier reset                                   |
| `tier/about_to_reset_2_weeks`    | Tier about to reset (2 weeks)    | 2 weeks before tier reset                                   |
| `tier/about_to_reset_1_day`      | Tier about to reset (1 day)      | 1 day before tier reset                                     |
| `referral/link_created`          | Referral link created            | When customer creates a referral link for the first time    |
| `referral/reward_earned`         | Referral reward earned           | When referrer receives reward after referee completes order |
| `referral/referee_claimed`       | Referee claimed reward           | When referee claims coupon reward                           |
| `birthday/reward_earned`         | Birthday reward earned           | When customer receives birthday reward                      |
| `milestone/achieved`             | Milestone achieved               | When customer achieves a milestone                          |

## Webhook delivery

When a loyalty event occurs, Joy automatically sends a POST request to your registered webhook endpoints with the following characteristics:

* **HTTP Method**: POST
* **Content-Type**: application/json
* **Timeout**: 5 seconds maximum response time
* **Retry Logic**: Failed webhooks are retried with exponential backoff
* **Security**: All requests include HMAC signature for verification

### Request headers

Every webhook request from Joy includes these headers:

```http
Content-Type: application/json
X-Joy-Loyalty-Shop-Source-Id: your_shop_source_id
X-Joy-Loyalty-Hmac-Sha256: signature_hash
X-Joy-Loyalty-Topic: webhook_topic
```

**Header Descriptions:**

| Header                         | Description                                      |
| ------------------------------ | ------------------------------------------------ |
| `Content-Type`                 | Always set to `application/json`                 |
| `X-Joy-Loyalty-Shop-Source-Id` | Your shop's unique identifier in Joy system      |
| `X-Joy-Loyalty-Hmac-Sha256`    | HMAC-SHA256 signature for verifying authenticity |
| `X-Joy-Loyalty-Topic`          | The webhook topic that triggered this request    |

### Base payload structure

All webhook payloads include these common fields:

```json
{
  "webhookId": "unique_webhook_id",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "activity_id",
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

## Event payload examples

### Points events

#### points/earned

```json
{
  "topic": "points/earned",
  "shopDomain": "myshop.myshopify.com",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "data": {
    "customerId": "gid://shopify/Customer/123456",
    "email": "customer@example.com",
    "points": 100,
    "totalPoints": 1500,
    "source": "order",
    "orderId": "gid://shopify/Order/789"
  }
}
```

#### points/redeemed

```json
{
  "topic": "points/redeemed", 
  "shopDomain": "myshop.myshopify.com",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "data": {
    "customerId": "gid://shopify/Customer/123456",
    "email": "customer@example.com",
    "pointsRedeemed": 500,
    "remainingPoints": 1000,
    "rewardType": "discount",
    "rewardValue": "$5 OFF"
  }
}
```

#### points/expired

```json
{
  "webhookId": "activity_xyz789",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe", 
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "activity_xyz789",
  "oldPoint": 500,
  "newPoint": 0,
  "type": "expire_point",
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

#### points/about\_to\_expire

```json
{
  "webhookId": "about_to_expire_customer123_1705312200000",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "about_to_expire_customer123_1705312200000", 
  "type": "points_about_to_expire",
  "point": 500,
  "expiredAt": "2024-01-22T00:00:00.000Z",
  "daysUntilExpiration": 7,
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

### Customer events

#### customer/status\_changed

```json
{
  "topic": "customer/status_changed",
  "shopDomain": "myshop.myshopify.com",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "data": {
    "customerId": "gid://shopify/Customer/123456",
    "email": "customer@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "type": "user_joined",
    "tier": "Bronze"
  }
}
```

**Possible type values:**

* `user_joined`: Customer joins loyalty program for the first time
* `user_rejoined`: Customer rejoins after leaving
* `user_excluded`: Customer is excluded from loyalty program

### Reward events

#### reward/coupon\_used

```json
{
  "webhookId": "activity_xyz789",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "activity_xyz789",
  "type": "place_order_with_coupon",
  "couponCode": "JOY-ABC123",
  "orderId": 123456789,
  "orderNumber": 1001,
  "orderName": "#1001",
  "orderSubTotal": "99.99",
  "orderCurrency": "USD",
  "programTitle": "10% Off Discount",
  "redeemPoint": 500,
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

### Tier events

#### tier/upgraded

```json
{
  "webhookId": "activity_xyz789",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "activity_xyz789",
  "type": "update_tier",
  "oldTierId": "tier_bronze_123",
  "oldTierName": "Bronze",
  "newTierId": "tier_silver_456", 
  "newTierName": "Silver",
  "tierPoint": 1500,
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

#### tier/downgraded

```json
{
  "webhookId": "activity_xyz789",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "activity_xyz789",
  "type": "demote_tier",
  "oldTierId": "tier_gold_789",
  "oldTierName": "Gold",
  "newTierId": "tier_silver_456",
  "newTierName": "Silver", 
  "tierPoint": 800,
  "reason": "reassessment",
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

### Referral events

#### referral/link\_created

```json
{
  "webhookId": "referral_link_customer123_1705312200000",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "referral_link_customer123_1705312200000",
  "type": "referral_link_created",
  "referralCode": "abc123xyz",
  "urlReferral": "https://myshop.myshopify.com?ref=abc123xyz",
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

#### referral/reward\_earned

```json
{
  "webhookId": "referral_reward_customer123_1705312200000",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "referrer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "referral_reward_customer123_1705312200000",
  "type": "referral_reward_earned",
  "referredCustomerEmail": "referee@example.com",
  "rewardType": "point",
  "earnPoints": 500,
  "couponCode": null,
  "orderId": "gid://shopify/Order/789",
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

### Birthday events

#### birthday/reward\_earned

Points reward example:

```json
{
  "webhookId": "birthday_reward_customer123_1705312200000",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "birthday_reward_customer123_1705312200000",
  "type": "earn_point",
  "event": "birthday",
  "source": "user",
  "earnPoints": 100,
  "oldPoint": 500,
  "newPoint": 600,
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```

Discount reward example:

```json
{
  "webhookId": "5QIhElOglJOCfyX6Zo6I",
  "triggeredAt": "2026-02-06T09:16:33.341Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe", 
    "shopifyCustomerId": 9127686570181
  },
  "id": "5QIhElOglJOCfyX6Zo6I",
  "source": "user",
  "event": "birthday", 
  "type": "earn_DISCOUNT",
  "createdAt": "2026-02-06T09:16:33.341Z"
}
```

### Milestone events

#### milestone/achieved

```json
{
  "webhookId": "activity_xyz789",
  "triggeredAt": "2024-01-15T10:30:00.000Z",
  "customer": {
    "email": "customer@example.com",
    "name": "John Doe",
    "shopifyCustomerId": "gid://shopify/Customer/123456"
  },
  "id": "activity_xyz789",
  "type": "earn_point",
  "event": "milestone",
  "earnBy": "order_count",
  "oldPoint": 1000,
  "newPoint": 1100,
  "programTitle": "Order Milestone Rewards",
  "createdAt": "2024-01-15T10:30:00.000Z"
}
```


---

# 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://devdocs.joy.so/webhook-api/webhook-events.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.
