Update minimum cycles for a subscription contract
Updates the minimum number of billing cycles (orders) that a customer must complete before they can cancel their subscription. This creates a commitment period that helps with customer retention and business predictability.
What are Minimum Cycles? Minimum cycles represent a commitment period where:
- Customers must complete a specified number of orders
- Cancellation is blocked until the minimum is met
- Often tied to special pricing or promotional offers
- Counted from the subscription start date
Key Features:
- Updates commitment period for existing subscriptions
- Can increase or decrease minimum cycles
- Setting to null removes the minimum commitment
- Preserves all other subscription settings
- Automatically handles invalid discount codes
- Updates future billing queue after change
Common Use Cases:
- Promotional Offers: ‘3-month minimum for 50% off’
- Hardware Subsidies: ‘12-month commitment with free device’
- Loyalty Programs: Reduce minimum after customer proves loyalty
- Seasonal Campaigns: Temporary commitment requirements
- Contract Adjustments: Customer service exceptions
Impact on Customers:
- Cannot cancel via portal until minimum cycles complete
- Pause/resume typically still allowed (check settings)
- Shows commitment status in customer portal
- No automatic notification sent (consider sending separately)
Cycle Counting:
- Only successful billing attempts count toward minimum
- Failed payments don’t increment the cycle count
- Skipped orders (if allowed) don’t count
- Current cycle = successful past orders + 1
Interaction with Max Cycles:
- Min cycles must be less than or equal to max cycles
- If max cycles exist, subscription auto-cancels after maximum
- Common pattern: 3 min cycles, 12 max cycles
Best Practices:
- Clearly communicate commitment terms upfront
- Consider grandfathering existing customers
- Use reasonable minimums (typically 3-12 cycles)
- Document reason for changes in activity logs
- Send customer notification for transparency
Important Notes:
- Changes apply immediately to cancellation logic
- Doesn’t affect past or in-progress orders
- Customer portal respects this setting automatically
- Activity log tracks old and new values
- Consider legal requirements in your jurisdiction
Authentication: Requires valid X-API-Key header
curl --request PUT \
--url https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cyclesimport requests
url = "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles"
response = requests.put(url)
print(response.text)const options = {method: 'PUT'};
fetch('https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const url = 'https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles';
const options = {method: 'PUT'};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles"
req, _ := http.NewRequest("PUT", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
response = http.request(request)
puts response.read_body{
"billingPolicy": {
"anchors": [
{
"day": 1,
"type": "MONTHDAY"
}
],
"interval": "MONTH",
"intervalCount": 1,
"maxCycles": null,
"minCycles": 6
},
"createdAt": "2024-01-01T00:00:00Z",
"customer": {
"displayName": "John Doe",
"email": "customer@example.com",
"id": "gid://shopify/Customer/987654321"
},
"customerPaymentMethod": {
"id": "gid://shopify/CustomerPaymentMethod/123456",
"instrument": {
"__typename": "CustomerCreditCard",
"brand": "VISA",
"expiryMonth": 12,
"expiryYear": 2025,
"lastDigits": "4242"
}
},
"deliveryPolicy": {
"interval": "MONTH",
"intervalCount": 1
},
"id": "gid://shopify/SubscriptionContract/123456789",
"lines": {
"edges": [
{
"node": {
"currentPrice": {
"amount": "49.99",
"currencyCode": "USD"
},
"id": "gid://shopify/SubscriptionLine/111111",
"quantity": 1,
"title": "Premium Subscription Box",
"variantId": "gid://shopify/ProductVariant/42549172011164"
}
}
]
},
"nextBillingDate": "2024-04-01T00:00:00Z",
"status": "ACTIVE"
}Headers
Query Parameters
Contract ID
API Key (Deprecated - Use Header X-API-Key instead)
Minimum Number of Orders. The minimum number of billing cycles a customer must complete before being allowed to cancel. Set to null to remove the minimum commitment. Common values:
- 3: Three-month commitment
- 6: Six-month commitment
- 12: Annual commitment
- null: No commitment
1 <= x <= 9999Response
Minimum cycles updated successfully
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
SUCCEEDED, FAILED, $UNKNOWN Show child attributes
Show child attributes
Show child attributes
Show child attributes
ACTIVE, PAUSED, CANCELLED, EXPIRED, FAILED, $UNKNOWN curl --request PUT \
--url https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cyclesimport requests
url = "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles"
response = requests.put(url)
print(response.text)const options = {method: 'PUT'};
fetch('https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const url = 'https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles';
const options = {method: 'PUT'};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles"
req, _ := http.NewRequest("PUT", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-min-cycles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
response = http.request(request)
puts response.read_body{
"billingPolicy": {
"anchors": [
{
"day": 1,
"type": "MONTHDAY"
}
],
"interval": "MONTH",
"intervalCount": 1,
"maxCycles": null,
"minCycles": 6
},
"createdAt": "2024-01-01T00:00:00Z",
"customer": {
"displayName": "John Doe",
"email": "customer@example.com",
"id": "gid://shopify/Customer/987654321"
},
"customerPaymentMethod": {
"id": "gid://shopify/CustomerPaymentMethod/123456",
"instrument": {
"__typename": "CustomerCreditCard",
"brand": "VISA",
"expiryMonth": 12,
"expiryYear": 2025,
"lastDigits": "4242"
}
},
"deliveryPolicy": {
"interval": "MONTH",
"intervalCount": 1
},
"id": "gid://shopify/SubscriptionContract/123456789",
"lines": {
"edges": [
{
"node": {
"currentPrice": {
"amount": "49.99",
"currencyCode": "USD"
},
"id": "gid://shopify/SubscriptionLine/111111",
"quantity": 1,
"title": "Premium Subscription Box",
"variantId": "gid://shopify/ProductVariant/42549172011164"
}
}
]
},
"nextBillingDate": "2024-04-01T00:00:00Z",
"status": "ACTIVE"
}