Tenant provisioning change
Keep your SaaS tenants in sync with the features enabled on their subscription
A common problem with SaaS applications is that as they mature there is a disconnect between the features a customer has subscribed and paid for versus what they actually get.
It might be that they are paying for features that were never unlocked on their account. Which will result in less utility, a lower customer experience and possibly see the account churn.
Alternately they may have access to features they have not paid for due to an account manager enabling a feature on trial and then forgetting to actually charge them for it. In this case you will be unaware that there is an upsell opportunity available.
In either case, the upshot is that keeping your SaaS tenants in sync with subscriptions has a direct correlation with revenue. Bunny helps you keep in sync through a concept that we call tenant provisioning.
You can keep your tenants in sync by enabling a webhook from Bunny to notify you when subscriptions change or new features are added to a product.
Prerequisites Link to Prerequisites section
To enable tenant provisioning you will need the following
- A Platform set up in Bunny with a webhook URL and Provisioning enabled.
- A Product set up for the platform.
- An account with a subscription to the product. If you have not created a subscription yet then you can do this via the UI or by following this guide.
Triggering the webhookLink to Triggering the webhook section
Bunny sends the webhook to the webhook URL of the tenant’s platform whenever something changes what the tenant is entitled to:
- A subscription is activated, for example when a trial converts to paid
- A subscription is canceled, expires, or has its cancellation undone
- A user extends a subscription’s trial
- A quote is applied to the tenant’s subscriptions, for example a quantity change, upgrade or renewal
- A subscription is linked to a different tenant
- The features on a plan the tenant subscribes to are changed
- You call the tenantProvisioningSend mutation, which sends the current state on demand
Subscriptions in the pending state don’t trigger a webhook until they start.
The webhook is sent directly from the platform’s settings, not through a workflow. If the platform has a Webhook auth token, it’s sent as a bearer token in the Authorization header, and a Webhook signing key is used to sign the payload.
Your endpoint must respond with a 2xx status within 5 seconds. Bunny tries a failed request once more, then gives up.
You can debug webhooks and resend them from the webhook log, under Settings > Developers > Webhook logs.
The webhook payloadLink to The webhook payload section
When a provisioning change is triggered, the webhook is sent as an HTTP POST request with a JSON body. The body describes the tenant in your SaaS application that needs an update, its account and platform, and the features and subscriptions it currently has.
{
"type": "TenantProvisioningChange",
"payload": {
"tenant": {
"id": 311,
"code": "superdesk-123456",
"name": "Superdesk",
"subdomain": "superdesk",
"platform": {
"id": 1,
"code": "main",
"name": "Main"
},
"account": {
"id": 384,
"name": "Superdesk",
"billing_day": 14,
"contacts": [
{
"id": 464,
"first_name": "Meg",
"last_name": "Ledon",
"email": "meg@example.com"
}
]
}
},
"change": {
"features": [
{
"code": "users",
"name": "Users",
"value": null,
"quantity": 10,
"plan": { "code": "business", "name": "Business" },
"price_list": { "code": "business_monthly", "name": "Business Monthly" }
},
{
"code": "sso",
"name": "SSO",
"value": null,
"quantity": 0,
"plan": { "code": "business", "name": "Business" },
"price_list": { "code": "business_monthly", "name": "Business Monthly" }
},
{
"code": "api_calls",
"name": "API calls",
"value": "100000",
"quantity": 100000,
"plan": { "code": "api_addon", "name": "API add-on" },
"price_list": { "code": "api_addon_monthly", "name": "API add-on Monthly" }
},
{
"code": null,
"name": "Platform fee",
"value": "",
"quantity": 1,
"plan": { "code": "business", "name": "Business" },
"price_list": { "code": "business_monthly", "name": "Business Monthly" }
}
],
"subscriptions": [
{
"id": 31782,
"state": "active",
"start_date": "2026-01-14",
"end_date": "2027-01-13",
"trial_start_date": null,
"trial_end_date": null,
"plan": { "code": "business", "name": "Business" },
"price_list": { "code": "business_monthly", "name": "Business Monthly" },
"features": [
{ "code": "users", "name": "Users", "value": null, "quantity": 10 },
{ "code": "sso", "name": "SSO", "value": null, "quantity": 0 },
{ "code": null, "name": "Platform fee", "value": "", "quantity": 1 }
],
"addon": false,
"addon_subscriptions": [
{
"id": 31790,
"state": "active",
"start_date": "2026-03-14",
"end_date": "2027-01-13",
"trial_start_date": null,
"trial_end_date": null,
"plan": { "code": "api_addon", "name": "API add-on" },
"price_list": { "code": "api_addon_monthly", "name": "API add-on Monthly" },
"features": [
{ "code": "api_calls", "name": "API calls", "value": "100000", "quantity": 100000 }
],
"addon": true
}
]
}
]
}
}
}
How to use the tenant provisioning change webhook?Link to How to use the tenant provisioning change webhook? section
Create or update a tenant in your SaaS applicationLink to Create or update a tenant in your SaaS application section
Use the tenant.code attribute in the payload to find the tenant in your SaaS application that needs updating.
Optionally, if the tenant doesn’t exist in your platform then you could provision a new tenant.
The tenantCode is set when you create a subscription in Bunny.
If the tenant exists then the next step is to update the features and usage limits so that they are in sync with the subscription details.
Update features and set limitsLink to Update features and set limits section
change.features is the combined list of features the tenant should have right now, across all of its active and trial subscriptions, including add-ons. In most cases this is the only part of the payload you need. change.subscriptions lists each subscription with its own features, with add-on subscriptions nested under their parent in addon_subscriptions.
Each feature has a code, a name, a value and a quantity:
- For a quantity feature that is a chargeable unit, such as users,
quantityis the quantity on the subscription’s current charges. In the example, the tenant is limited to 10 users. - For other features on the plan,
valueis the value set on the plan (nullwhen none is set), andquantityis that value as a whole number, or0when it isn’t a number. In the example, the tenant has SSO and 100,000 API calls. - Each of the subscription’s current charges also appears. A charge that isn’t linked to a feature has a
nullcode and the charge’s name, like the Platform fee in the example; skip these.
Plan features only appear when the feature has Is provisioned to platform turned on. A feature that is missing from change.features should be turned off.
To prevent headaches when you scale up you should only allow access to features if they are enabled on a subscription.
For example, if a tenant wanted to try a new feature, then you would first create it in Bunny and add it to their subscription. Then trigger the provisioning webhook to enable the feature on the tenant.