> ## Documentation Index
> Fetch the complete documentation index at: https://developers.appstle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Appstle Memberships API changelog

> Dated changes to the Appstle Memberships Admin API, Storefront API and Partner Framework: new endpoints, fields and values, and what to do about each.

This page lists every developer-facing change to the Memberships Admin API, the Storefront API and the Partner Framework, newest first. Each entry is dated the day the change reached production.

* **Additive changes** (new endpoints, new response fields, new optional parameters, new values of an existing field) don't break a client that ignores what it doesn't know. Parse JSON leniently and don't treat enum fields as closed lists.
* Entries tagged **Action required** may need a code change in your integration. Each one says who is affected and what to do.
* The API reference is generated from the live API, so it always shows the current contract. The Admin API base URL is `https://membership-admin.appstle.com`, and Storefront API paths start with `/memberships/cp/api/`.

**November 2, 2026**

Pagination links, reactivated memberships, failed upgrades

## Pagination `Link` URLs start with `https://`

`GET /api/external/v2/subscription-contract-details`, `GET /api/external/v2/subscription-billing-attempts/past-orders` and `GET /api/external/v2/subscription-billing-attempts/past-orders/report` return a `Link` header with the URLs of the first and last pages, and of the next and previous pages when they exist. Until now, those URLs started with `http://`, even when you called `https://membership-admin.appstle.com`. Following one sent the request over plain HTTP and got a `301` redirect to the same URL on `https://`. They now start with `https://`.

**Who is affected:** clients that follow `Link` URLs as they are. Their requests now go straight to HTTPS, without the redirect. If your code rewrites `http://` to `https://` before it follows a `Link` URL, you can drop that step; keeping it does no harm.

