HTTPS webhooks for SaaS subscription lifecycle
For Software / SaaS products, Zenofy POSTs JSON to the webhook URL configured on the product. Each request includes an "event" string and subscription fields your backend can use to sync access.
Enter the endpoint on the product edit/create form (Webhook URL for SaaS). Optionally set a webhook secret; when present, Zenofy sends it as the X-Webhook-Secret header, same idea as the order webhook integration.
Events are separate from the Integrations → Webhook payloads: here the names are subscription.created, subscription.activated, subscription.renewed, subscription.suspended, subscription.canceled, and subscription.expired.
Use Content-Type: application/json; respond with HTTP 2xx quickly and process asynchronously if needed. Delivery is best-effort. Failed deliveries may be recorded for operators—check Zenofy webhook logs when troubleshooting.
Fields such as dates and nested order may vary with serialization; always treat payloads as backward-compatible — new fields may appear over time.
HTTP request
POST · Content-Type: application/json · optional X-Webhook-Secret
POST /your-endpoint HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-Webhook-Secret: your-shared-secret
{ ... }
Events and "event" values
| event | When (typical) |
|---|---|
subscription.created | Subscription row created with the originating order |
subscription.activated | First successful payment completed; access period active |
subscription.renewed | Renewal payment succeeded; period extended (see renewal object) |
subscription.suspended | Recurring product schedule: unpaid after billing period crosses the configured “suspend after period end” threshold |
subscription.canceled | Merchant canceled the subscription from the dashboard (or related refund/cancel flows) |
subscription.expired | Paid period lapsed without renewal — immediately if no suspend/expire schedule, or when “expire after period end” days elapse after suspend |
Payload shape (fields)
event— one of the subscription.* values abovesubscriptionId,productId,productName,merchantIdstatus— subscription status (e.g. PENDING, ACTIVE, SUSPENDED, CANCELED, EXPIRED)billingCycle— snapshot at subscription time (e.g. WEEKLY, MONTHLY, YEARLY)currentPeriodStart,currentPeriodEnd— nullable dates for the billed period where applicablecustomerEmail— normalized/lowercase email for matching renewalscustomerNameSnapshotisRenewal— boolean;truewheneventis subscription.renewedrenewal— object for renewed events:renewalOrderId,previousPeriodEnd,newPeriodEnd(ISO 8601 strings); otherwise often nullinitialOrderId,lastPaidOrderId,pendingOrderIdorderId— id associated with this notification contextorder— when an order snapshot is included:status,totalAmount,currency,paidAt,customer(name, email, phoneNumber); may be null
Sample JSON bodies
subscription.activated (illustrative)
{
"event": "subscription.activated",
"subscriptionId": "683a1b2c3d4e5f6789012345",
"productId": "507f1f77bcf86cd799439011",
"productName": "My SaaS plan",
"merchantId": "merchantUserIdExample",
"status": "ACTIVE",
"billingCycle": "MONTHLY",
"currentPeriodStart": "2026-05-01T10:00:00+02:00",
"currentPeriodEnd": "2026-06-01T10:00:00+02:00",
"customerEmail": "buyer@example.com",
"customerNameSnapshot": "Ada Buyer",
"isRenewal": false,
"renewal": null,
"initialOrderId": "673f92b2c3d94a0012abcd01",
"lastPaidOrderId": "673f92b2c3d94a0012abcd01",
"pendingOrderId": null,
"orderId": "673f92b2c3d94a0012abcd01",
"order": {
"status": "PAID",
"totalAmount": 99.99,
"currency": "MZN",
"paidAt": "2026-05-01T10:42:18+02:00",
"customer": {
"name": "Ada Buyer",
"email": "buyer@example.com",
"phoneNumber": "+258840000000"
}
}
}
subscription.renewed — note renewal.previousPeriodEnd and renewal.newPeriodEnd
{
"event": "subscription.renewed",
"subscriptionId": "683a1b2c3d4e5f6789012345",
"productId": "507f1f77bcf86cd799439011",
"productName": "My SaaS plan",
"merchantId": "merchantUserIdExample",
"status": "ACTIVE",
"billingCycle": "MONTHLY",
"currentPeriodStart": "2026-05-01T10:00:00+02:00",
"currentPeriodEnd": "2026-07-01T10:00:00+02:00",
"customerEmail": "buyer@example.com",
"customerNameSnapshot": "Ada Buyer",
"isRenewal": true,
"renewal": {
"renewalOrderId": "673f92b2c3d94a0012abcd99",
"previousPeriodEnd": "2026-06-01T10:00:00+02:00",
"newPeriodEnd": "2026-07-01T10:00:00+02:00"
},
"initialOrderId": "673f92b2c3d94a0012abcd01",
"lastPaidOrderId": "673f92b2c3d94a0012abcd99",
"pendingOrderId": null,
"orderId": "673f92b2c3d94a0012abcd99",
"order": {
"status": "PAID",
"totalAmount": 99.99,
"currency": "MZN",
"paidAt": "2026-06-05T14:20:00+02:00",
"customer": {
"name": "Ada Buyer",
"email": "buyer@example.com",
"phoneNumber": "+258840000000"
}
}
}
Merchant overview — product setup, renewals, and Catalog → SaaS Subscriptions
All tutorials