Upgrade a subscription
curl --request POST \
--url https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Tenant-ID: <tenant-id>' \
--data '
{
"rentalId": "<string>",
"newSku": "<string>",
"newSerialNumber": "<string>",
"contractLength": 60,
"newMonthlyAmount": 1,
"reason": "<string>",
"notes": "<string>"
}
'import requests
url = "https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade"
payload = {
"rentalId": "<string>",
"newSku": "<string>",
"newSerialNumber": "<string>",
"contractLength": 60,
"newMonthlyAmount": 1,
"reason": "<string>",
"notes": "<string>"
}
headers = {
"Tenant-ID": "<tenant-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Tenant-ID': '<tenant-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
rentalId: '<string>',
newSku: '<string>',
newSerialNumber: '<string>',
contractLength: 60,
newMonthlyAmount: 1,
reason: '<string>',
notes: '<string>'
})
};
fetch('https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'rentalId' => '<string>',
'newSku' => '<string>',
'newSerialNumber' => '<string>',
'contractLength' => 60,
'newMonthlyAmount' => 1,
'reason' => '<string>',
'notes' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Tenant-ID: <tenant-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade"
payload := strings.NewReader("{\n \"rentalId\": \"<string>\",\n \"newSku\": \"<string>\",\n \"newSerialNumber\": \"<string>\",\n \"contractLength\": 60,\n \"newMonthlyAmount\": 1,\n \"reason\": \"<string>\",\n \"notes\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Tenant-ID", "<tenant-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade")
.header("Tenant-ID", "<tenant-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rentalId\": \"<string>\",\n \"newSku\": \"<string>\",\n \"newSerialNumber\": \"<string>\",\n \"contractLength\": 60,\n \"newMonthlyAmount\": 1,\n \"reason\": \"<string>\",\n \"notes\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Tenant-ID"] = '<tenant-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"rentalId\": \"<string>\",\n \"newSku\": \"<string>\",\n \"newSerialNumber\": \"<string>\",\n \"contractLength\": 60,\n \"newMonthlyAmount\": 1,\n \"reason\": \"<string>\",\n \"notes\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "<string>",
"oldRentalId": "<string>",
"newRentalId": "<string>",
"oldDevice": "<string>",
"newDevice": "<string>",
"newMonthlyAmount": 1,
"currency": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>"
}
}Subscriptions
Upgrade Subscription
Upgrades the customer to a better device. Creates a new subscription and ends the current one.
POST
/
v1
/
subscriptions
/
{subscriptionId}
/
upgrade
Upgrade a subscription
curl --request POST \
--url https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Tenant-ID: <tenant-id>' \
--data '
{
"rentalId": "<string>",
"newSku": "<string>",
"newSerialNumber": "<string>",
"contractLength": 60,
"newMonthlyAmount": 1,
"reason": "<string>",
"notes": "<string>"
}
'import requests
url = "https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade"
payload = {
"rentalId": "<string>",
"newSku": "<string>",
"newSerialNumber": "<string>",
"contractLength": 60,
"newMonthlyAmount": 1,
"reason": "<string>",
"notes": "<string>"
}
headers = {
"Tenant-ID": "<tenant-id>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Tenant-ID': '<tenant-id>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
rentalId: '<string>',
newSku: '<string>',
newSerialNumber: '<string>',
contractLength: 60,
newMonthlyAmount: 1,
reason: '<string>',
notes: '<string>'
})
};
fetch('https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'rentalId' => '<string>',
'newSku' => '<string>',
'newSerialNumber' => '<string>',
'contractLength' => 60,
'newMonthlyAmount' => 1,
'reason' => '<string>',
'notes' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Tenant-ID: <tenant-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade"
payload := strings.NewReader("{\n \"rentalId\": \"<string>\",\n \"newSku\": \"<string>\",\n \"newSerialNumber\": \"<string>\",\n \"contractLength\": 60,\n \"newMonthlyAmount\": 1,\n \"reason\": \"<string>\",\n \"notes\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Tenant-ID", "<tenant-id>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade")
.header("Tenant-ID", "<tenant-id>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"rentalId\": \"<string>\",\n \"newSku\": \"<string>\",\n \"newSerialNumber\": \"<string>\",\n \"contractLength\": 60,\n \"newMonthlyAmount\": 1,\n \"reason\": \"<string>\",\n \"notes\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-eu.flexportal.io/v1/subscriptions/{subscriptionId}/upgrade")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Tenant-ID"] = '<tenant-id>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"rentalId\": \"<string>\",\n \"newSku\": \"<string>\",\n \"newSerialNumber\": \"<string>\",\n \"contractLength\": 60,\n \"newMonthlyAmount\": 1,\n \"reason\": \"<string>\",\n \"notes\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "<string>",
"oldRentalId": "<string>",
"newRentalId": "<string>",
"oldDevice": "<string>",
"newDevice": "<string>",
"newMonthlyAmount": 1,
"currency": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>"
}
}Overview
Upgrade a customer to a different (typically better) device. This ends the current subscription and creates a new order for the upgraded product. The customer returns their current device and receives a new one.When to Use Upgrades
Upgrade Flow
1. Calculate upgrade terms
2. Call upgrade endpoint
3. Current subscription marked for end
4. New upgrade order created
5. Customer returns old device
6. New device shipped
7. New subscription starts
Request Fields
| Field | Required | Description |
|---|---|---|
newSku | Yes | SKU of the upgrade product |
newContractLength | Yes | Contract length for new subscription |
reason | No | Reason for upgrade |
Example Request
{
"newSku": "MACBOOK-PRO-16-M4",
"newContractLength": 24,
"reason": "Customer requested latest model"
}
What Happens
When you process an upgrade:- Current subscription marked as ending (status changes on return)
- Upgrade order created with type
upgrade - Order linked to original subscription for tracking
- Pricing calculated based on new variant’s rates
- Return scheduled for current device
Response Structure
{
"success": true,
"message": "Upgrade order created",
"upgradeOrderId": "ord_xyz789",
"currentSubscription": {
"subscriptionId": "sub_abc123",
"scheduledEndDate": "2025-02-15"
},
"upgradeDetails": {
"previousDevice": "MacBook Pro 16\" M3",
"newDevice": "MacBook Pro 16\" M4",
"previousMonthlyAmount": 129.00,
"newMonthlyAmount": 159.00
}
}
Upgrade Pricing Considerations
Consider these factors when setting upgrade prices:- Remaining value on current device
- New device acquisition cost
- Market rates for the new device
- Customer loyalty and history
// Example: Check if upgrade makes sense
async function analyzeUpgrade(subscriptionId, newSku) {
const subscription = await getSubscription(subscriptionId);
const newVariant = await getVariantBySku(newSku);
return {
currentMonthly: subscription.monthlyAmount,
newMonthly: newVariant.pricing['24'], // 24-month price
monthlyCostIncrease: newVariant.pricing['24'] - subscription.monthlyAmount,
currentDeviceReturn: subscription.serialNumber,
recommendation: newVariant.pricing['24'] > subscription.monthlyAmount
? 'upsell'
: 'downgrade'
};
}
Completing the Upgrade
After calling upgrade:- Process the return of the current device
- Create subscription on the upgrade order (like any new order)
- Ship the new device to customer
Error Handling
| Error Code | Cause | Solution |
|---|---|---|
SUBSCRIPTION_NOT_ACTIVE | Cannot upgrade inactive subscription | Check subscription status |
VARIANT_NOT_FOUND | New SKU doesn’t exist | Verify SKU in product catalog |
VARIANT_INACTIVE | New SKU is deactivated | Use an active variant |
INVALID_CONTRACT_LENGTH | Contract length not supported | Use valid contract length |
Related Endpoints
- Get Subscription - Check current subscription
- List Variants - Find upgrade options
- Create Subscription - Fulfill the upgrade order
- Replace Device - For faulty device swaps (not upgrades)
Authorizations
API key obtained from FlexPortal dashboard
Headers
Your tenant identifier
Path Parameters
The subscription ID
Body
application/json
Minimum string length:
1Minimum string length:
1Minimum string length:
1Required range:
1 < x <= 120Required range:
x >= 0Minimum string length:
1Was this page helpful?