Subscriptions
Receive notifications for subscription lifecycle events including creation, renewal, cancellation, and expiration.
Overview
Subscription webhooks notify your application about every stage of the subscription lifecycle — from creation and renewal to cancellation and expiration. These are the most commonly used Pelcro webhooks and carry a deeply nested payload including customer, plan, invoice, and payment method details.
Use Cases
- Provision or revoke access when subscriptions are created, renewed, or expire
- Trigger dunning flows when subscriptions are canceled or past due
- Sync subscription status to your CMS or access control system
- Alert customers before their trial ends
- Track gift subscription fulfillment
- Monitor churn risk with prediction scores
Events
| Event | Description |
|---|---|
subscription.created | A new subscription was created |
subscription.updated | A subscription was modified |
subscription.changed | A subscription was changed (e.g., plan switch) |
subscription.canceled | A subscription was canceled |
subscription.renewed | A subscription was renewed for a new billing period |
subscription.expired | A subscription has expired |
subscription.trial_will_end | A subscription's trial period is about to end |
subscription.gift_notification | A gift subscription notification was sent |
subscription.created
Triggered When
- A customer subscribes via the SDK
- A subscription is created via the Core API
- A subscription is created from the Platform
Note: This event is not fired during data migrations (when the subscription is flagged as migrating).
Payload
{
"type": "subscription.created",
"id": "evt_a1B2c3D4e5F6g7H8i9J0k1L2",
"created": 1704067200,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"address_id": 200001,
"address_shipping": {
"object": "address",
"id": 200001,
"city": "Springfield",
"company": null,
"country": "US",
"country_name": "United States",
"created_at": "2025-06-15T10:00:00.000000Z",
"department": null,
"first_name": "Jane",
"is_default": true,
"last_name": "Doe",
"line1": "123 Main Street",
"line2": null,
"postal_code": "62704",
"salutation": null,
"site_ids": [1],
"state": "IL",
"state_name": "Illinois",
"title": null,
"type": "shipping",
"updated_at": "2025-06-15T10:00:00.000000Z"
},
"agency_id": null,
"auto_renew": true,
"backdate_start_date": null,
"billing": 1,
"billing_cycle_anchor": "2026-01-01T12:00:00.000000Z",
"campaign_id": null,
"campaign_key": null,
"cancel_at": null,
"cancel_at_period_end": 0,
"cancel_reason": null,
"canceled_at": null,
"canceled_by_email": null,
"canceled_by_type": null,
"churn_prediction_created_at": null,
"churn_prediction_updated_at": null,
"churn_risk_bucket": null,
"churn_risk_score": null,
"coupon": null,
"created": "2026-01-01T12:00:00.000000Z",
"current_period_end": "2026-02-01T12:00:00.000000Z",
"current_period_start": "2026-01-01T12:00:00.000000Z",
"customer": {
"object": "customer",
"id": 300001,
"balance": 0,
"billing_email": null,
"created": "2025-06-15T10:00:00.000000Z",
"currency": "usd",
"default_source": {
"object": "source",
"id": 400001,
"object_id": "pm_XXXXXXXXXXXXXXXXXXXX",
"object_gateway": "stripe",
"brand": "visa",
"country": "US",
"exp_month": 12,
"exp_year": 2028,
"funding": "credit",
"last4": "4242",
"site_ids": [1]
},
"delinquent": false,
"display_name": null,
"email": "[email protected]",
"email_confirm": 0,
"email_hardbounce": false,
"first_name": "Jane",
"has_password": 1,
"language": null,
"last_active_at": 1704067190,
"last_login_method": "email",
"last_name": "Doe",
"mail_marketing": null,
"metadata": null,
"object_id": "cus_XXXXXXXXXXXXXX",
"organization": null,
"password_last_updated_at": null,
"phone": null,
"phone_confirm": null,
"salutation": null,
"site_ids": [1],
"tele_marketing": null,
"title": null,
"username": null
},
"days_until_due": null,
"default_source": {
"object": "source",
"id": 400001,
"object_id": "pm_XXXXXXXXXXXXXXXXXXXX",
"object_gateway": "stripe",
"brand": "visa",
"country": "US",
"exp_month": 12,
"exp_year": 2028,
"funding": "credit",
"last4": "4242",
"site_ids": [1]
},
"default_tax_rates": null,
"ended_at": null,
"expires_at": null,
"gift_code": null,
"gift_donor_subscription_id": null,
"gift_message": null,
"gift_notification_sent": 0,
"gift_recipient_email": null,
"gift_recipient_first_name": null,
"gift_recipient_last_name": null,
"gift_start_date": null,
"is_gift_donor": 0,
"is_gift_recipient": 0,
"is_redeemed": null,
"latest_invoice": {
"object": "invoice",
"id": 500001,
"...": "full invoice object (see Invoice Webhooks)"
},
"metadata": null,
"object_id": "sub_XXXXXXXXXXXXXXXXXXXXXXXX",
"plan": {
"id": 600001,
"product_id": 700001,
"parent_plan_id": null,
"object_id": "plan_XXXXXXXXXXXXXX",
"active": "active",
"type": "regular",
"amount": 1999,
"recognized_revenue_type": "time",
"renewal_strategy": null,
"auto_renew": true,
"available_online": true,
"allow_send_invoice": true,
"auto_uncollectible_days": null,
"invoice_upcoming_notification": null,
"currency": "usd",
"countries": null,
"entitlements": null,
"interval": "month",
"interval_count": 1,
"is_imported": false,
"is_hidden": false,
"is_donation": false,
"metadata": null,
"member_seat_capacity": null,
"nickname": "Monthly Premium",
"name_internal": null,
"refundable": false,
"shipments_per_interval": null,
"description": "Monthly access to premium content.",
"trial_period_days": 0,
"ip_addresses": null,
"domains": null,
"created_at": "2025-12-15T10:00:00.000000Z",
"updated_at": "2025-12-15T10:00:00.000000Z",
"site_ids": [1],
"product": {
"id": 700001,
"object_id": "prod_XXXXXXXXXXXXXX",
"type": "service",
"active": "active",
"address_required": null,
"countries": null,
"description": "Premium content subscription.",
"entitlements": null,
"is_imported": null,
"is_hidden": null,
"language": null,
"livemode": true,
"metadata": null,
"name": "Premium Content",
"name_internal": null,
"shippable": false,
"statement_descriptor": null,
"created_at": "2025-12-15T10:00:00.000000Z",
"updated_at": "2025-12-15T10:00:00.000000Z",
"site_ids": [1]
}
},
"quantity": 1,
"recommended_plan_id": null,
"renews_at": 1738411200,
"schedules": null,
"shipments_remaining": null,
"shipments_suspended_until": null,
"shipments_undeliverable": false,
"shipping_address": {
"...": "same as address_shipping (deprecated)"
},
"site_id": 1,
"source": null,
"start_date": "2026-01-01T12:00:00.000000Z",
"status": "active",
"trial_end": null,
"trial_start": null
}
}
}subscription.updated
Triggered When
- Any subscription attribute is saved (the most frequently fired subscription event)
- A subscription is updated via the Core API or from the Platform
- A subscription is updated during an agency import
Payload
The payload includes a previous_attributes object containing only the fields that changed.
{
"type": "subscription.updated",
"id": "evt_b2C3d4E5f6G7h8I9j0K1l2M3",
"created": 1704153600,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"...": "full subscription object (same structure as subscription.created)"
},
"previous_attributes": {
"current_period_start": "2025-12-01T12:00:00.000000Z",
"current_period_end": "2026-01-01T12:00:00.000000Z",
"billing_cycle_anchor": "2025-12-01T12:00:00.000000Z"
}
}
}subscription.changed
Triggered When
- A subscription's plan is changed (upgrade or downgrade)
Payload
The payload may include a previous_attributes object containing the fields that changed.
{
"type": "subscription.changed",
"id": "evt_c3D4e5F6g7H8i9J0k1L2m3N4",
"created": 1704240000,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"...": "full subscription object with the new plan and updated fields"
},
"previous_attributes": {
"plan": {
"id": 600001,
"nickname": "Monthly Basic",
"amount": 999
}
}
}
}subscription.canceled
Triggered When
- A customer cancels their subscription via the SDK
- A subscription is canceled via the Core API or from the Platform
- A subscription is immediately canceled (
cancelNow) with or without a refund - A subscription schedule triggers a cancellation
Payload
When a subscription is canceled, the canceled_at, canceled_by_email, canceled_by_type, and cancel_reason fields are populated. If the subscription will remain active until the end of the billing period, cancel_at_period_end is 1 and status remains "active".
{
"type": "subscription.canceled",
"id": "evt_d4E5f6G7h8I9j0K1l2M3n4O5",
"created": 1704326400,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"...": "full subscription object",
"auto_renew": false,
"cancel_at_period_end": 1,
"cancel_reason": "Too expensive",
"canceled_at": "2026-01-04T12:00:00.000000Z",
"canceled_by_email": "[email protected]",
"canceled_by_type": "Admin/Collaborator",
"expires_at": 1738411200,
"status": "active"
}
}
}subscription.renewed
Triggered When
- A subscription is renewed at the end of a billing period by the Pelcro Billing Engine
- A subscription is manually renewed via the Core API, SDK, or Platform
- A canceled or expired subscription is reactivated/restarted (Pelcro-owned subscriptions)
- A campaign-driven renewal is processed
- An agency import renews a subscription
Payload
{
"type": "subscription.renewed",
"id": "evt_e5F6g7H8i9J0k1L2m3N4o5P6",
"created": 1706832000,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"...": "full subscription object with updated billing period dates",
"current_period_start": "2026-02-01T12:00:00.000000Z",
"current_period_end": "2026-03-01T12:00:00.000000Z",
"status": "active"
}
}
}subscription.expired
Triggered When
- A subscription's billing period has ended and the Pelcro Billing Engine expiration workflow runs
- A subscription is immediately canceled (
cancelNow) — this fires bothsubscription.canceledandsubscription.expiredback-to-back
Payload
{
"type": "subscription.expired",
"id": "evt_f6G7h8I9j0K1l2M3n4O5p6Q7",
"created": 1738411200,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"...": "full subscription object",
"auto_renew": false,
"cancel_at_period_end": 0,
"ended_at": "2026-02-01T12:00:00.000000Z",
"status": "canceled"
}
}
}subscription.trial_will_end
Triggered When
- A subscription's trial period is approaching its end date (a scheduled job scans for trials ending soon and fires this event)
Payload
The payload includes a previous_attributes object containing only the fields that changed.
{
"type": "subscription.trial_will_end",
"id": "evt_g7H8i9J0k1L2m3N4o5P6q7R8",
"created": 1704240000,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"...": "full subscription object",
"status": "trialing",
"trial_start": "2026-01-01T12:00:00.000000Z",
"trial_end": "2026-01-14T12:00:00.000000Z"
},
"previous_attributes": {
"status": "trialing"
}
}
}subscription.gift_notification
Triggered When
- A gift subscription is created and the
gift_start_dateis today or in the past (fires immediately at creation) - A scheduled job processes gift subscriptions whose
gift_start_datehas arrived (for future-dated gifts)
Payload
Gift-related fields are populated when this event fires.
{
"type": "subscription.gift_notification",
"id": "evt_h8I9j0K1l2M3n4O5p6Q7r8S9",
"created": 1704067200,
"data": {
"object": {
"object": "subscription",
"id": 100001,
"...": "full subscription object",
"is_gift_donor": 1,
"is_gift_recipient": 0,
"gift_code": "GIFT-XXXX-XXXX",
"gift_recipient_email": "[email protected]",
"gift_recipient_first_name": "John",
"gift_recipient_last_name": "Smith",
"gift_message": "Enjoy your subscription!",
"gift_start_date": "2026-02-01T00:00:00.000000Z",
"gift_notification_sent": 1,
"gift_donor_subscription_id": null
}
}
}Payload Fields
Subscription Object
| Field | Type | Description |
|---|---|---|
object | string | Object type identifier, always subscription |
id | integer | Unique identifier for the subscription |
object_id | string | null | External gateway identifier (e.g., Stripe subscription ID) |
site_id | integer | Site the subscription belongs to |
address_id | integer | null | Shipping address ID |
status | string | Subscription status: "active", "canceled", "past_due", "trialing", "unpaid", "incomplete", "incomplete_expired", "extended", "pending_payment", or "scheduled" |
auto_renew | boolean | Whether the subscription will auto-renew (true when active and not set to cancel) |
cancel_at_period_end | integer | Whether the subscription will cancel at period end: 0 (no) or 1 (yes) |
billing | integer | Billing type |
billing_cycle_anchor | string | null | ISO 8601 timestamp of the billing cycle anchor |
cancel_at | string | null | ISO 8601 timestamp when the subscription is scheduled to cancel |
canceled_at | string | null | ISO 8601 timestamp when the subscription was canceled |
canceled_by_email | string | null | Email of the user who canceled the subscription |
canceled_by_type | string | null | Who canceled: "Customer", "Admin/Collaborator", "System", "churn_prediction", or null |
cancel_reason | string | null | Reason provided for cancellation |
created | string | ISO 8601 timestamp when the subscription was created |
current_period_start | string | null | ISO 8601 timestamp of the current billing period start |
current_period_end | string | null | ISO 8601 timestamp of the current billing period end |
days_until_due | integer | null | Number of days before an invoice is due (for send-invoice billing) |
ended_at | string | null | ISO 8601 timestamp when the subscription ended |
expires_at | integer | null | Unix timestamp when the subscription will expire (null if auto-renewing) |
renews_at | integer | null | Unix timestamp when the subscription will next renew (null if not renewing) |
quantity | integer | null | Number of seats or units |
start_date | string | null | ISO 8601 timestamp when the subscription started |
trial_start | string | null | ISO 8601 timestamp when the trial started |
trial_end | string | null | ISO 8601 timestamp when the trial ends |
source | string | null | Deprecated source field |
backdate_start_date | string | null | ISO 8601 timestamp for backdated subscriptions |
metadata | object | null | Custom key-value metadata |
recommended_plan_id | integer | null | Recommended plan ID for upgrades |
agency_id | integer | null | Associated agency ID |
Gift Fields
| Field | Type | Description |
|---|---|---|
is_gift_donor | integer | Whether this subscription is a gift donation: 0 or 1 |
is_gift_recipient | integer | Whether this is a gift recipient subscription: 0 or 1 |
is_redeemed | integer | null | Whether the gift has been redeemed |
gift_code | string | null | Gift redemption code |
gift_recipient_email | string | null | Email of the gift recipient |
gift_recipient_first_name | string | null | Gift recipient's first name |
gift_recipient_last_name | string | null | Gift recipient's last name |
gift_message | string | null | Message from the gift donor |
gift_start_date | string | null | ISO 8601 timestamp when the gift starts |
gift_notification_sent | integer | Whether the gift notification was sent: 0 or 1 |
gift_donor_subscription_id | integer | null | ID of the donor's subscription |
Shipment Fields
| Field | Type | Description |
|---|---|---|
shipments_remaining | integer | null | Number of shipments remaining in the current period |
shipments_undeliverable | boolean | Whether shipments are undeliverable |
shipments_suspended_until | string | null | ISO 8601 timestamp until when shipments are suspended |
Churn Prediction Fields
Premium FeatureChurn Prediction Score is a premium add-on. Contact [email protected] to activate it for your account. Additional fees apply. These fields will be
nulluntil the feature is enabled.
| Field | Type | Description |
|---|---|---|
churn_risk_score | integer | null | Predicted churn risk score |
churn_risk_bucket | string | null | Churn risk category: "low", "medium", or "high" |
churn_prediction_created_at | integer | null | Unix timestamp when the prediction was created |
churn_prediction_updated_at | integer | null | Unix timestamp when the prediction was last updated |
Campaign Fields
| Field | Type | Description |
|---|---|---|
campaign_id | integer | null | Associated campaign ID |
campaign_key | string | null | Campaign key used for acquisition tracking |
Nested Objects
| Field | Type | Description |
|---|---|---|
customer | object | null | Full customer object (see Customer Webhooks) |
plan | object | null | Full plan object with nested product (see Plan Webhooks) |
coupon | object | null | Applied coupon (see below) |
default_source | object | null | Default payment method (see Payment Method Webhooks). The nested customer field is excluded to avoid circular nesting. |
latest_invoice | object | null | Most recent invoice with nested charge (see Invoice Webhooks). The nested subscription field is excluded to avoid circular nesting. |
address_shipping | object | null | Shipping address (see Address Webhooks) |
shipping_address | object | null | Same as address_shipping (deprecated naming, kept for backwards compatibility) |
schedules | object | null | Subscription schedules list (see below) |
default_tax_rates | object | null | Applied tax rates list (see below) |
Coupon Object (Nested)
| Field | Type | Description |
|---|---|---|
object | string | Always coupon |
object_id | string | External gateway coupon ID |
type | string | Coupon type (e.g., "subscription") |
amount_off | integer | null | Discount amount in cents |
code | string | Coupon code |
created | string | ISO 8601 timestamp |
currency | string | null | Three-letter ISO currency code |
duration | string | Duration: "once", "repeating", or "forever" |
duration_in_months | integer | null | Number of months (for repeating coupons) |
livemode | boolean | Whether this is a live coupon |
name | string | Coupon display name |
max_redemptions | integer | null | Maximum number of times the coupon can be used |
percent_off | float | null | Discount percentage |
redeem_by | string | null | ISO 8601 timestamp when the coupon expires |
times_redeemed | integer | Number of times the coupon has been used |
valid | boolean | Whether the coupon is still valid |
Schedules Object (Nested)
When present, schedules use a list format:
{
"object": "list",
"data": [
{
"object": "subscription_schedule",
"object_id": "sub_sched_XXXXXXXXXXXX",
"status": "active",
"phases": {
"object": "list",
"data": [
{
"object": "subscription_schedule_phase",
"start_date": "2026-01-01T12:00:00.000000Z",
"end_date": "2026-04-01T12:00:00.000000Z",
"coupon": null,
"plans": {
"object": "list",
"data": [{ "...": "phase plan objects" }]
}
}
]
}
}
]
}Previous Attributes (updated, changed, and trial_will_end events)
| Field | Type | Description |
|---|---|---|
previous_attributes | object | Contains only the publishable fields that changed, with their previous values before the update. |
Notes
- The subscription payload is the most deeply nested of all webhook resources — it includes full customer, plan (with product), invoice (with charge), payment method, and address objects.
statusis a string (e.g.,"active","canceled"), not an integer.auto_renewis computed: it istruewhen the subscription is active or trialing andcancel_at_period_endis0.expires_atandrenews_atare unix timestamps (integers), unlike most other date fields which use ISO 8601 strings.cancel_at_period_endis an integer (0or1), not a boolean.canceled_by_typemaps the internal value"Publisher"to"Admin/Collaborator"in the webhook payload.- The
default_sourceobject in the subscription payload excludes the nestedcustomerto avoid circular nesting. Similarly,latest_invoiceexcludes the nestedsubscription. - Both
shipping_address(deprecated) andaddress_shipping(current) are included in the payload with identical data for backwards compatibility. subscription.expiredis the event name shown to subscribers, even though it corresponds to the internal event codeSUBSCRIPTION_DELETED(305).
Related
- Webhooks Overview - Setup, signatures, and configuration
- Customer Webhooks - Customer object field descriptions
- Plan Webhooks - Plan and product object field descriptions
- Invoice Webhooks - Invoice object field descriptions
- Payment Method Webhooks - Payment method field descriptions
- Address Webhooks - Address object field descriptions
Updated 41 minutes ago
