Plans
Overview
Plan webhooks notify your application when subscription plans are created or updated. Use these events to sync pricing information, update your catalog, and track plan configuration changes.
Use Cases
- Sync plan pricing and details to your storefront
- Track plan configuration changes (pricing, intervals, trial periods)
- Update external billing systems when plans change
- Monitor plan activation and archival
Events
| Event | Description |
|---|---|
plan.created | A new subscription plan was created |
plan.updated | An existing plan was modified |
plan.created
Triggered When
- A plan is created via the Core API
- A plan is created from the Platform
Payload
{
"type": "plan.created",
"id": "evt_a1B2c3D4e5F6g7H8i9J0k1L2",
"created": 1704067200,
"data": {
"object": {
"id": 100001,
"product_id": 200001,
"parent_plan_id": null,
"object_id": "plan_XXXXXXXXXXXXXX",
"active": "active",
"type": "regular",
"amount": 1999,
"recognized_revenue_type": null,
"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": 7,
"ip_addresses": null,
"domains": null,
"created_at": "2026-01-01T12:00:00.000000Z",
"updated_at": "2026-01-01T12:00:00.000000Z",
"site_ids": [1],
"product": {
"id": 200001,
"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]
}
}
}
}plan.updated
Triggered When
- A plan is updated via the Core API
- A plan is updated from the Platform
- A plan is activated or archived
Payload
The payload includes a previous_attributes object containing only the fields that changed.
{
"type": "plan.updated",
"id": "evt_b2C3d4E5f6G7h8I9j0K1l2M3",
"created": 1704153600,
"data": {
"object": {
"id": 100001,
"product_id": 200001,
"parent_plan_id": null,
"object_id": "plan_XXXXXXXXXXXXXX",
"active": "archived",
"type": "regular",
"amount": 1999,
"recognized_revenue_type": null,
"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": 7,
"ip_addresses": null,
"domains": null,
"created_at": "2026-01-01T12:00:00.000000Z",
"updated_at": "2026-01-02T12:00:00.000000Z",
"site_ids": [1],
"product": {
"id": 200001,
"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]
}
},
"previous_attributes": {
"active": "active",
"updated_at": "2026-01-01T12:00:00.000000Z"
}
}
}Payload Fields
Plan Object
| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the plan |
product_id | integer | Parent product ID |
parent_plan_id | integer | null | Parent plan ID (for child/tiered plans) |
object_id | string | null | External gateway identifier (e.g., Stripe plan ID) |
active | string | Plan status: "active" or "archived" |
type | string | null | Plan type (e.g., "regular", "membership") |
amount | integer | Price in cents (e.g., 1999 = $19.99) |
recognized_revenue_type | string | null | Revenue recognition method: "time" or "shipments" |
renewal_strategy | string | null | Renewal strategy (e.g., "regular") |
auto_renew | boolean | Whether the plan auto-renews at the end of each interval |
available_online | boolean | Whether the plan is available for online purchase |
allow_send_invoice | boolean | Whether invoices can be sent for this plan |
auto_uncollectible_days | integer | null | Days before an unpaid invoice is marked uncollectible |
invoice_upcoming_notification | integer | null | Days before renewal to send an upcoming invoice notification |
currency | string | Three-letter ISO currency code |
countries | array | null | Array of ISO country codes where the plan is available |
entitlements | array | null | Entitlement keys granted by this plan |
interval | string | Billing interval: "minute", "hour", "day", "week", "month", or "year" |
interval_count | integer | Number of intervals between billings (e.g., 3 with "month" = quarterly) |
is_imported | boolean | Whether the plan was imported from an external system |
is_hidden | boolean | Whether the plan is hidden from customers |
is_donation | boolean | Whether the plan is a donation plan |
metadata | object | null | Custom key-value metadata |
member_seat_capacity | integer | null | Maximum number of member seats (for membership plans) |
nickname | string | null | Plan display name |
name_internal | string | null | Internal plan name (not shown to customers) |
refundable | boolean | Whether payments on this plan are refundable |
shipments_per_interval | integer | null | Number of shipments per billing interval (for shipment-based plans) |
description | string | null | Plan description |
trial_period_days | integer | null | Number of free trial days |
ip_addresses | array | null | IP address restrictions for access |
domains | array | null | Domain restrictions for access |
site_ids | array | Array of site IDs where the plan is available (derived from the parent product) |
product | object | Nested product object (see Product Webhooks for field descriptions) |
created_at | string | ISO 8601 timestamp when the plan was created |
updated_at | string | ISO 8601 timestamp when the plan was last modified |
Previous Attributes (updated events only)
| Field | Type | Description |
|---|---|---|
previous_attributes | object | Contains only the fields that changed, with their previous values before the update. Only includes publishable fields. |
Notes
- The Plan object does not include an
objectfield in the payload (unlike most other webhook resources). It uses the top-leveltypefield to identify the event. activeis a string ("active"or"archived"), not a boolean.intervalis a string (e.g.,"month","year"), not an integer.amountis in cents (e.g.,1999= $19.99).- There is no
plan.deletedevent — plans are archived (active: "archived") rather than deleted, which triggers aplan.updatedevent. - The nested
productobject uses the same structure as Product Webhooks but does not include anobjectfield.
Related
- Webhooks Overview - Setup, signatures, and configuration
- Product Webhooks - Product events and field descriptions
- Subscription Webhooks - Subscription events that reference plans
Updated about 4 hours ago
