Guides and Examples
...
Subscriptions
Fetching Subscription Product Relationships
23 min
this guide explains how to retrieve available upgrade, downgrade, and swap (uds) paths for customer subscriptions using the nue self service api understanding available product relationships enables you to build dynamic self service experiences where customers can modify their subscriptions prerequisites before you begin, ensure you have a valid nue api key subscription names for the subscriptions you want to query basic understanding of rest apis and json familiarity with product relationship concepts (upgrade, downgrade, swap) authentication all product relationship operations require authentication using your nue api key in the nue api key header const myheaders = new headers(); myheaders append("nue api key", "your api key here"); myheaders append("content type", "application/json"); understanding product relationships product relationships in nue define how subscribed products can be changed there are three types of relationships relationship type description use case upgrade move to a higher tier product basic plan to professional plan downgrade move to a lower tier product enterprise plan to standard plan swap exchange for an equivalent product us region to eu region rest endpoint get https //api nue io/subscriptions/product relationships?subscriptionnames={names} query parameters parameter type required description subscriptionnames array\[string] yes json encoded array of subscription names to query basic usage fetch product relationships for a single subscription const myheaders = new headers(); myheaders append("nue api key", "your api key here"); myheaders append("content type", "application/json"); // fetch product relationships for a single subscription const subscriptionnames = \["sub 000115"]; const encodednames = encodeuricomponent(json stringify(subscriptionnames)); fetch(`https //api nue io/subscriptions/product relationships?subscriptionnames=${encodednames}`, { method 'get', headers myheaders }) then(response => response json()) then(result => { if (result status === 'success' && result data) { console log('product relationships retrieved successfully'); // data is keyed by subscription name object entries(result data) foreach((\[subname, subscription]) => { console log(`\nsubscription ${subscription subscriptionname}`); console log(` current product ${subscription productsku}`); console log(` current uom ${subscription subscriptionuomname}`); const options = subscription options; // display upgrade options if (options upgrade && options upgrade length > 0) { console log('\n upgrade options '); options upgrade foreach(option => { option toproducts foreach(product => { console log(` ${product name} (${product sku})`); console log(` same uom only ${option sameuomonly}`); console log(` available from ${option startdate}`); }); }); } // display downgrade options if (options downgrade && options downgrade length > 0) { console log('\n downgrade options '); options downgrade foreach(option => { option toproducts foreach(product => { console log(` ${product name} (${product sku})`); }); }); } // display swap options if (options swap && options swap length > 0) { console log('\n swap options '); options swap foreach(option => { option toproducts foreach(product => { console log(` ${product name} (${product sku})`); console log(` same price swap ${option samepriceswap}`); }); }); } }); } }) catch(error => console log('error ', error)); fetch product relationships for multiple subscriptions query relationships for multiple subscriptions in a single api call // fetch product relationships for multiple subscriptions const subscriptionnames = \["sub 000115", "sub 000116"]; const encodednames = encodeuricomponent(json stringify(subscriptionnames)); fetch(`https //api nue io/subscriptions/product relationships?subscriptionnames=${encodednames}`, { method 'get', headers myheaders }) then(response => response json()) then(result => { if (result status === 'success' && result data) { const subscriptions = object values(result data); console log(`retrieved relationships for ${subscriptions length} subscriptions`); // analyze available options across all subscriptions const withupgrades = subscriptions filter(s => s options upgrade? length > 0); const withdowngrades = subscriptions filter(s => s options downgrade? length > 0); const withswaps = subscriptions filter(s => s options swap? length > 0); console log(`\nsummary `); console log(` subscriptions with upgrades ${withupgrades length}`); console log(` subscriptions with downgrades ${withdowngrades length}`); console log(` subscriptions with swaps ${withswaps length}`); // display details for each subscription subscriptions foreach(subscription => { const options = subscription options; const upgradecount = options upgrade? length || 0; const downgradecount = options downgrade? length || 0; const swapcount = options swap? length || 0; console log(`\n${subscription subscriptionname} `); console log(` product ${subscription productsku}`); console log(` available ${upgradecount} upgrade(s), ${downgradecount} downgrade(s), ${swapcount} swap(s)`); }); } }) catch(error => console log('error ', error)); building self service experiences extracting product options for ui display build a selection interface for customers to choose their desired product change function extractchangeoptions(subscriptiondata) { const options = { subscriptionname subscriptiondata subscriptionname, subscriptionid subscriptiondata subscriptionid, currentproduct { sku subscriptiondata productsku, uom subscriptiondata subscriptionuomname }, availablechanges \[] }; const relationshipoptions = subscriptiondata options; // process upgrade options if (relationshipoptions upgrade) { relationshipoptions upgrade foreach(upgrade => { upgrade toproducts foreach(product => { options availablechanges push({ type 'upgrade', relationshipid upgrade id, targetproduct { id product id, name product name, sku product sku, pricebookentries product pricebookentries }, sameuomonly upgrade sameuomonly, startdate upgrade startdate }); }); }); } // process downgrade options if (relationshipoptions downgrade) { relationshipoptions downgrade foreach(downgrade => { downgrade toproducts foreach(product => { options availablechanges push({ type 'downgrade', relationshipid downgrade id, targetproduct { id product id, name product name, sku product sku, pricebookentries product pricebookentries }, sameuomonly downgrade sameuomonly, startdate downgrade startdate }); }); }); } // process swap options if (relationshipoptions swap) { relationshipoptions swap foreach(swap => { swap toproducts foreach(product => { options availablechanges push({ type 'swap', relationshipid swap id, targetproduct { id product id, name product name, sku product sku, pricebookentries product pricebookentries }, sameuomonly swap sameuomonly, samepriceswap swap samepriceswap, startdate swap startdate }); }); }); } return options; } // usage fetch(`https //api nue io/subscriptions/product relationships?subscriptionnames=${encodednames}`, { method 'get', headers myheaders }) then(response => response json()) then(result => { if (result status === 'success' && result data) { object values(result data) foreach(subscription => { const changeoptions = extractchangeoptions(subscription); console log(`\n${changeoptions subscriptionname}`); console log(`current ${changeoptions currentproduct sku} (${changeoptions currentproduct uom})`); if (changeoptions availablechanges length === 0) { console log(' no changes available'); return; } changeoptions availablechanges foreach((change, index) => { const typeicon = change type === 'upgrade' ? '(up)' change type === 'downgrade' ? '(down)' '(swap)'; console log(` ${index + 1} ${typeicon} ${change targetproduct name}`); console log(` sku ${change targetproduct sku}`); // show pricing options if (change targetproduct pricebookentries) { const prices = change targetproduct pricebookentries map(p => `${p listprice} ${p uom name}`) join(', '); console log(` pricing ${prices}`); } }); }); } }); displaying pricing options for target products the response includes full pricing details for target products, allowing you to show customers what their new subscription will cost function displaypricingoptions(pricebookentries) { console log(' available pricing '); pricebookentries foreach(entry => { const uom = entry uom; const recommended = entry recommended ? ' (recommended)' ''; console log(` $${entry listprice} per ${uom name}${recommended}`); console log(` billing ${entry billingtiming}`); console log(` price book entry id ${entry id}`); }); } // usage within the change options flow changeoptions availablechanges foreach(change => { console log(`\nupgrade to ${change targetproduct name}`); displaypricingoptions(change targetproduct pricebookentries); }); response structure success response (200 ok) { "status" "success", "data" { "sub 000115" { "currencyisocode" "usd", "subscriptionname" "sub 000115", "subscriptionid" "a0udi00000484tlaaa", "productsku" "order forms uds", "subscriptionuomname" "user/year", "options" { "upgrade" \[ { "id" "a0odi000000y2qvaay", "relationshiptype" "upgrade", "sameuomonly" false, "startdate" "2025 01 16", "productrelationshippricetags" \[ { "id" "a0xdi000001abc", "name" "upgrade discount", "pricetagtype" "discount", "publishstatus" "published" } ], "fromproduct" { "id" "01tdi000008imqiaaw", "name" "order forms uds", "sku" "order forms uds", "pricebookentries" \[ { "id" "01udi00000341kwaaq", "listprice" 100, "billingtiming" "in advance", "uom" { "id" "a0wdi0000020dsqaa2", "name" "user/year", "quantitydimension" "user", "termdimension" "year" } } ], "pricemodel" "recurring", "productcategory" "recurringservices" }, "toproducts" \[ { "id" "01tdi000008imqxaaw", "name" "nue recurring test", "sku" "nue recurring test", "pricebookentries" \[ { "id" "01udi00000341kxaaq", "listprice" 120, "billingtiming" "in advance", "uom" { "id" "a0wdi0000020dsqaa2", "name" "user/year", "quantitydimension" "user", "termdimension" "year" } } ], "pricemodel" "recurring", "pricetags" \[ ] } ] } ], "swap" \[ { "id" "a0odi000000y2qraay", "relationshiptype" "swap", "sameuomonly" false, "samepriceswap" false, "startdate" "2025 07 30", "fromproduct" { }, "toproducts" \[ ] } ] } } } } partial success response (200 ok) when some subscriptions are found but others fail, the api returns a partial success with warnings { "status" "partial success", "data" { "sub 000115" { "subscriptionname" "sub 000115", "subscriptionid" "a0udi00000484tlaaa", "productsku" "order forms uds", "subscriptionuomname" "user/year", "options" { } } }, "warnings" \[ { "code" "subscription not found", "message" "subscription not found sub invalid" } ] } response fields top level response field type description status string response status success , partial success , or error data object object keyed by subscription name warnings array (optional) array of warning objects when some subscriptions fail warning object field type description code string warning code (e g , subscription not found , subscription evaluation failed ) message string human readable warning message subscription object field type description currencyisocode string (optional) currency iso code for multi currency tenants subscriptionname string name of the subscription subscriptionid string unique subscription identifier productsku string sku of the currently subscribed product subscriptionuomname string current unit of measure name options object contains upgrade , downgrade , and swap arrays relationship option object field type description id string unique relationship identifier relationshiptype string upgrade , downgrade , or swap sameuomonly boolean whether only same uom products are valid samepriceswap boolean (swap only) whether price is preserved startdate string date when relationship becomes effective productrelationshippricetags array (optional) price tags/discounts associated with this relationship fromproduct object product object for current product (contains only the subscription's specific price book entry) toproducts array array of target product objects (contains all available price book entries) product object field type description id string product id name string product name sku string product sku pricebookentries array available pricing options (see note below) pricemodel string recurring , onetime , etc pricetags array applied price/discount tags productcategory string product category autorenew boolean default auto renewal setting credittype string cash or credit creditconversion object (optional) credit conversion settings defaultrenewterm number default renewal term evergreen boolean whether the product is evergreen freetrialtype string free trial type freetrialunit number free trial duration recordtype string record type showincludedproductoptions boolean whether to show included product options taxcode string tax code taxmode string tax mode note the pricebookentries array differs between fromproduct and toproducts fromproduct contains only the subscription's specific price book entry (the one the customer is currently subscribed to) toproducts contains all available active price book entries for the target product price tag object field type description id string price tag id name string price tag name code string price tag code pricetagtype string type of price tag (e g , discount ) pricetype string price type publishstatus string publish status active boolean whether the tag is active recordtype string record type price book entry object field type description id string price book entry id listprice number list price amount billingtiming string in advance or in arrears recommended boolean whether this is the recommended option uom object unit of measure details uom name string uom display name (e g , user/month ) uom quantitydimension string quantity dimension (e g , user ) uom termdimension string term dimension (e g , month ) empty options response when a subscription has no available product relationships { "status" "success", "data" { "sub 000117" { "subscriptionname" "sub 000117", "subscriptionid" "a0udi00000485abcde", "productsku" "standalone product", "subscriptionuomname" "license", "options" {} } } } error handling robust error handling async function safegetproductrelationships(subscriptionnames) { const myheaders = new headers(); myheaders append("nue api key", "your api key here"); myheaders append("content type", "application/json"); try { // validate input if (!array isarray(subscriptionnames) || subscriptionnames length === 0) { throw new error('subscriptionnames must be a non empty array'); } const encodednames = encodeuricomponent(json stringify(subscriptionnames)); const url = `https //api nue io/subscriptions/product relationships?subscriptionnames=${encodednames}`; const response = await fetch(url, { method 'get', headers myheaders }); if (!response ok) { throw new error(`http ${response status} ${response statustext}`); } const result = await response json(); if (result status !== 'success') { throw new error(`api error ${result message || 'unknown error'}`); } return { success true, data result data || {}, subscriptions object values(result data || {}) }; } catch (error) { console error('failed to fetch product relationships ', error); return { success false, data {}, subscriptions \[], error error message }; } } // usage with error handling safegetproductrelationships(\["sub 000115", "sub 000116"]) then(result => { if (result success) { console log(`retrieved relationships for ${result subscriptions length} subscriptions`); // process result subscriptions } else { console error('failed ', result error); // handle error state in ui } }); best practices performance batch requests query multiple subscriptions in a single api call cache results product relationships change infrequently lazy load only fetch relationships when the user accesses subscription management user experience clear labeling use descriptive labels like "upgrade to enterprise" instead of just product names show pricing impact display price differences using the included pricebookentries indicate restrictions show when sameuomonly applies to set expectations highlight swap benefits indicate when samepriceswap is true integration combine with subscription data fetch subscriptions first, then query relationships for active ones use for change orders after user selects an option, use the relationshipid and target product details to create a change order handle empty states gracefully handle subscriptions with no available changes next steps after retrieving available product relationships, you can create change orders execute the selected upgrade, downgrade, or swap using the relationship data fetch pricing data use the pricebookentries to show customers accurate pricing update subscriptions modify subscription settings and custom fields see the creating draft change orders guide to learn how to implement product changes based on the relationships retrieved from this endpoint