Update multiple properties of a subscription line item
Comprehensive endpoint for updating a subscription line item’s quantity, price, product variant, and/or selling plan in a single API call. Changes are applied intelligently - only modified values trigger updates.
Key Features:
- Update any combination of: quantity, price, variant, or selling plan
- Intelligent change detection - only updates what’s different
- Automatic handling of prepaid subscription pricing
- Preserves existing discount cycles when updating price
- Partial success allowed - some updates may fail while others succeed
- Each change creates separate activity log entries
Prepaid Subscription Handling: For prepaid subscriptions (billing interval > delivery interval):
- When
isPricePerUnit=true: Price is multiplied by interval ratio - Example: Monthly billing, weekly delivery = price × 4
- When
isPricePerUnit=false: Price is used as total billing amount
Update Process:
- Validates line item exists in the subscription
- Calculates interval multiplier for prepaid logic
- Updates in order: selling plan → price → quantity → variant
- Each update uses separate Shopify GraphQL mutation
- Failures are logged but don’t block other updates
Selling Plan Updates:
- Can update by ID or name (name takes precedence)
- Only updates if different from current plan
- Useful for changing delivery frequency options
Price Updates:
- Preserves existing discount cycles (up to 2 cycles)
- Recalculates cycle discounts based on new base price
- Triggers shipping price recalculation
- Sends price update email to customer
Quantity Updates:
- Validates against min/max quantity rules
- Updates Build-a-Box totals if applicable
- May trigger discount recalculations
Variant Updates:
- Changes the product itself (different SKU)
- Validates new variant exists and is available
- May affect pricing and discounts
Important Notes:
- All parameters except contractId and lineId are optional
- Provide only the values you want to change
- Price is always in shop’s base currency
- Changes apply to future orders only
Authentication: Requires valid X-API-Key header
curl --request PUT \
--url https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-line-item \
--header 'X-API-Key: <x-api-key>'import requests
url = "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-line-item"
headers = {"X-API-Key": "<x-api-key>"}
response = requests.put(url, headers=headers)
print(response.text)const options = {method: 'PUT', headers: {'X-API-Key': '<x-api-key>'}};
fetch('https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-line-item', 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-line-item';
const options = {method: 'PUT', headers: {'X-API-Key': '<x-api-key>'}};
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-line-item",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_HTTPHEADER => [
"X-API-Key: <x-api-key>"
],
]);
$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-line-item"
req, _ := http.NewRequest("PUT", url, nil)
req.Header.Add("X-API-Key", "<x-api-key>")
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-line-item")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-API-Key"] = '<x-api-key>'
response = http.request(request)
puts response.read_body{
"billingPolicy": {
"interval": "MONTH",
"intervalCount": 1
},
"deliveryPolicy": {
"interval": "WEEK",
"intervalCount": 1
},
"id": "gid://shopify/SubscriptionContract/123456789",
"lines": {
"edges": [
{
"node": {
"currentPrice": {
"amount": "119.97",
"currencyCode": "USD"
},
"id": "gid://shopify/SubscriptionLine/111111",
"pricingPolicy": {
"basePrice": {
"amount": "9.99",
"currencyCode": "USD"
},
"cycleDiscounts": [
{
"adjustmentType": "PERCENTAGE",
"adjustmentValue": {
"percentage": 10
},
"afterCycle": 3,
"computedPrice": {
"amount": "107.97",
"currencyCode": "USD"
}
}
]
},
"quantity": 3,
"sellingPlanId": "gid://shopify/SellingPlan/777777",
"sellingPlanName": "Deliver every week",
"title": "Premium Coffee - Medium Roast",
"variantId": "gid://shopify/ProductVariant/98765432101"
}
}
]
},
"nextBillingDate": "2024-03-01T00:00:00Z",
"status": "ACTIVE"
}Headers
API Key for authentication
Query Parameters
Subscription contract ID
x >= 1API Key (Deprecated - Use X-API-Key header instead)
New quantity for the line item. Leave unchanged if not updating quantity.
1 <= x <= 9999New product variant ID in Shopify GID format (e.g., gid://shopify/ProductVariant/42549172011164). This updates the associated product. Leave this parameter blank if you are not switching products.
Line item ID to update. Must be the full GraphQL ID including gid:// prefix
New price for the line item in shop currency. Behavior depends on isPricePerUnit flag.
0.01 <= x <= 999999.99Determines how price is interpreted for prepaid subscriptions:
- true: Price is per unit (per delivery), will be multiplied by interval ratio
- false: Price is total billing amount (for all deliveries in billing period)
Example: Monthly billing, weekly delivery, price=$10
- isPricePerUnit=true: Customer pays $40/month ($10 × 4 weeks)
- isPricePerUnit=false: Customer pays $10/month total
Name of the selling plan to apply. Takes precedence over current selling plan. Used to change delivery frequencies.
255Response
Line item successfully updated
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-line-item \
--header 'X-API-Key: <x-api-key>'import requests
url = "https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-line-item"
headers = {"X-API-Key": "<x-api-key>"}
response = requests.put(url, headers=headers)
print(response.text)const options = {method: 'PUT', headers: {'X-API-Key': '<x-api-key>'}};
fetch('https://subscription-admin.appstle.com/api/external/v2/subscription-contracts-update-line-item', 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-line-item';
const options = {method: 'PUT', headers: {'X-API-Key': '<x-api-key>'}};
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-line-item",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_HTTPHEADER => [
"X-API-Key: <x-api-key>"
],
]);
$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-line-item"
req, _ := http.NewRequest("PUT", url, nil)
req.Header.Add("X-API-Key", "<x-api-key>")
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-line-item")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["X-API-Key"] = '<x-api-key>'
response = http.request(request)
puts response.read_body{
"billingPolicy": {
"interval": "MONTH",
"intervalCount": 1
},
"deliveryPolicy": {
"interval": "WEEK",
"intervalCount": 1
},
"id": "gid://shopify/SubscriptionContract/123456789",
"lines": {
"edges": [
{
"node": {
"currentPrice": {
"amount": "119.97",
"currencyCode": "USD"
},
"id": "gid://shopify/SubscriptionLine/111111",
"pricingPolicy": {
"basePrice": {
"amount": "9.99",
"currencyCode": "USD"
},
"cycleDiscounts": [
{
"adjustmentType": "PERCENTAGE",
"adjustmentValue": {
"percentage": 10
},
"afterCycle": 3,
"computedPrice": {
"amount": "107.97",
"currencyCode": "USD"
}
}
]
},
"quantity": 3,
"sellingPlanId": "gid://shopify/SellingPlan/777777",
"sellingPlanName": "Deliver every week",
"title": "Premium Coffee - Medium Roast",
"variantId": "gid://shopify/ProductVariant/98765432101"
}
}
]
},
"nextBillingDate": "2024-03-01T00:00:00Z",
"status": "ACTIVE"
}