Webhook List
Comprehensive list of all webhook events that Stash can send to your backend, including event types, triggers, payload structures, and detailed examples for each webhook type across Stash products.
This page lists all webhook events Stash can send to your backend. Each event is triggered by a specific user action or system event. Use the table below to see what's available, then expand the tabs for full payload examples.
Product-specific events: Some events are specific to Stash Pay or Stash Webshop. See the Stash Pay integration guide or Stash Webshop integration guide for product-specific webhook details.
Available webhooks
PURCHASE_SUCCEEDED is the primary event for granting items — always subscribe to it. PURCHASE_REFUNDED should be handled to revoke or adjust granted items. The remaining events are optional and used mainly for analytics, marketing, and behavior tracking.
| Event Type | Required | Products | Description |
|---|---|---|---|
PURCHASE_SUCCEEDED | Yes | Pay, Webshop | Purchase successfully completed. Use to grant items to the player. source is "StashPay" or "Cart". |
PURCHASE_REFUNDED | Recommended | Pay, Webshop | Full or partial refund processed. Use to revoke or adjust granted items. |
CREATE_PAYMENT_INTENT | No | Pay, Webshop | Payment process initiated (user starts checkout). |
FREE_ITEM_REDEEMED | No | Pay, Webshop | A free item was claimed (promotional rewards, etc.). |
MUTATE_CART | No | Webshop | Cart contents changed (add/remove/quantity). |
VIEW_ITEM | No | Webshop | User viewed an item (debounced once per 100s per item). |
VIEW_CHECKOUT_PAGE | No | Webshop | User opened the checkout page. |
VIEW_PRODUCT_DETAIL_PAGE | No | Webshop | User opened a product detail page. |
CART_BUTTON_CLICK | No | Webshop | User interacted with a cart button. |
Payload structure
All v1 webhooks follow a consistent base structure:
{
"type": "EVENT_TYPE",
"environment": "test",
"eventData": {
// Event-specific payload data
}
}The environment field is always present. Possible values: "test" (test/staging) or "production" (live). Use it to distinguish test traffic from real transactions.
Detailed payload examples
Pick the event you're integrating to see its full JSON payload.
Triggered when a purchase is successfully completed. The source field is "StashPay" for Stash Pay or "Cart" for Webshop.
{
"type": "PURCHASE_SUCCEEDED",
"environment": "test",
"purchaseSucceeded": {
"timeMillis": 1640995200000,
"orderId": "order_abc123",
"checkoutLinkId": "checkout_xyz789",
"currency": "USD",
"userId": "user_123",
"items": [
{
"id": "item_456",
"quantity": 2,
"price": "9.99",
"metadata": {
"category": "weapons",
"rarity": "legendary"
}
}
],
"tax": "1.50",
"total": "21.48",
"taxDetails": {
"items": [
{
"label": "Sales Tax",
"isInclusive": false,
"amount": "1.50",
"percentage": "7.5",
"country": "US",
"state": "CA"
}
]
},
"emailMarketingOptIn": true,
"regionCode": "US",
"source": "StashPay",
"ipAddress": "192.168.1.1"
}
}Triggered when a refund is successfully processed for a previous purchase. Both full and partial refunds fire this event — multiple partial refunds on the same order each generate a separate event.
The items array contains all items from the original purchase, not just refunded ones. The total field reflects the actual refund amount.
{
"type": "PURCHASE_REFUNDED",
"environment": "test",
"purchaseRefunded": {
"timeMillis": 1640995200000,
"orderId": "order_abc123",
"userId": "user_123",
"items": [
{
"id": "item_456",
"quantity": 2,
"price": "9.99",
"metadata": {
"category": "weapons",
"rarity": "legendary"
}
}
],
"total": "21.48",
"currency": "USD",
"reason": "Customer requested refund",
"regionCode": "US",
"source": "StashPay"
}
}Triggered when the payment process is initiated. The source field is "StashPay" for Stash Pay or "Cart" for Webshop.
{
"type": "CREATE_PAYMENT_INTENT",
"environment": "test",
"openPayment": {
"cartId": "cart_789",
"userId": "user_123",
"items": [
{
"id": "item_456",
"quantity": 2
}
],
"source": "StashPay"
}
}Triggered when a free item is redeemed (promotional rewards, claim-codes, etc.).
{
"type": "FREE_ITEM_REDEEMED",
"environment": "test",
"freeItemRedeemed": {
"userId": "user_123",
"itemId": "item_free_001",
"ipAddress": "192.168.1.1",
"metadata": {
"promotionId": "summer_promo",
"redeemCode": "SUMMER2024"
}
}
}Triggered when cart contents are modified. Quantity deltas can be negative (removal) or positive (addition).
{
"type": "MUTATE_CART",
"environment": "test",
"mutateCart": {
"cartId": "cart_789",
"timeMillis": 1640995200000,
"userId": "user_123",
"items": [
{
"id": "item_456",
"quantity": 3
},
{
"id": "item_789",
"quantity": -1
}
],
"regionCode": "US",
"source": "Cart",
"ipAddress": "192.168.1.1"
}
}Triggered when a user views an item. Debounced to once per 100 seconds per item per user.
{
"type": "VIEW_ITEM",
"environment": "test",
"viewItem": {
"userId": "user_123",
"itemId": "item_456",
"regionCode": "US"
}
}Triggered when the checkout page is viewed.
{
"type": "VIEW_CHECKOUT_PAGE",
"environment": "test",
"viewCheckoutPage": {
"userId": "user_123",
"regionCode": "US"
}
}Triggered when a product detail page is viewed.
{
"type": "VIEW_PRODUCT_DETAIL_PAGE",
"environment": "test",
"viewProductDetailsPage": {
"userId": "user_123",
"itemId": "item_456",
"regionCode": "US"
}
}Common fields
Several fields appear across multiple webhook types:
| Field | Type | Description |
|---|---|---|
type | string | Event type identifier (e.g. "PURCHASE_SUCCEEDED"). |
environment | string | Environment that sent the webhook. Always present. "test" or "production". |
userId | string | External user identifier from your system. |
regionCode | string | Unicode CLDR region code (e.g. "US", "FR") based on user location. |
ipAddress | string | User's IP address when the event occurred. |
timeMillis | number | Timestamp in milliseconds since Unix epoch. |
Subscription Events (v2)
Subscription webhooks use a v2 payload format with lowercase dot-notation event types, unlike the v1 format used by purchase and webshop events above.
Subscription events notify your system of subscription lifecycle changes. See the Subscriptions guide for full integration details.
Available subscription events
| Event Type | Description | Trigger |
|---|---|---|
subscription.created | New subscription created | Player completes subscription checkout |
subscription.updated | Subscription changed | Plan or status was modified |
subscription.canceled | Subscription canceled | Player cancels their subscription |
subscription.reactivated | Subscription reactivated | Canceled subscription was reactivated before expiration |
subscription.expired | Subscription expired | Subscription reached terminal state |
subscription.payment_failed | Payment failed | Renewal payment attempt failed; subscription enters past_due |
subscription.payment_succeeded | Payment succeeded | Renewal payment processed successfully |
v2 Payload structure
Subscription webhooks use a different structure from v1 events:
{
"type": "subscription.event_name",
"data": {
// Subscription object
}
}Detailed subscription payloads
Triggered when a new subscription is created.
{
"type": "subscription.created",
"data": {
"id": "sub_xyz789",
"external_account_id": "player_123",
"plan_id": "plan_abc123",
"status": "active",
"period": {
"value": 1,
"unit": "month"
},
"trial_end": "2024-02-01T00:00:00Z",
"access_end_date": "2024-03-01T00:00:00Z",
"current_period_end": "2024-03-01T00:00:00Z",
"next_billing_date": "2024-03-01T00:00:00Z",
"cancel_at_period_end": false,
"canceled_at": null,
"created_at": "2024-01-01T00:00:00Z"
}
}Triggered when a subscription's plan or status changes.
{
"type": "subscription.updated",
"data": {
"id": "sub_xyz789",
"external_account_id": "player_123",
"plan_id": "plan_abc123",
"status": "active",
"period": {
"value": 1,
"unit": "month"
},
"trial_end": null,
"access_end_date": "2024-04-01T00:00:00Z",
"current_period_end": "2024-04-01T00:00:00Z",
"next_billing_date": "2024-04-01T00:00:00Z",
"cancel_at_period_end": false,
"canceled_at": null,
"created_at": "2024-01-01T00:00:00Z"
}
}Triggered when a player cancels their subscription.
{
"type": "subscription.canceled",
"data": {
"id": "sub_xyz789",
"external_account_id": "player_123",
"plan_id": "plan_abc123",
"status": "canceled",
"period": {
"value": 1,
"unit": "month"
},
"trial_end": null,
"access_end_date": "2024-03-01T00:00:00Z",
"current_period_end": "2024-03-01T00:00:00Z",
"next_billing_date": "2024-03-01T00:00:00Z",
"cancel_at_period_end": true,
"canceled_at": "2024-02-15T14:30:00Z",
"created_at": "2024-01-01T00:00:00Z"
}
}Triggered when a canceled subscription is reactivated before expiration.
{
"type": "subscription.reactivated",
"data": {
"id": "sub_xyz789",
"external_account_id": "player_123",
"plan_id": "plan_abc123",
"status": "active",
"period": {
"value": 1,
"unit": "month"
},
"trial_end": null,
"access_end_date": "2024-03-01T00:00:00Z",
"current_period_end": "2024-03-01T00:00:00Z",
"next_billing_date": "2024-03-01T00:00:00Z",
"cancel_at_period_end": false,
"canceled_at": null,
"created_at": "2024-01-01T00:00:00Z"
}
}Triggered when a subscription reaches its terminal state.
{
"type": "subscription.expired",
"data": {
"id": "sub_xyz789",
"external_account_id": "player_123",
"plan_id": "plan_abc123",
"status": "expired",
"period": {
"value": 1,
"unit": "month"
},
"trial_end": null,
"access_end_date": "2024-03-01T00:00:00Z",
"current_period_end": "2024-03-01T00:00:00Z",
"next_billing_date": "2024-03-01T00:00:00Z",
"cancel_at_period_end": true,
"canceled_at": "2024-02-15T14:30:00Z",
"created_at": "2024-01-01T00:00:00Z"
}
}Triggered when a renewal payment attempt fails. The subscription enters past_due status and Stash will retry.
{
"type": "subscription.payment_failed",
"data": {
"id": "sub_xyz789",
"external_account_id": "player_123",
"plan_id": "plan_abc123",
"status": "past_due",
"period": {
"value": 1,
"unit": "month"
},
"trial_end": null,
"access_end_date": "2024-03-03T00:00:00Z",
"current_period_end": "2024-03-01T00:00:00Z",
"next_billing_date": "2024-03-02T00:00:00Z",
"cancel_at_period_end": false,
"canceled_at": null,
"created_at": "2024-01-01T00:00:00Z"
}
}Triggered when a renewal payment is processed successfully.
{
"type": "subscription.payment_succeeded",
"data": {
"id": "sub_xyz789",
"external_account_id": "player_123",
"plan_id": "plan_abc123",
"status": "active",
"period": {
"value": 1,
"unit": "month"
},
"trial_end": null,
"access_end_date": "2024-04-01T00:00:00Z",
"current_period_end": "2024-04-01T00:00:00Z",
"next_billing_date": "2024-04-01T00:00:00Z",
"cancel_at_period_end": false,
"canceled_at": null,
"created_at": "2024-01-01T00:00:00Z"
}
}Subscription object fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the subscription. |
external_account_id | string | Identifier for the player/user. |
plan_id | string | Plan identifier. |
status | string | active, past_due, canceled, or expired. |
period | object | Billing period with value (integer) and unit (day / week / month / year). |
trial_end | string (nullable) | ISO 8601 timestamp when trial ends. |
access_end_date | string | ISO 8601 timestamp when access expires. |
current_period_end | string | ISO 8601 timestamp for current period end. |
next_billing_date | string | ISO 8601 timestamp for next billing attempt. |
cancel_at_period_end | boolean | Whether cancellation is scheduled. |
canceled_at | string (nullable) | ISO 8601 timestamp when canceled. |
created_at | string | ISO 8601 timestamp when created. |
How is this guide?