# Change Plan

This endpoint allows you to replace a subscriber’s current package with a new one (upgrade or downgrade). Plan transitions follow Zotlo’s billing logic, including prorated upgrades, cycle preservation options, and deferred downgrades.

The service operates using the **POST** method.

## **How Plan Changes Work**

### **Upgrade**

When switching to a more expensive plan, Zotlo processes the change based on the **saveCycle** parameter.

**If `saveCycle = true` (Keep Billing Cycle)**

* The current billing cycle **does not change,** `expireDate`  **remains the same**.
* The unused portion of the current plan is **credited**.
* The user pays **only the price difference**.
* The new plan becomes active immediately **after a successful charge**.
* If the charge fails → upgrade does **not** happen; current plan continues.

**If `saveCycle = false` (Start New Cycle)**

* The current billing cycle is **terminated immediately**.
* The unused amount is **credited** to the new plan.
* A **full charge** for the new plan is attempted instantly.
* A **new billing cycle starts immediately** with a new expire date.
* If the charge fails → upgrade does **not** occur; the current plan continues unchanged.

{% hint style="info" %}
**Note:** Choosing `saveCycle = false` is ideal when you want users to “reset” into a new full cycle.
{% endhint %}

### **Downgrade**

When switching to a cheaper plan:

* The change **never happens immediately**.
* The new plan activates **at the next renewal**.
* The current billing cycle continues uninterrupted.
* **expireDate does not change**.
* No proration or credit calculation applies.

{% hint style="info" %}
**Note:** `saveCycle` is ignored for downgrades because downgrades always activate at the next renewal.
{% endhint %}

