Webhook event reference
Webhooks notify your website/app of events that happen in Memberful, such as when a member creates an account, a subscription is updated, or a plan is deleted.
This article lists all the events that can trigger a webhook. If you're looking for an explanation of how to set up and use webhooks, check out our main article about Webhooks.
In this help doc:
- Member events
- Subscription events
- Order events
- Plan events
- Download events
- How upgrades/downgrades are handled
Member events
Member accounts are created automatically when a member purchases a subscription or registers for free (if that feature is enabled). You can also create member accounts manually from your dashboard.
Most webhook types follow the object.event naming convention. Member events currently follow an object_event convention instead. Pay close attention to the event names laid out in this doc.
member_signup
Sent when a new member account is created.
Use this webhook to add new members to your app or to a third-party service.
{
"event": "member_signup",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
}
}
member_updated
Sent when a member's profile information is updated.
Use this webhook to update a member's profile information in your app.
This will not be triggered when a member updates their answers to custom fields. Use the custom_fields.updated webhook to detect custom field updates.
{
"event": "member_updated",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"changed": {
"email": [
"old_email@example.com",
"john.doe@example.com"
]
}
}
member.deleted
Sent when a member is deleted from your Memberful account.
Use this webhook to remove a member from your app if they were deleted from Memberful.
It's not common for a member account to be deleted — in most cases, you'll want to react to Subscription Deactivated instead.
{
"event": "member.deleted",
"member": {
"deleted": true,
"id": 0
}
}
tax_id.updated
Sent when a member adds, changes, or removes their tax ID.
Use this trigger to update your app when a member updates their tax ID.
{
"event": "tax_id.updated",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"tax_id": {
"country": "FR",
"type": "eu_vat",
"value": "FR1234567890"
}
}
custom_fields.updated
Sent when a member answers or updates their custom fields. The value
field can be a String, an Array of Strings, or an empty Array.
Use this trigger to update your app when a member updates their answers to custom fields, either during checkout or by visiting their member profile.
{
"event": "custom_fields.updated",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"custom_fields": [
{
"field": {
"id": 1,
"label": "Who's your favorite writer?"
},
"value": "John Doe"
},
{
"field": {
"id": 2,
"label": "What's your t-shirt size?"
},
"value": ""
},
{
"field": {
"id": 3,
"label": "How do you consume our content?"
},
"value": [
"Podcast",
"Newsletter"
]
}
]
}
Subscription events
Sent when a member subscribes to a plan or when that subscription is updated, renewed, or deleted.
Almost all times across all webhooks are returned as Unix time, but subscription events use ISO 8601 for the following attributes: `activated_at`, `created_at`, `expires_at`, `trial_end_at`, and `trial_start_at`.
subscription.created
Sent when a new subscription is added to a member's account. This includes when a member purchases a subscription or activates a gifted subscription, when a member is added to a group subscription, and when a staff account manually creates a subscription.
Use this webhook to let your app know when a member has subscribed.
{
"event": "subscription.created",
"subscription": {
"active": true,
"autorenew": true,
"created_at": "2025-06-04T22:15:31Z",
"expires_at": "2025-07-04T22:15:31Z",
"id": 1,
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"subscription_plan": {
"id": 0,
"interval_count": 1,
"interval_unit": "month",
"name": "Sample plan",
"price_cents": 100000000,
"slug": "0-sample-plan"
},
"trial_end_at": null,
"trial_start_at": null
}
}
subscription.updated
Sent when a member's subscription is updated.
If you want to know if the update is a plan change, you'll need to see if the plan_id field is present in the changed object. The first value is the old plan and the second value is the new one. The same applies to other changed fields.
{
"event": "subscription.updated",
"subscription": {
"active": true,
"autorenew": true,
"created_at": "2025-06-04T22:15:31Z",
"expires_at": "2025-07-04T22:15:31Z",
"id": 1,
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"subscription_plan": {
"id": 0,
"interval_count": 1,
"interval_unit": "month",
"name": "Sample plan",
"price_cents": 100000000,
"slug": "0-sample-plan"
},
"trial_end_at": null,
"trial_start_at": null
},
"changed": {
"plan_id": [
42,
0
],
"expires_at": [
"2025-07-04T22:15:31Z",
"2025-08-03T22:15:31Z"
],
"autorenew": [
false,
true
]
}
}
subscription.renewed
Sent when a member's subscription is renewed or when a returning member reactivates an old subscription.
Use this webhook to renew the member's access to your app. At this time there's no way to differentiate between a renewal and a reactivation, but you could reach out to our API to find out more about the subscription's history.
{
"event": "subscription.renewed",
"subscription": {
"active": true,
"autorenew": true,
"created_at": "2025-06-04T22:15:31Z",
"expires_at": "2025-07-04T22:15:31Z",
"id": 1,
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"subscription_plan": {
"id": 0,
"interval_count": 1,
"interval_unit": "month",
"name": "Sample plan",
"price_cents": 100000000,
"slug": "0-sample-plan"
},
"trial_end_at": null,
"trial_start_at": null
},
"order": {
"created_at": "2025-06-04T22:15:31Z",
"status": "completed",
"total": 9900,
"uuid": "4DACB7B0-B728-0130-F9E8-102B343DC979"
}
}
subscription.activated
Sent when a suspended order is marked completed by staff and the subscription becomes active again.
Sent when a member's (suspended order)[/help/manage-your-members/pause-a-subscription/#suspend-the-order] is marked by staff as complete and the subscription becomes active again.
This does not refer to when a member reactivates a previously expired subscription — use the Subscription Renewed trigger for that.
{
"event": "subscription.activated",
"subscription": {
"active": true,
"autorenew": true,
"created_at": "2025-06-04T22:15:31Z",
"expires_at": "2025-07-04T22:15:31Z",
"id": 1,
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"subscription_plan": {
"id": 0,
"interval_count": 1,
"interval_unit": "month",
"name": "Sample plan",
"price_cents": 100000000,
"slug": "0-sample-plan"
},
"trial_end_at": null,
"trial_start_at": null
}
}
subscription.deactivated
Sent when a member's subscription fails to renew, expires, or becomes inactive.
Sent when a member's subscription fails to renew, expires, or becomes inactive. Also sent when a staff account suspends an order, making the subscription inactive.
Use this webhook to remove the member's access to your app or to update their status if they stop paying.
{
"event": "subscription.deactivated",
"subscription": {
"active": false,
"autorenew": true,
"created_at": "2025-06-04T22:15:31Z",
"expires_at": "2025-07-04T22:15:31Z",
"id": 1,
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"subscription_plan": {
"id": 0,
"interval_count": 1,
"interval_unit": "month",
"name": "Sample plan",
"price_cents": 100000000,
"slug": "0-sample-plan"
},
"trial_end_at": null,
"trial_start_at": null
}
}
subscription.deleted
Sent when a staff account deletes a member’s subscription from the Memberful dashboard.
Use this webhook to remove the member's access to your app or to update their status.
{
"event": "subscription.deleted",
"subscription": {
"active": true,
"autorenew": true,
"created_at": "2025-06-04T22:15:31Z",
"expires_at": "2025-07-04T22:15:31Z",
"id": 1,
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"subscription_plan": {
"id": 0,
"interval_count": 1,
"interval_unit": "month",
"name": "Sample plan",
"price_cents": 100000000,
"slug": "0-sample-plan"
},
"trial_end_at": null,
"trial_start_at": null
}
}
Order events
Sent when a member places an order to purchase plans or downloads.
Custom fields are now collected after checkout, which means they're no longer set when the order_purchased event is triggered. To access them, webhook recipients must now use the custom_fields.updated event instead.
order.purchased
Sent when a member places an order or when a staff account manually adds an order to a member's account.
This is not triggered for renewal payments.
A member purchasing a gift subscription for someone else will trigger this webhook, but no subscription will be created until the recipient activates their gift.
Use this webhook to notify your app when a member makes a purchase.
{
"event": "order.purchased",
"order": {
"uuid": "4DACB7B0-B728-0130-F9E8-102B343DC979",
"number": "4DACB7B0",
"total": 9900,
"status": "completed",
"receipt": "receipt text",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"products": [
],
"subscriptions": [
{
"active": true,
"created_at": 1749075331,
"expires": true,
"expires_at": 1751667331,
"id": 0,
"in_trial_period": false,
"subscription": {
"id": 0,
"price": 1000,
"name": "Sample plan",
"slug": "0-sample-plan",
"renewal_period": "monthly",
"interval_unit": "month",
"interval_count": 1,
"for_sale": true
},
"trial_end_at": null,
"trial_start_at": null
}
]
}
}
order.refunded
Sent when a staff account refunds an order.
Use this trigger to update your app when a refund has been processed.
{
"event": "order.refunded",
"order": {
"uuid": "4DACB7B0-B728-0130-F9E8-102B343DC979",
"number": "4DACB7B0",
"total": 9900,
"status": "refunded",
"receipt": "receipt text",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"products": [
],
"subscriptions": [
{
"active": true,
"created_at": 1749075331,
"expires": true,
"expires_at": 1751667331,
"id": 0,
"in_trial_period": false,
"subscription": {
"id": 0,
"price": 1000,
"name": "Sample plan",
"slug": "0-sample-plan",
"renewal_period": "monthly",
"interval_unit": "month",
"interval_count": 1,
"for_sale": true
},
"trial_end_at": null,
"trial_start_at": null
}
]
}
}
order.suspended
Sent when an order is suspended by staff.
{
"event": "order.suspended",
"order": {
"uuid": "4DACB7B0-B728-0130-F9E8-102B343DC979",
"number": "4DACB7B0",
"total": 9900,
"status": "suspended",
"receipt": "receipt text",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"products": [
],
"subscriptions": [
{
"active": true,
"created_at": 1749075331,
"expires": true,
"expires_at": 1751667331,
"id": 0,
"in_trial_period": false,
"subscription": {
"id": 0,
"price": 1000,
"name": "Sample plan",
"slug": "0-sample-plan",
"renewal_period": "monthly",
"interval_unit": "month",
"interval_count": 1,
"for_sale": true
},
"trial_end_at": null,
"trial_start_at": null
}
]
}
}
order.completed
Sent when a suspended order is marked completed by staff.
{
"event": "order.completed",
"order": {
"uuid": "4DACB7B0-B728-0130-F9E8-102B343DC979",
"number": "4DACB7B0",
"total": 9900,
"status": "completed",
"receipt": "receipt text",
"member": {
"address": {
"street": "Street",
"city": "City",
"state": "State",
"postal_code": "Postal code",
"country": "City"
},
"created_at": 1749075331,
"credit_card": {
"exp_month": 1,
"exp_year": 2040
},
"custom_field": "Custom field value",
"discord_user_id": "000000000000000000",
"email": "john.doe@example.com",
"first_name": "John",
"full_name": "John Doe",
"id": 0,
"last_name": "Doe",
"phone_number": "555-12345",
"signup_method": "checkout",
"stripe_customer_id": "cus_00000",
"tracking_params": {
"utm_term": "shoes",
"utm_campaign": "summer_sale",
"utm_medium": "social",
"utm_source": "instagram",
"utm_content": "textlink"
},
"unrestricted_access": false,
"username": "john_doe"
},
"products": [
],
"subscriptions": [
{
"active": true,
"created_at": 1749075331,
"expires": true,
"expires_at": 1751667331,
"id": 0,
"in_trial_period": false,
"subscription": {
"id": 0,
"price": 1000,
"name": "Sample plan",
"slug": "0-sample-plan",
"renewal_period": "monthly",
"interval_unit": "month",
"interval_count": 1,
"for_sale": true
},
"trial_end_at": null,
"trial_start_at": null
}
]
}
}
Plan events
Sent when staff accounts create, update, or delete plans.
subscription_plan.created
Sent when a new plan is created.
{
"event": "subscription_plan.created",
"subscription": {
"id": 0,
"price": 1000,
"name": "Sample plan",
"slug": "0-sample-plan",
"renewal_period": "monthly",
"interval_unit": "month",
"interval_count": 1,
"for_sale": true
}
}
subscription_plan.updated
Sent when a plan is updated.
{
"event": "subscription_plan.updated",
"subscription": {
"id": 0,
"price": 1000,
"name": "Sample plan",
"slug": "0-sample-plan",
"renewal_period": "monthly",
"interval_unit": "month",
"interval_count": 1,
"for_sale": true
}
}
subscription_plan.deleted
Sent when a plan is deleted.
{
"event": "subscription_plan.deleted",
"subscription": {
"id": 0,
"price": 1000,
"name": "Sample plan",
"slug": "0-sample-plan",
"renewal_period": "monthly",
"interval_unit": "month",
"interval_count": 1,
"for_sale": true
}
}
Download events
Staff can create downloads to include with plans or to sell separately.
download.created
Sent when a download is created.
{
"event": "download.created",
"product": {
"id": 0,
"name": "Sample download",
"price": 1000,
"slug": "0-sample-download",
"for_sale": true
}
}
download.updated
Sent when a download is updated.
{
"event": "download.updated",
"product": {
"id": 0,
"name": "Sample download",
"price": 1000,
"slug": "0-sample-download",
"for_sale": true
}
}
download.deleted
Sent when a download is deleted.
{
"event": "download.deleted",
"product": {
"id": 0,
"name": "Sample download",
"price": 1000,
"slug": "0-sample-download",
"for_sale": true
}
}
How upgrades/downgrades are handled
Both upgrades and downgrades trigger the subscription.updated webhook.
Upgrades include a "changed" section detailing the changes:
"changed": {
"plan_id": [
42,
0
],
"expires_at": [
"2023-05-12T15:09:58Z",
"2023-06-11T15:09:58Z"
],
"autorenew": [
false,
true
]
}
Downgrades tend to happen on the next renewal date (since the member already paid a higher price for their current period, they're not downgraded immediately). In that case, the "Changed" section will be empty.
Related help docs: