Cancel subscriptions
Zuora client libraries version 3.x is in the Early Adopter phase. It integrates both the v1 API and Quickstart API into a unified set of libraries. We recommend that you try our client libraries 3.x to start testing or integrating with Zuora.
Subscriptions sometimes need to end. In Zuora terms, ending a subscription means cancelation. Canceling a subscription stops new invoice items from being generated by all the charges belonging to the canceled subscription.
Cancel options
You can submit a subscription cancelation today but have the cancelation take effect on a date in the future or in the past.
For example, consider a monthly subscription billed on the 1st of each month. After a few months the your customer clicks the "Cancel subscription" button on the 15th, you should immediately capture their action as an order with the order action type of "CancelSubscription" with one of the cancelation options:
Cancelation options (cancellationPolicy enums) | Result |
---|---|
EndOfCurrentTerm | Cancelation will take effect at the end of the current term, and your customers will continue to be billed as appropriate until then. Subscription will not renew even if the "auto-renew" option is enabled. |
EndOfLastInvoicePeriod | Cancelation will take effect on the next billing day. If no additional orders are created against this subscription, no further invoices will be generated for this subscription. |
SpecificDate | Cancelation will take effect on the specified date. You must specify the cancellationEffectiveDate field if you choose this option. It may result in a credit memo being created for any prepaid amounts covering the unused period after the specified date. For example, suppose 15th of a month is specified as the cancelation date. If your customer has prepaid through the end of the month, a bill run for this account will generate a credit memo for the unused period (16th till the end of the month). |
Which cancelation option you choose depends on your contract with your customer.
Some useful tips about canceling a subscription:
- A cancelation is for the specified subscription, not for the billing account.
- There is no account-level cancelation. You need to iterate through every subscription and cancel them individually.
- Canceling one subscription on a billing account with multiple subscriptions has no impact on the other subscriptions.
- The impact of cancelation is to prevent the charges from generating new invoice items once the cancelation date is in the past.
- Subscription cancelation has no impact on existing invoices or payments. You can still collect payment on any unpaid invoices that contain charges from that subscription, and these invoices will be added to the Aging as usual based on their payment terms. Check your company's policy for writing off unpaid invoices on canceled subscriptions.
End-user flow
Suppose that today's date is December 20, 2024. For a specific subscription on the My Account page, your customers click the Cancel Subscription button to cancel a specific subscription associated with their accounts.
Sample code
The sample codes below create a subscription cancelation order with the following information:
orderDate
: Today's date,2024-12-20
.existingAccountNumber
:A00000006
, which is the number of the your customer's billing account.subscriptionNumber
: The number of the subscription we want to cancel. We useA-S00000038
as an example.triggerName
:ContractEffective
. It's the most commonly used trigger name.cancellationPolicy
:EndOfLastInvoicePeriod
. The subscription is canceled on the last day of the last invoice period.processingOptions
:runBilling
:true
. The invoice is generated for this cancel subscription order action.colletPayment
:false
. No payment will be applied to the generated invoice.
curl --request POST \
--url https://rest.test.zuora.com/v1/orders \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer {your_token}" \
--data '{
"orderDate": "2024-12-20",
"existingAccountNumber": "A00000006",
"subscriptions": [
{
"subscriptionNumber": "A-S00000038",
"orderActions": [
{
"type": "CancelSubscription",
"triggerDates": [
{
"name": "ContractEffective",
"triggerDate": "2024-12-20"
}
],
"cancelSubscription": {
"cancellationPolicy": "EndOfLastInvoicePeriod"
}
}
]
}
],
"processingOptions": {
"runBilling": true,
"collectPayment": false
}
}'
final CreateOrderRequest createOrderRequest = new CreateOrderRequest()
.category(OrderCategory.NEWSALES)
.orderDate(LocalDate.of(2024,12,20))
.existingAccountNumber("A00000006")
.description("Order cancel a subscription")
.subscriptions(Arrays.asList(new CreateOrderSubscription()
.subscriptionNumber("A-S00000038")
.orderActions(Arrays.asList(new CreateOrderAction()
.type(OrderActionType.CANCELSUBSCRIPTION)
.triggerDates(Arrays.asList(
new TriggerDate()
.name(TriggerDateName.CONTRACTEFFECTIVE)
.triggerDate(LocalDate.of(2024,12,20))
))
.cancelSubscription(new OrderActionCancelSubscription()
.cancellationPolicy(SubscriptioncancellationPolicy.ENDOFLASTINVOICEPERIOD))
))))
.processingOptions(new ProcessingOptionsWithDelayedCapturePayment()
.runBilling(true)
.collectPayment(false)
);
CreateOrderResponse response = zuoraClient.ordersApi().createOrderApi(createOrderRequest).executeWithOrderMetrics();
System.out.print(response);
const triggerDate = new TriggerDate();
triggerDate.name = "ContractEffective";
triggerDate.triggerDate = new Date().toISOString().split('T')[0];
const orderActionCancelSubs = new OrderActionCancelSubscription();
orderActionCancelSubs.cancellationPolicy = 'EndOfLastInvoicePeriod';
const cancelSubsOrderAction = new CreateOrderAction();
cancelSubsOrderAction.type = 'CancelSubscription';
cancelSubsOrderAction.triggerDates = [triggerDate];
cancelSubsOrderAction.cancelSubscription = orderActionCancelSubs;
const createOrderSubcription = new CreateOrderSubscription();
createOrderSubcription.subscriptionNumber = 'A-S00000038';
createOrderSubcription.orderActions = [cancelSubsOrderAction];
const processingOptions = new ProcessingOptionsWithDelayedCapturePayment();
processingOptions.runBilling = true;
processingOptions.collectPayment = false;
const request = new CreateOrderRequest();
request.existingAccountNumber = 'A00000006';
request.category = 'NewSales';
request.orderDate = new Date().toISOString().split('T')[0];
request.description = 'Cancel Subscription Order';
request.subscriptions = [createOrderSubcription];
request.processingOptions = processingOptions;
const createOrderResp = await zuoraClient.ordersApi.createOrder(request);
console.log(JSON.stringify(createOrderResp, (k, v) => v ?? undefined, 2));
def cancel_subscription(client, subscription_name, cancellation_date=None):
"""
Parameters:
client (ZuoraClient): An authenticated instance of the Zuora client.
subscription_name (str): The name of the subscription to cancel.
cancellation_date (str, optional): The effective cancellation date in 'YYYY-MM-DD' format.
If not provided, the cancellation will be effective on the next billing day (EndOfLastInvoicePeriod)
"""
try:
# Find the subscription objects by name to verify they exists
sub_verify = client.object_queries_api().query_subscriptions(filter=['name.EQ:%s' % subscription_name,'status.EQ:%s' % 'Active'])
if len(sub_verify.data) == 0:
return {
"error": f"Subscription with name '{subscription_name}' not found or query failed."
}
account_id = sub_verify.data[0].account_id
# Prepare cancellation payload
cancellation_payload = {
"cancellationPolicy": "SpecificDate" if cancellation_date else "EndOfLastInvoicePeriod",
}
if cancellation_date:
# Validate and add the cancellation date
try:
cancellation_date_obj = datetime.strptime(cancellation_date, "%Y-%m-%d")
cancellation_payload["cancellationEffectiveDate"] = cancellation_date_obj.strftime("%Y-%m-%d")
except ValueError:
return {"error": "Invalid cancellation date format. Use 'YYYY-MM-DD'."}
create_order_request = CreateOrderRequest(
order_date=date.today().strftime('%Y-%m-%d'),
existingAccountId=account_id,
subscriptions=[{
'subscriptionNumber': subscription_name,
'order_actions': [{
'type': 'CancelSubscription',
'cancel_subscription': cancellation_payload
}]
}],
)
# Submit the order request
response = client.orders_api().create_order(create_order_request)
print("Cancellation Order Created Successfully!")
print(response.to_json())
return
except Exception as e:
print(f"Failed to create cancellation order: {e}")
return {"error": f"An exception occurred: {str(e)}"}
if __name__ == '__main__':
cancel_subscription(client, subscription_name='A-S00000038')
If the request succeeds, you will get a response similar to the following snippet:
{
"success": true,
"orderNumber": "O-00000882",
"accountNumber": "A00000006",
"status": "Completed",
"subscriptionNumbers": [
"A-S00000038"
]
}
Troubleshooting
- If you get authentication errors, it is likely you are trying to connect to the wrong tenant. The correct base URL depends on your assigned data center and environment. Check Zuora Data Centers to locate the correct base URL.
- Don't assume the cancelation will always succeed. For example, you might get an error if you try to cancel a subscription before it starts or after it ends.