You can learn more about plan changes [👉 here](https://docs.zotlo.com/features/subscriptions/plan-changes)

{% hint style="info" %}
**Currency Matching Rule**

The new plan must use the same currency defined for the subscriber’s country. Example: A subscriber using USD in the USA can only switch to another USD plan.
{% endhint %}

### Discount Behavior on Plan Change

* The **unused value of remaining days** is always calculated based on the **actual amount paid by the customer**. If the current subscription includes a discount, the discounted price is used.
* `keepDiscount` parameter can be used to **preserve the existing discount** during an upgrade or downgrade.
  * `true` → current discount continues if the target plan supports it.
* To apply a **different discount**, send `discountCode` .\
  If a new discount is provided, the **existing discount is overridden** (only one discount can be active).
* **Discount cycles (redemption count)** increase **only on renewal**, not during plan changes.

<table data-header-hidden><thead><tr><th width="99.640625"></th><th></th></tr></thead><tbody><tr><td>Method</td><td><h4>  <mark style="color:blue;"><code>POST</code></mark></h4></td></tr><tr><td>URL</td><td><pre data-overflow="wrap" data-full-width="false"><code>https://api.zotlo.com/v1/payment/change-package
</code></pre></td></tr></tbody></table>

## **Request Parameters**

<table><thead><tr><th width="189.7109375">Field</th><th width="115.48828125">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>subscriberId</code></td><td>Required</td><td>The email or phone number the user provided when starting the subscription.</td></tr><tr><td><code>packageId</code></td><td>Required</td><td>The ID of the current active package. A successful response is returned only if the user has a subscription for this package.</td></tr><tr><td><code>newPackageId</code></td><td>Required</td><td>Package ID to switch to</td></tr><tr><td><code>changeType</code> </td><td>Required</td><td><code>upgrade</code> or <code>downgrade</code></td></tr><tr><td><code>saveCycle</code></td><td>Required</td><td><p>true → Upgrade with cycle continuity</p><p>false → Upgrade with a new billing cycle</p><p><sub><em>(This parameter has</em><em> </em><em><strong>no effect</strong></em><em> </em><em>for downgrades)</em></sub></p></td></tr><tr><td>keepDiscount</td><td>Optional</td><td><p>Indicates whether the current discount should be preserved during the plan change.</p><ul><li><code>true</code> → The existing discount is kept if the target plan supports it.</li><li><code>false</code> → The existing discount is not preserved.</li></ul></td></tr><tr><td>discountCode</td><td>Optional</td><td>The code of the discount to apply during the plan change (e.g., <code>NEWYEAR26</code>). If provided, the existing discount (if any) will be overridden.</td></tr><tr><td><code>subscriberIpAddress</code></td><td>Required</td><td>Subscriber’s IP address</td></tr><tr><td><code>redirectUrl</code></td><td>Optional</td><td>Required for 3DS flows, user is redirected here after authentication</td></tr><tr><td><code>platform</code></td><td>Optional</td><td>Example: <code>web</code>, <code>ios</code>, <code>android</code></td></tr></tbody></table>

## **Sample Request**

{% code overflow="wrap" %}

```js
POST https://api.zotlo.com/v1/payment/change-package HTTP/1.1
AccessKey: ••••••
AccessSecret: ••••••
Content-Type: application/json
ApplicationId: •
Language: ••

{
  "platform": "web",
  "subscriberId": "Z113322",
  "subscriberIpAddress": "212.154.57.216",
  "redirectUrl": "https://example.com",
  "changeType": "upgrade",
  "packageId": "zotlo.premium",
  "newPackageId": "zotlo.business",
  "saveCycle": "false", 
  "keepDiscount": "false"
}
```

{% endcode %}

{% hint style="info" %}
You can find your **AccessKey** and **AccessSecret** in the Zotlo Panel under **Developer Tools → API Keys**

Sending **ApplicationId** is optional.
{% endhint %}

## Successful Response

{% code overflow="wrap" %}

```json
{
    "meta": {
        "requestId": "6d2989e84793-REQ-5f32ad6851343",
        "httpStatus": 200
    },
    "result": {
        "profile": {
            "status": "active",
            "realStatus": "active",
            "subscriberId": "test@mail.com",
            "subscriptionId": 20338,
            "subscriptionType": "trial",
            "startDate": "2026-03-12 13:44:12",
            "expireDate": "2026-03-19 13:44:12",
            "renewalDate": "2026-03-19 13:44:12",
            "package": "package_1",
            "country": "US",
            "phoneNumber": null,
            "language": "en",
            "originalTransactionId": "ffad490c-6681-5c76-f63000cafc46",
            "lastTransactionId": "ffad490c-6681-5c178-f63000cafc46",
            "subscriptionPackageType": "single",
            "cancellation": null,
            "customParameters": {
                "saveCycle": true,
                "subscriberIpAddress": "111111"
            },
            "quantity": 1,
            "pendingQuantity": 0,
            "renewalFetchCount": 0,
            "freezeEndDate": null
        },
        "package": {
            "packageId": "package_1",
            "price": 0,
            "currency": "USD",
            "packageType": "subscription",
            "name": "package_1",
            "subscriptionPackageType": "single",
            "bundlePackages": [],
            "discount": {
                "code": "DISC75NOTRIAL",
                "type": "rate",
                "appliedRate": 75,
                "appliedAmount": "9.6699999999999999289 USD"
            }
        },
        "customer": null,
        "newPackage": null,
        "card": {
            "cardNumber": "42424242****4242",
            "expireDate": "12/30",
        },
        "response": {
            "isSuccess": true,
            "transactionId": "ffad490c-61-5c76-a178-f63000cafc46",
            "providerTransactionId": "pi_3TA9WwCbenXbI1ZL1CrILgpO",
            "customTransactionId": "",
            "statusCode": "S0000001",
            "statusMessage": "Ödeme işlemi başarıyla tamamlandı.",
            "paymentDate": "2026-03-12 01:44:08",
            "providerStatus": "Captured",
            "paymentStatus": "COMPLETE",
            "redirectUrl": null,
            "packageId": "package_1",
            "providerPaymentMethod": null,
            "networkDeclineCode": null,
            "networkDeclineMessage": null
        },
        "redirect": null,
        "paymentStatus": "COMPLETE",
        "paymentHash": "83f89c204fef73a34e4a04eb38f06f477f0",
        "oldOriginalTransactionId": "95271196-8438-d7637a100802"
    }
}
```

{% endcode %}

## Key **Response Fields**

<table><thead><tr><th width="205.5546875">Field</th><th>Description</th></tr></thead><tbody><tr><td>profile</td><td>Updated subscription profile after the change</td></tr><tr><td>package</td><td>Details of the new package and discount</td></tr><tr><td>response.isSuccess</td><td>Indicates whether any required payment was successful</td></tr><tr><td>card</td><td>Card information used for upgrade </td></tr><tr><td>expireDate</td><td>Updated expire date (only changes for successful upgrades)</td></tr></tbody></table>

## **Failed Response**

All failed responses follow the same standard error format.\
(See: [**Error Handling**](https://docs.zotlo.com/integrating-zotlo/api-reference/error-handling))

##