Reference: [Get membership contract list](https://developers.appstle.com/memberships-admin-api/membership-contracts/get-membership-contract-list.md), [Get past orders](https://developers.appstle.com/memberships-admin-api/billing-&-orders/get-past-orders.md), [Get paginated past or upcoming orders report](https://developers.appstle.com/memberships-admin-api/billing-&-orders/get-paginated-past-or-upcoming-orders-report.md).

## A reactivated membership with an unpaid renewal gets its customer tag once the renewal is paid

This applies when `PUT /api/external/v2/subscription-contracts-update-status` or `PUT /memberships/cp/api/subscription-contracts-update-status` sets a cancelled membership to `ACTIVE`, the membership's last billing attempt failed for a cycle that's already due, and the store's **Failed Payment Recovery** settings have **Retry Attempts** set and **Remove customer tag after last retry attempt** turned on. Reactivating the membership charges the customer for the missed cycle right away, as the October 18 and October 26 entries describe. Until now, Appstle also added the plan's customer tag at reactivation, so the customer had the tag, and whatever the store grants with it, through the retries of a renewal they hadn't paid.

Now Appstle adds the tag once a charge for the membership succeeds: the charge made at reactivation, or one of its retries. Until then, the membership's `status` is `"active"` and its `dunning` field is `true`. The **Membership Contract Activated** entry in its activity log shows `Added once the missed renewal is paid` under `membershipTags`. In every other case, Appstle still adds the tag at reactivation.

**Who is affected:** integrations and storefront code that set a cancelled membership to `ACTIVE`, and anything that grants access from the plan's customer tag.

**Action required:** none for the call. If you grant access from the plan's customer tag, expect the tag once the charge succeeds, not when the call returns.

Reference: [Update membership contract status (pause/resume/activate)](https://developers.appstle.com/memberships-admin-api/membership-contracts/update-membership-contract-status-pauseresumeactivate.md), [Update membership contract status](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-membership-contract-status.md), [Dunning-state tags](https://developers.appstle.com/memberships/metafields-and-tags.md#dunning-state-tags).

## A failed upgrade payment restores the membership's billing schedule

This applies when `PUT /api/external/v2/subscription-contract-update-variant` or `PUT /memberships/cp/api/subscription-contract-update-variant` moves an active membership to a higher-priced variant, the store has **Immediately charge/refund members on membership upgrade/downgrade** turned on, the Admin API call doesn't send `skipBilling=true`, and the charge for the upgrade fails. Appstle already moved the membership back to its old variant and the old plan's customer tag. Until now, the membership kept the billing and delivery frequency and the next billing date that the plan change set. For example, a monthly membership whose upgrade to a yearly plan failed had its next renewal a year away.

Now Appstle also restores the billing and delivery frequency and the next billing date from before the call, and rebuilds the upcoming orders on that schedule. If the restored next billing date is less than one day and five minutes away, or has passed, Appstle sets it to one day and five minutes after the rollback instead.

**Who is affected:** integrations and storefront code that upgrade memberships with these calls and read `billingPolicyInterval`, `billingPolicyIntervalCount`, `deliveryPolicyInterval`, `deliveryPolicyIntervalCount` or `nextBillingDate` afterwards.

**Action required:** none.

Reference: [Update product variant in membership contract](https://developers.appstle.com/memberships-admin-api/membership-contracts/update-product-variant-in-membership-contract.md), [Update line item variant](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-line-item-variant.md).

**November 1, 2026**

Partner tokens, calls to partner endpoints

## Partner tokens work on any plan again

Admin API calls made with a scoped partner token work whether or not the store's plan includes API access. From August 19, 2026, they returned `401` with the `detail` `You do not have permission to access the External API. Please contact the Appstle Membership Support team.` on a plan without it. Keys a merchant creates under **API Access** still need a plan that includes API access.

## Appstle authenticates its connect and verify calls to your app

Appstle's calls to your `/appstle/connect` and `/appstle/verify` endpoints now carry the same headers as its calls to `/appstle/approved` and your disconnect endpoint: `X-Partner-Timestamp` and `X-Partner-Signature` in HMAC-SHA256 mode, and `X-Partner-Secret` in Partner Secret mode if you gave Appstle an outbound secret. Until now, the Nonce Handshake calls to these two endpoints carried no authentication header, and the Simple Token Exchange call to `/appstle/connect` carried none in Partner Secret mode.

**Who is affected:** partners whose `/appstle/connect` or `/appstle/verify` endpoint accepts requests without checking that they come from Appstle.

**Action required:** none. You can now check these headers on `/appstle/connect` and `/appstle/verify` as well.

Reference: [Authentication: Partner integrations](https://developers.appstle.com/memberships/authentication.md#partner-integrations), [Partner integration](https://developers.appstle.com/memberships/partner-integration.md#authentication).

**October 30, 2026**

Plans, billing intervals, selling plan products

## Plans report whether they're excluded from upgrades and downgrades

* `GET /api/external/v2/subscription-groups` and `GET /api/external/v2/subscription-groups/{subscriptionGroupId}` return `excludedFromUpgradeDowngrade` (boolean) on each entry in `subscriptionPlans`.
* `GET /api/external/v2/subscription-contract-details/billing-interval` returns `excludedFromUpgradeDowngrade` (boolean).
* `GET /memberships/cp/api/subscription-groups/all-selling-plans` returns `excludedFromUpgradeDowngrade` (boolean) on each plan.
* `GET /memberships/cp/api/data/selling-plan-products` accepts an optional `sellingPlanIds` query parameter (string). For a signed-in customer, the list now leaves out plans the merchant excluded from upgrades and downgrades. A plan stays in the list when its ID is in `sellingPlanIds`, so pass the IDs of the plans the member is on (separated by `|` or `,`) and the member still sees their own plan.

Reference: [Get a membership plan group](https://developers.appstle.com/memberships-admin-api/membership-plans/get-a-membership-plan-group.md), [List all membership plan groups](https://developers.appstle.com/memberships-admin-api/membership-plans/list-all-membership-plan-groups.md), [Get all selling plans](https://developers.appstle.com/memberships-customer-portal-api/membership-plans/get-all-selling-plans.md), [List products attached to selling plans](https://developers.appstle.com/memberships-customer-portal-api/product-&-inventory-data/list-products-attached-to-selling-plans.md).

## Customers can't switch into an excluded plan

`PUT /memberships/cp/api/subscription-contract-update-variant` returns `400` with the title `UserGeneratedError:This membership plan isn't available for upgrade or downgrade` when a signed-in customer switches to a variant that belongs only to plans the merchant excluded from upgrades and downgrades. A variant in a plan the membership is already on still works. The Admin API call `PUT /api/external/v2/subscription-contract-update-variant` doesn't check the exclusion.

**Who is affected:** storefronts that offer every variant of a membership product as an upgrade or downgrade choice.

**Action required:** build the choices from `GET /memberships/cp/api/data/selling-plan-products`, as above, and handle `400` on the switch by showing the `title`.

Reference: [Update line item variant](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-line-item-variant.md).

**October 26, 2026**

Reactivating a cancelled membership

## Reactivating a cancelled membership can retry a billing attempt that ran out of retries

This applies when `PUT /api/external/v2/subscription-contracts-update-status` or `PUT /memberships/cp/api/subscription-contracts-update-status` sets a cancelled membership to `ACTIVE`, the membership's last billing attempt failed for a cycle that's already due after using up the retries set under the store's **Failed Payment Recovery** settings, and the membership has no upcoming order to copy. Appstle now retries the failed billing attempt once. If that charge fails, Appstle takes the action the store chose under **Once the membership reaches the maximum number of failures**, such as cancelling the membership again. Until now, nothing was charged in this case. A store without **Failed Payment Recovery** settings still charges nothing.

**Who is affected:** integrations and storefront code that set a cancelled membership to `ACTIVE`.

**Action required:** treat setting a cancelled membership to `ACTIVE` as a charge. Do it only when the customer has agreed to pay for the cycle they missed.

Reference: [Update membership contract status (pause/resume/activate)](https://developers.appstle.com/memberships-admin-api/membership-contracts/update-membership-contract-status-pauseresumeactivate.md), [Update membership contract status](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-membership-contract-status.md).

**October 18, 2026**

Reactivating a cancelled membership

## Reactivating a cancelled membership retries its failed billing attempt

When `PUT /api/external/v2/subscription-contracts-update-status` or `PUT /memberships/cp/api/subscription-contracts-update-status` sets a cancelled membership to `ACTIVE` and the membership's last billing attempt failed, Appstle tries to charge the customer for the unpaid cycle. Until now, it billed a copy of the membership's next upcoming order right away. Cancelling deletes the upcoming orders, so often nothing was charged.

Now, when the failed billing attempt is for a cycle that's already due and hasn't used up the retries set under the store's **Failed Payment Recovery** settings, Appstle retries that billing attempt itself and turns its retries back on. If this charge fails too, the store's **Failed Payment Recovery** settings decide what happens next, as for any failed renewal. The membership's activity log shows a **Retry Enabled** entry. In every other case, Appstle still bills a copy of the next upcoming order, or nothing if there is none.

**Who is affected:** integrations and storefront code that set a cancelled membership to `ACTIVE`.

**Action required:** treat setting a cancelled membership to `ACTIVE` as a charge. Do it only when the customer has agreed to pay for the cycle they missed.

Reference: [Update membership contract status (pause/resume/activate)](https://developers.appstle.com/memberships-admin-api/membership-contracts/update-membership-contract-status-pauseresumeactivate.md), [Update membership contract status](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-membership-contract-status.md).

**September 23, 2026**

API keys

## Merchants can revoke the legacy API key

Under **API Access** in the Appstle admin, the **Legacy API Key** now has a **Revoke** button. After a merchant revokes it, every request that sends that key returns `401` with `Content-Type: application/problem+json` and the `detail` `Invalid API key.`, the same response as any other unknown legacy key (see [Authentication errors](https://developers.appstle.com/memberships/authentication.md#authentication-errors)). Revoking can't be undone, and Appstle doesn't issue a replacement legacy key. Keys that start with `apst_` aren't affected.

**Who is affected:** integrations that still send the key shown on the **Legacy API Key** card.

**Action required:** create an `apst_` key under **API Access** and move your integration to it before the merchant revokes the legacy key.

**September 17, 2026**

Error responses

## Error responses use `appstle.com` problem types and hide internal messages

The `application/problem+json` error responses changed in two ways:

* The `type` of an error now starts with `https://appstle.com/problem/`, for example `https://appstle.com/problem/problem-with-message`. Before, it started with a different base URL.
* `detail` no longer carries internal error text. A server error (`5xx`) has the `detail` `An unexpected error occurred. Please try again later.` A `400` for a path or query parameter of the wrong type, such as a contract ID that isn't a number, has the `detail` `Unexpected runtime exception`. A `400` for a request body that isn't valid JSON has the `detail` `Unable to convert http message`.

**Who is affected:** clients that match on the `type` URL, or on the text of `detail` in these errors.

**Action required:** stop matching on `type`. Branch on `status`, and on `errorKey` or `title` where the body has them. Treat `detail` in these errors as a generic message.

**September 14, 2026**

Pickup locations, product data

## Pickup locations are no longer available to customers

`GET /memberships/cp/api/delivery-profiles/get-locations` returns `403` with the `detail` `Access is denied` for a signed-in customer. Only the merchant's admin session can call it. Until now, a signed-in customer could.

**Who is affected:** storefront code that loads the store's pickup locations for customers.

**Action required:** stop calling it from the storefront.

## Product data for customers covers only products on the Online Store

For a signed-in customer, the Storefront API product calls now cover only products that are active and published to the Online Store sales channel:

* `GET /memberships/cp/api/data/products` and `GET /memberships/cp/api/data/selling-plan-products` leave other products out of `products` and `productHandleData`.
* `GET /memberships/cp/api/data/product` and `GET /memberships/cp/api/data/product-selling-plan-variant` return `200` with an empty body for any other product.
* `GET /memberships/cp/api/data/variant-contextual-pricing` returns `400` with the title `UserGeneratedError:Product is not available` for a variant of any other product.
* `POST /memberships/cp/api/product-swaps-by-variant-groups` returns the same `400` when a variant in the request or in its swap options belongs to any other product.

**Who is affected:** storefronts that show members draft or archived products, or products published only to other sales channels, such as a headless storefront's own channel.

**Action required:** publish the products members should see to the Online Store sales channel. Handle `400` on the pricing and swap calls by showing the `title`.

Reference: [Get product details by ID](https://developers.appstle.com/memberships-customer-portal-api/product-&-inventory-data/get-product-details-by-id.md), [Get product with selling plans and variants](https://developers.appstle.com/memberships-customer-portal-api/product-&-inventory-data/get-product-with-selling-plans-and-variants.md), [Search membership-enabled products with pagination](https://developers.appstle.com/memberships-customer-portal-api/product-&-inventory-data/search-membership-enabled-products-with-pagination.md), [List products attached to selling plans](https://developers.appstle.com/memberships-customer-portal-api/product-&-inventory-data/list-products-attached-to-selling-plans.md), [Get contextual pricing for a variant by currency](https://developers.appstle.com/memberships-customer-portal-api/product-&-inventory-data/get-contextual-pricing-for-a-variant-by-currency.md), [Get product swap options by variant](https://developers.appstle.com/memberships-customer-portal-api/product-swap-rules/get-product-swap-options-by-variant.md).

**September 13, 2026**

Request parameters and bodies

## Storefront requests can't name another customer or store

A Storefront API request that names a `customerId` other than the signed-in customer's returns `403` with the `detail` `Customer does not match the authenticated session`. This covers `customerId` and `customer_id` in the path (for example `GET /memberships/cp/api/subscription-contract-details/shopify/customer/{customerId}/payment-methods`), in the query string and in the request body. A request body whose `shop` names another store returns `403` with the `detail` `Shop does not match the authenticated session`.

**Who is affected:** storefront code that sends a `customerId` or `shop` that isn't the signed-in customer's and the store's own, for example `PUT /memberships/cp/api/subscription-contract-details`.

**Action required:** leave `customerId` and `shop` out of Storefront API request bodies, or send the signed-in customer's and the store's own values. The session already identifies both.

Reference: [Get customer payment methods](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/get-customer-payment-methods.md), [Update membership contract details](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-membership-contract-details.md).

**September 11, 2026**

Partner authentication

## One error for every failed partner authentication

`connect`, `initiate-connect`, `verify`, `disconnect` and `status` under `/api/partner/{partnerId}/` now return the same error for every authentication failure: `400` with the `errorKey` `TOKEN_INVALID` and the title `UserGeneratedError:Invalid partner credentials.` That covers a Partner ID that is wrong or belongs to a deactivated partner, a wrong `X-Partner-Secret` (or a missing one on `connect`, `initiate-connect` and `disconnect`), and an invalid HMAC signature or a timestamp more than 5 minutes off. Until now, an unknown or deactivated Partner ID returned `PARTNER_NOT_FOUND`, and each authentication failure had its own title, such as `UserGeneratedError:Invalid partner secret.`

**Who is affected:** partners whose code branches on `PARTNER_NOT_FOUND`, or on the title of a `TOKEN_INVALID` error.

**Action required:** stop handling `PARTNER_NOT_FOUND` and the old titles. On `TOKEN_INVALID` with the title `UserGeneratedError:Invalid partner credentials.`, check the Partner ID first, then the secret or HMAC key.

Reference: [Partner integration](https://developers.appstle.com/memberships/partner-integration.md#error-codes).

**September 5, 2026**

Contract revisions, shop parameter

## Admin API calls can't name another store

An Admin API call whose `shop`, `shopName`, `shopDomain` or `shop_domain` parameter names a store other than the API key's store returns `403` with the `detail` `Shop does not match the authenticated session`. The API key already identifies the store.

**Who is affected:** integrations that send a `shop` parameter to the Admin API.

**Action required:** leave `shop` out of Admin API calls, or send the store domain of the key's own store.

## Contract responses return `revisionId`

Membership contract responses return `revisionId` (string), the contract's revision number in Shopify. It appears on the contract reads, for example `GET /api/external/v2/subscription-contracts/contract-external/{contractId}`, and on the contract-changing calls such as `PUT /api/external/v2/subscription-contract-add-line-item` and `PUT /api/external/v2/subscription-contract-update-variant`. The Storefront API contract reads and `PUT /memberships/cp/api/subscription-contracts-apply-discount` return it too.

**September 4, 2026**

Add line item, contract details, billing attempts

## Storefront calls can change less of a membership

* The Storefront API no longer serves `PUT /memberships/cp/api/subscription-contracts-add-line-item`. Use `PUT /memberships/cp/api/v2/subscription-contracts-add-line-item`, which takes `contractId`, `shop`, `quantity` and `variantId`. It doesn't take `price`.
* `PUT /memberships/cp/api/subscription-contract-details` saves only `orderNote` and `phone`. It ignores every other field in the request body and keeps the stored value.
* `PUT /memberships/cp/api/subscription-billing-attempts` changes only `billingDate` and `orderNote`, and only on a billing attempt whose status is `QUEUED`. Otherwise it returns `400` with the title `Invalid billing attempt status`. It returns `400` with the title `Invalid contract` when `contractId` isn't the billing attempt's contract, and `Invalid id` when the billing attempt doesn't exist.

**Who is affected:** storefront code that calls the first add-line-item path or sends `price`, or that changes contract details or billing attempts beyond the order note, phone and billing date.

**Action required:** call the `v2` add-line-item path without `price`. From the storefront, send both `orderNote` and `phone` to update contract details, and both `billingDate` and `orderNote` to update a queued billing attempt. Each call overwrites both fields with what you send. Handle `400` on the billing attempt call and show the `title`.

Reference: [Add line item to contract](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/add-line-item-to-contract.md), [Update membership contract details](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-membership-contract-details.md), [Update billing attempt](https://developers.appstle.com/memberships-customer-portal-api/billing-&-orders/update-billing-attempt.md).

**August 28, 2026**

Bundle endpoints

## Three subscription bundle endpoints are no longer in the Admin API reference

The Memberships Admin API reference no longer lists these three bundle endpoints, which aren't part of the Memberships API:

* `GET /api/external/v2/subscription-bundle-settings/{id}`
* `GET /api/external/v2/subscription-bundlings/external/get-bundle/{handle}`
* `PUT /api/external/v2/subscription-bundlings/discount/{token}`

A call to `subscription-bundle-settings/{id}` still answered on November 1, 2026. But none of the three is documented any more, so don't build on them.

**Who is affected:** integrations that call these three endpoints.

**Action required:** stop calling them.

**August 20, 2026**

Contract details

## Contract details return `nextActivationDate` and `pauseDurationCycle`

* `GET /api/external/v2/subscription-contract-details` and `GET /api/external/v2/subscription-customers-detail/valid/{customerId}` return `nextActivationDate` (date-time) and `pauseDurationCycle` (integer, the number of billing cycles to pause when the pause option is selected).
* `GET /memberships/cp/api/subscription-contract-details/customer/{contractId}`, `GET /memberships/cp/api/subscription-customers-detail/valid/{id}` and `PUT /memberships/cp/api/subscription-contract-details` return both fields.

Reference: [Get membership contract list](https://developers.appstle.com/memberships-admin-api/membership-contracts/get-membership-contract-list.md), [Get detailed customer membership information](https://developers.appstle.com/memberships-admin-api/membership-contracts/get-detailed-customer-membership-information.md), [Get customer membership details](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/get-customer-membership-details.md), [Update membership contract details](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-membership-contract-details.md).

**August 19, 2026**

Authentication errors, API access on the plan

## Failed Admin API authentication returns a `401` that says why

A request to an `/api/external/` path with a missing, unknown or revoked API key now returns `401` with `Content-Type: application/problem+json`. The body has `title`, `status`, `detail`, `path` and `message`, and `detail` names the failed check, for example `API key can not be blank.` Before, these requests got the generic message `Full authentication is required to access this resource`.

**Who is affected:** clients that match on the old generic message.

**Action required:** none for most clients. Read `detail` to tell a bad key from a plan without API access, and log it.

## The Admin API needs a plan that includes API access

Admin API calls now work only when the store's Appstle plan includes API access. On a plan without it, every call returns `401` with the `detail` `You do not have permission to access the External API. Please contact the Appstle Membership Support team.`, whichever key it sends. Until now, calls worked whatever the plan. Appstle checks the plan before it checks whether an `apst_` key is revoked, so a store without API access gets this message even when its key has been revoked. Scoped partner tokens got this `401` too, until November 1, 2026.

**Who is affected:** integrations on stores whose plan doesn't include API access.

**Action required:** when a call returns this `detail`, ask the merchant to add API access to their plan: in the upgrade notice on the **API Access** page, they click **Chat with us**.

Reference: [Authentication errors](https://developers.appstle.com/memberships/authentication.md#authentication-errors).

**August 3, 2026**

Billing date

## Changing the billing date of a membership with a pending upgrade or downgrade needs confirmation

`PUT /api/external/v2/subscription-contracts-update-billing-date` and `PUT /memberships/cp/api/subscription-contracts-update-billing-date` accept an optional `cancelPendingScheduledChange` (boolean) query parameter.

If the membership has a scheduled upgrade or downgrade, a call without `cancelPendingScheduledChange=true` returns `400`, and the billing date and the scheduled change stay as they are. The `title` names the change and its date, for example `UserGeneratedError:A membership downgrade is scheduled for …`. With `true`, the scheduled change is cancelled and the billing date changes.

**Who is affected:** integrations that change billing dates on memberships with a scheduled upgrade or downgrade.

**Action required:** handle `400` on this call. Show the `title` to the person making the change, and send `cancelPendingScheduledChange=true` only after they confirm that the scheduled change can be cancelled.

Reference: [Update membership contract billing date](https://developers.appstle.com/memberships-admin-api/membership-contracts/update-membership-contract-billing-date.md), [Update next billing date](https://developers.appstle.com/memberships-customer-portal-api/membership-contracts/update-next-billing-date.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.