Fetching Subscription Data
This guide provides comprehensive instructions for retrieving subscription data using the Nue Lifecycle Management API. Learn how to fetch customer subscriptions, create point-in-time snapshots, analyze upcoming changes, and implement efficient subscription data retrieval patterns.
Prerequisites
Before you begin, ensure you have:
- A valid Nue API key with subscription read permissions
- Customer IDs for the subscriptions you want to retrieve
- Basic understanding of REST APIs and JSON
- Familiarity with subscription data structures and lifecycle
Authentication
All subscription retrieval 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");REST Endpoints
The Nue API provides REST endpoints for flexible subscription data access:
// Global subscription endpoints
GET https://api.nue.io/subscriptions // Get all subscriptions with pagination
GET https://api.nue.io/subscriptions/{subscriptionName} // Get a specific subscription by name
// Customer-scoped subscription endpoints
GET https://api.nue.io/customers/{customerId}/subscriptions // Get subscriptions for a customer
GET https://api.nue.io/customers/{customerId}/subscriptions/{subscriptionName} // Get a specific subscription for a customerFiltering
Subscription endpoints support filtering using query parameters:
Query Parameters:
- customerIds - Array of customer IDs to filter by
- name - Subscription name to search for
- status - Filter by status: Active, Expired, Canceled, Draft
- bundled - Whether the subscription is a bundled component of a parent bundle (true/false)
- subscriptionLevel - Depth in the bundle hierarchy: 1 for a top-level subscription, 2 and above for nested options. Accepts a comma-separated list
- snapshotDate - Point-in-time snapshot date (YYYY-MM-DD)
- history - Include subscription history (true/false)
- includes - Include related data (e.g., product, pricetags)
- page - Page number for pagination
- limit - Number of results per page
Beyond the parameters above, any field on the subscription object can be used as an equality filter, for example ?quantity=5 or ?autoRenew=true. Unknown field names and values of the wrong type are rejected with 400 rather than ignored; see Filtering by Bundle Structure๏ปฟ for the details.
Basic Subscription Retrieval
Fetch All Subscriptions for Customer
Try it now: Fetch Subscriptions โ
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
// Fetch all subscriptions for a customer
const customerIds = ["d2e04653-ae90-49df-a986-134cf64f6d03"];
const encodedCustomerIds = encodeURIComponent(JSON.stringify(customerIds));
fetch(`https://api.nue.io/subscriptions?customerIds=${encodedCustomerIds}`, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
console.log('Subscriptions retrieved successfully:', result);
if (result.status === 'SUCCESS' && result.data) {
console.log(`Found ${result.data.length} subscriptions for customer`);
// Display subscription summary
result.data.forEach((subscription, index) => {
console.log(`\n${index + 1}. ${subscription.name || subscription.id}`);
console.log(` Status: ${subscription.status}`);
console.log(` Product: ${subscription.productId}`);
console.log(` Quantity: ${subscription.quantity}`);
console.log(` Start Date: ${subscription.subscriptionStartDate}`);
console.log(` End Date: ${subscription.subscriptionEndDate}`);
console.log(` Auto Renew: ${subscription.autoRenew}`);
console.log(` Total Value: $${subscription.totalAmount || subscription.tcv}`);
console.log(` List Price: $${subscription.listPrice}`);
console.log(` Sales Price: $${subscription.salesPrice}`);
// Display term information
if (subscription.subscriptionTerm) {
console.log(` Term: ${subscription.subscriptionTerm} months`);
}
// Display billing information
if (subscription.billingPeriod) {
console.log(` Billing Period: ${subscription.billingPeriod}`);
}
if (subscription.billingTiming) {
console.log(` Billing Timing: ${subscription.billingTiming}`);
}
if (subscription.nextBillingDate) {
console.log(` Next Billing: ${subscription.nextBillingDate}`);
}
});
}
})
.catch(error => console.log('Error:', error));Fetch Subscriptions for Multiple Customers
Retrieve subscriptions for multiple customers in a single API call:
// Fetch subscriptions for multiple customers
const customerIds = [
"d2e04653-ae90-49df-a986-134cf64f6d03",
"cc5e1f0f-5e14-48cc-ab98-9e5b191aa46f",
"f1b2c3d4-e5f6-7890-abcd-ef1234567890"
];
const encodedCustomerIds = encodeURIComponent(JSON.stringify(customerIds));
fetch(`https://api.nue.io/subscriptions?customerIds=${encodedCustomerIds}`, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS') {
console.log(`Retrieved subscriptions for ${customerIds.length} customers`);
// Group subscriptions by customer
const subscriptionsByCustomer = {};
result.data.forEach(subscription => {
const customerId = subscription.customerId;
if (!subscriptionsByCustomer[customerId]) {
subscriptionsByCustomer[customerId] = [];
}
subscriptionsByCustomer[customerId].push(subscription);
});
// Display grouped results
Object.keys(subscriptionsByCustomer).forEach(customerId => {
const subscriptions = subscriptionsByCustomer[customerId];
console.log(`\n--- Customer ${customerId} ---`);
console.log(`Subscriptions: ${subscriptions.length}`);
// Calculate totals
const totalValue = subscriptions.reduce((sum, sub) => sum + (sub.totalAmount || 0), 0);
const activeCount = subscriptions.filter(s => s.status === 'Active').length;
console.log(`Active: ${activeCount}/${subscriptions.length}`);
console.log(`Total Value: $${totalValue.toLocaleString()}`);
subscriptions.forEach(subscription => {
console.log(` โข ${subscription.name || subscription.id} - ${subscription.status} - $${subscription.totalAmount || 0}`);
});
});
}
})
.catch(error => console.log('Error:', error));Filtered Subscription Retrieval
Fetch Active Subscriptions Only
Filter subscriptions by status to retrieve only active subscriptions:
const customerIds = ["d2e04653-ae90-49df-a986-134cf64f6d03"];
const encodedCustomerIds = encodeURIComponent(JSON.stringify(customerIds));
// Fetch only active subscriptions
const url = `https://api.nue.io/subscriptions?customerIds=${encodedCustomerIds}&status=Active`;
fetch(url, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS') {
console.log('๐ข Active Subscriptions Retrieved');
if (result.data && result.data.length > 0) {
result.data.forEach(subscription => {
console.log(`\n๐ฆ ${subscription.name || subscription.id}`);
console.log(` Product: ${subscription.productId}`);
console.log(` Status: ${subscription.status}`);
console.log(` Quantity: ${subscription.quantity}`);
console.log(` Current Period: ${subscription.subscriptionStartDate} to ${subscription.subscriptionEndDate}`);
console.log(` Monthly Value: $${(subscription.totalAmount / (subscription.subscriptionTerm || 12)).toFixed(2)}`);
console.log(` Auto Renew: ${subscription.autoRenew ? 'Yes' : 'No'}`);
// Show renewal information
if (subscription.renewalTerm) {
console.log(` Renewal Term: ${subscription.renewalTerm} months`);
}
// Show bundling information
if (subscription.bundled) {
console.log(` Bundled: Yes (Level ${subscription.subscriptionLevel})`);
}
});
} else {
console.log('No active subscriptions found for this customer');
}
}
})
.catch(error => console.log('Error:', error));Fetch Specific Subscription by Name
Retrieve a specific subscription using its name:
const subscriptionName = "Enterprise Software License - Acme Corp";
const encodedName = encodeURIComponent(subscriptionName);
fetch(`https://api.nue.io/subscriptions?name=${encodedName}`, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS' && result.data.length > 0) {
const subscription = result.data[0];
console.log('๐ฏ Specific Subscription Retrieved');
console.log(`Name: ${subscription.name}`);
console.log(`ID: ${subscription.id}`);
console.log(`Customer: ${subscription.customerId}`);
console.log(`Status: ${subscription.status}`);
console.log(`Product: ${subscription.productId}`);
console.log(`Quantity: ${subscription.quantity}`);
console.log(`Total Contract Value: $${subscription.tcv}`);
console.log(`Annual Contract Value: $${subscription.totalACV}`);
console.log(`Subscription Period: ${subscription.subscriptionStartDate} to ${subscription.subscriptionEndDate}`);
if (subscription.externalId) {
console.log(`External ID: ${subscription.externalId}`);
}
} else {
console.log('Subscription not found');
}
})
.catch(error => console.log('Error:', error));Filtering by Bundle Structure
When a customer buys a bundle, Nue creates one subscription for the bundle itself and one for every option underneath it. A customer with a handful of bundles can easily own several dozen subscription rows, most of which are components rather than things the customer thinks of as purchases.
Two parameters let you ask for the rows you actually want instead of fetching everything and filtering client-side:
Parameter | Type | Meaning |
|---|---|---|
bundled | boolean | Whether the subscription is a bundled component priced as part of its parent. |
subscriptionLevel | integer | Depth in the bundle hierarchy. 1 is a top-level subscription, 2 is an option inside it, 3 an option inside that, and so on. |
To show a customer what they bought, ask for the top level only:
const url = `https://api.nue.io/subscriptions?customerId=${customerId}&subscriptionLevel=1`;
fetch(url, { method: 'GET', headers: myHeaders })
.then(response => response.json())
.then(result => {
console.log(`Top-level subscriptions: ${result.data.length}`);
result.data.forEach(s => console.log(` ${s.name}: qty ${s.quantity}`));
})
.catch(error => console.log('Error:', error));subscriptionLevel accepts a comma-separated list, so ?subscriptionLevel=1,2 returns a bundle and its immediate options but stops before the deeper tiers. The two parameters combine with each other and with every other filter, so ?bundled=true&subscriptionLevel=2 returns only the second-level rows that are priced as part of their parent.
bundled=false also matches subscriptions with no value set
?bundled=false returns rows where bundled is false or where it was never populated. This is deliberate, since an unset value means "not a bundled component", but it does mean bundled=true and bundled=false partition the result set between them, and bundled=false may return more rows than a strict equality check would.
Filters Are Validated, Not Ignored
Any field on the subscription object can be used as an equality filter, not just the parameters listed above. Because of that, an unrecognised parameter cannot be silently discarded, because it is indistinguishable from a genuine field filter that the caller expects to be applied. Requests carrying one are rejected:
Request | Result |
|---|---|
?customerId=001...&foo=bar | 400. foo is not a field on the subscription object |
?subscriptionLevel=abc | 400 INVALID_FILTER_VALUE. "Invalid value 'abc' for integer field 'subscriptionLevel'. Expected a whole number." |
?bundled=yes | 400 INVALID_FILTER_VALUE. "Invalid value 'yes' for boolean field 'bundled'. Expected 'true' or 'false'." |
Boolean values are case-insensitive (TRUE works), but only true and false are accepted, so 1 and 0 are not. An out-of-range but well-formed integer such as ?subscriptionLevel=99 is a valid filter that simply matches nothing, and an empty value such as ?subscriptionLevel= is treated as absent.
If you are migrating from an older integration, drop any stray query parameters before upgrading. A request that previously returned 200 while quietly ignoring an unknown parameter will now fail outright.
Filters That a Snapshot Cannot Apply
snapshotDate reconstructs each subscription as it stood on a past date, which means some fields are recalculated rather than read from the stored row. A filter on one of those fields cannot be pushed down into the query.
Rather than dropping it silently, the response tells you:
const url = `https://api.nue.io/subscriptions?customerId=${customerId}`
+ `&snapshotDate=2026-01-01&quantity=5`;
fetch(url, { method: 'GET', headers: myHeaders })
.then(response => response.json())
.then(result => {
// 207 PARTIAL_SUCCESS: the snapshot was produced, one filter was not applied
console.log(result.status);
result.warnings
.filter(w => w.code === 'FILTER_NOT_SUPPORTED_WITH_SNAPSHOT')
.forEach(w => console.log(w.message));
})
.catch(error => console.log('Error:', error));The request returns 207 PARTIAL_SUCCESS with a FILTER_NOT_SUPPORTED_WITH_SNAPSHOT warning naming the filters that were skipped. Treat any 207 as "the rows are right but narrower filtering did not happen" and apply the remaining condition yourself.
bundled and subscriptionLevel are both stored fields, so they apply normally alongside snapshotDate and do not trigger this warning.
Including Related Data
Fetch Subscriptions with Product Details
Include product information in the subscription response:
const customerIds = ["d2e04653-ae90-49df-a986-134cf64f6d03"];
const encodedIds = encodeURIComponent(JSON.stringify(customerIds));
// Include product details in the response
const urlWithProducts = `https://api.nue.io/subscriptions?customerIds=${encodedIds}&includes=product`;
fetch(urlWithProducts, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS' && result.data) {
result.data.forEach(subscription => {
console.log(`\n๐ฆ Subscription: ${subscription.name || subscription.id}`);
console.log(` Status: ${subscription.status}`);
console.log(` Quantity: ${subscription.quantity}`);
console.log(` Value: $${subscription.totalAmount}`);
// Display product information if included
if (subscription.product) {
console.log(`\n ๐ Product Details:`);
console.log(` Name: ${subscription.product.name}`);
console.log(` SKU: ${subscription.product.sku}`);
console.log(` Category: ${subscription.product.productCategory}`);
console.log(` Price Model: ${subscription.product.priceModel}`);
}
// Display pricing tags if included
if (subscription.pricetags && subscription.pricetags.length > 0) {
console.log(`\n ๐ท๏ธ Applied Price Tags:`);
subscription.pricetags.forEach(tag => {
console.log(` โข ${tag.name}: ${tag.type} - ${tag.value}`);
});
}
});
}
})
.catch(error => console.log('Error:', error));Controlling How Much Product Detail Is Returned
By default includes=product returns the complete product, including its full product option graph. For a bundle with a deep option tree that graph is usually far larger than the rest of the response, and if you only need to identify which product a subscription is on, you are paying for data you will not read.
Pass productDetail=root to get the product's own identity and pricing without the option graph:
const urlRootDetail = `https://api.nue.io/subscriptions?customerIds=${encodedIds}`
+ `&includes=product&productDetail=root`;
fetch(urlRootDetail, { method: 'GET', headers: myHeaders })
.then(response => response.json())
.then(result => {
result.data.forEach(subscription => {
const p = subscription.product;
if (p) {
// Still present: identity and pricing
console.log(`${subscription.name}: ${p.sku} (${p.priceModel})`);
console.log(` price book entries: ${p.priceBookEntries.length}`);
// Not present: productOptions and productFeatures
}
});
})
.catch(error => console.log('Error:', error));What each value returns:
Value | Product content |
|---|---|
full (default) | The complete product, including productOptions and productFeatures and everything nested beneath them. |
root | id, sku, name, priceModel, status, publishStatus, uom and priceBookEntries. productOptions and productFeatures are omitted. |
Notes:
- productDetail requires includes=product. Sending it without that returns 400 INVALID_PARAMETER_COMBINATION. It is not ignored.
- Any value other than full or root returns 400 INVALID_PARAMETER. The value is case-insensitive, so ROOT and Root both work, and an empty productDetail= is treated as full.
- In root mode the productOptions and productFeatures keys are absent from the product object rather than present and empty, so check for the key before iterating.
- Pricing is unaffected. priceBookEntries is returned in full in both modes.
Use root for subscription lists, renewal views, and anywhere you are displaying what a customer already owns. Use full when you need the sellable configuration, for example when building a change order against the bundle.
Fetch with All Available Data
Include all available related data:
// Include all available data types
const urlWithAll = `https://api.nue.io/subscriptions?customerIds=${encodedIds}&includes=product,pricetags`;
fetch(urlWithAll, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS' && result.data) {
result.data.forEach(subscription => {
console.log(`\n๐ฏ Complete Subscription Details: ${subscription.name || subscription.id}`);
console.log(` Status: ${subscription.status}`);
console.log(` Customer: ${subscription.customerId}`);
console.log(` Period: ${subscription.subscriptionStartDate} to ${subscription.subscriptionEndDate}`);
// Financial Summary
console.log(`\n ๐ฐ Financial Details:`);
console.log(` List Price: $${subscription.listPrice || 0}`);
console.log(` Sales Price: $${subscription.salesPrice || 0}`);
console.log(` Total Amount: $${subscription.totalAmount || 0}`);
console.log(` TCV: $${subscription.tcv || 0}`);
console.log(` ACV: $${subscription.totalACV || 0}`);
// Product Details
if (subscription.product) {
console.log(`\n ๐ Product:`);
console.log(` ${subscription.product.name} (${subscription.product.sku})`);
console.log(` Category: ${subscription.product.productCategory}`);
console.log(` Price Model: ${subscription.product.priceModel}`);
}
// Bundle Information
if (subscription.bundled) {
console.log(`\n ๐ฆ Bundle Details:`);
console.log(` Bundled: Yes`);
console.log(` Level: ${subscription.subscriptionLevel}`);
console.log(` Parent: ${subscription.parentSubscriptionObject || 'N/A'}`);
}
// Billing Information
console.log(`\n ๐งพ Billing:`);
console.log(` Billing Account: ${subscription.billingAccountId}`);
console.log(` Billing Timing: ${subscription.billingTiming || 'Standard'}`);
// Renewal Information
console.log(`\n ๐ Renewal:`);
console.log(` Auto Renew: ${subscription.autoRenew ? 'Yes' : 'No'}`);
console.log(` Renewal Term: ${subscription.renewalTerm || 'Same as original'} months`);
console.log(` Evergreen: ${subscription.evergreen ? 'Yes' : 'No'}`);
});
}
})
.catch(error => console.log('Error:', error));Point-in-Time Snapshots and Historical Analysis
Subscription snapshots provide powerful capabilities for historical analysis, compliance reporting, and business intelligence. Unlike the current contract view which shows the complete subscription timeline, snapshots show the exact state of subscriptions as they existed on a specific date.
Understanding Subscription Snapshots
Snapshots show the exact state of subscriptions as they existed on a specific date, including upcoming changes scheduled from that point forward.
Key differences from current contract view:
- Current view: Complete timeline for operational management
- Snapshot view: Point-in-time state for historical analysis and compliance
Primary use cases:
- Historical billing reconciliation
- Compliance and audit reporting
- Change impact analysis
Fetch Snapshot by Customer
Generate a point-in-time snapshot to see subscription state at a specific date:
// Fetch snapshot by customer IDs
const customerIds = ["d2e04653-ae90-49df-a986-134cf64f6d03"];
const encodedIds = encodeURIComponent(JSON.stringify(customerIds));
const snapshotDate = "2025-06-26";
fetch(`https://api.nue.io/subscriptions?customerIds=${encodedIds}&snapshotDate=${snapshotDate}&includes=upcomingChanges`, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS' && result.data) {
console.log(`๐ธ Customer Snapshot for ${snapshotDate}`);
result.data.forEach(subscription => {
console.log(`\n๐ฆ ${subscription.name}`);
console.log(` Status: ${subscription.status}`);
console.log(` Quantity: ${subscription.quantity}`);
console.log(` Period: ${subscription.subscriptionStartDate} to ${subscription.subscriptionEndDate}`);
// Show upcoming changes from snapshot date
if (subscription.upcomingChanges && subscription.upcomingChanges.length > 0) {
console.log(` ๐
Upcoming Changes:`);
subscription.upcomingChanges.forEach(change => {
console.log(` โข ${change.changeType} on ${change.startDate}`);
});
}
});
}
})
.catch(error => console.log('Error:', error));Fetch Snapshot by Subscription Name
Retrieve snapshot for a specific subscription by name:
// Fetch snapshot by subscription name
const subscriptionName = "Enterprise License - Customer";
const snapshotDate = "2025-06-26";
fetch(`https://api.nue.io/subscriptions?name=${encodeURIComponent(subscriptionName)}&snapshotDate=${snapshotDate}&includes=upcomingChanges`, {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS' && result.data.length > 0) {
const subscription = result.data[0];
console.log(`๐ธ Subscription Snapshot: ${subscription.name}`);
console.log(` Snapshot Date: ${subscription.snapshotDate}`);
console.log(` Customer: ${subscription.customerId}`);
console.log(` Status: ${subscription.status}`);
console.log(` Quantity: ${subscription.quantity}`);
console.log(` ACV: $${subscription.totalACV}`);
// Show what changes were scheduled from this snapshot date
if (subscription.upcomingChanges && subscription.upcomingChanges.length > 0) {
console.log(`\n๐
Changes scheduled from ${snapshotDate}:`);
subscription.upcomingChanges.forEach(change => {
console.log(` โข ${change.changeType} on ${change.startDate}`);
if (change.changeInQuantity) {
console.log(` Quantity change: ${change.changeInQuantity > 0 ? '+' : ''}${change.changeInQuantity}`);
}
});
} else {
console.log(`\n๐
No changes scheduled from ${snapshotDate}`);
}
}
})
.catch(error => console.log('Error:', error));Snapshot Response Structure
Key snapshot-specific fields:
{
"status": "SUCCESS",
"data": [
{
"autoRenew": false,
"billingAccountId": "001Aq00000qemnyIAA",
"customerId": "001Aq00000qemnyIAA",
"externalId": "a0tAq00000WTI7BIAX",
"externalName": "SUB-000004",
"id": "a0tAq00000WTI7BIAX",
"name": "SUB-000004",
"orderProductId": "802Aq00000MuogcIAB",
"priceBookEntryId": "01uAq000006A1e6IAC",
"productId": "01tAq00000DJScOIAX",
"quantity": 1,
"snapshotDate": "2025-11-09",
"status": "Active",
"subscriptionEndDate": "2026-11-05",
"subscriptionStartDate": "2025-11-06",
"subscriptionTerm": 12,
"tax": 0,
"totalAmount": 106.8,
"totalPrice": 106.8,
"uom": {
"termDimension": "Month"
}
}
],
"warnings": []
}Subscription History Analysis
Fetch subscription history for trend analysis:
async function analyzeSubscriptionHistory(subscriptionName) {
const encodedName = encodeURIComponent(subscriptionName);
const url = `https://api.nue.io/subscriptions?name=${encodedName}&history=true`;
try {
const response = await fetch(url, {
method: 'GET',
headers: myHeaders
});
const result = await response.json();
if (result.status === 'SUCCESS' && result.data.length > 0) {
console.log(`๐ Subscription History Analysis: ${subscriptionName}`);
console.log('='.repeat(60));
// Sort by version to show evolution
const sortedHistory = result.data.sort((a, b) => a.subscriptionVersion - b.subscriptionVersion);
sortedHistory.forEach((version, index) => {
console.log(`\n${index + 1}. Version ${version.subscriptionVersion}`);
console.log(` Period: ${version.subscriptionStartDate} to ${version.subscriptionEndDate}`);
console.log(` Status: ${version.status}`);
console.log(` Quantity: ${version.quantity}`);
console.log(` Total Amount: $${version.totalAmount || 0}`);
console.log(` Modified: ${version.lastModifiedDate} by ${version.lastModifiedById}`);
// Compare with previous version
if (index > 0) {
const previous = sortedHistory[index - 1];
const quantityChange = version.quantity - previous.quantity;
const amountChange = (version.totalAmount || 0) - (previous.totalAmount || 0);
if (quantityChange !== 0 || amountChange !== 0) {
console.log(` ๐ Changes from previous version:`);
if (quantityChange !== 0) {
console.log(` Quantity: ${quantityChange > 0 ? '+' : ''}${quantityChange}`);
}
if (amountChange !== 0) {
console.log(` Amount: ${amountChange > 0 ? '+' : ''}$${amountChange.toLocaleString()}`);
}
}
}
});
// Generate summary insights
console.log(`\n๐ก History Insights:`);
console.log(` Total Versions: ${sortedHistory.length}`);
const firstVersion = sortedHistory[0];
const currentVersion = sortedHistory[sortedHistory.length - 1];
const totalQuantityChange = currentVersion.quantity - firstVersion.quantity;
const totalAmountChange = (currentVersion.totalAmount || 0) - (firstVersion.totalAmount || 0);
console.log(` Quantity Evolution: ${firstVersion.quantity} โ ${currentVersion.quantity} (${totalQuantityChange > 0 ? '+' : ''}${totalQuantityChange})`);
console.log(` Value Evolution: $${firstVersion.totalAmount || 0} โ $${currentVersion.totalAmount || 0} (${totalAmountChange > 0 ? '+' : ''}$${totalAmountChange.toLocaleString()})`);
return sortedHistory;
} else {
console.log('No subscription history found');
return [];
}
} catch (error) {
console.error('History analysis failed:', error);
return [];
}
}
// Usage example
analyzeSubscriptionHistory("Enterprise Software License - Acme Corp")
.then(history => {
console.log(`Analysis complete: ${history.length} versions analyzed`);
});Query Parameters Reference
Parameter | Type | Required | Description | Options |
|---|---|---|---|---|
customerIds | Array[String] | * | JSON-encoded array of customer IDs | ["customer-uuid-1", "customer-uuid-2"] |
name | String | * | Specific subscription name to fetch | "Enterprise License - Customer" |
snapshotDate | String | No | Point-in-time snapshot date (YYYY-MM-DD) | "2025-06-26" |
status | String | No | Filter by subscription status | "active" "expired" "canceled" |
version | String | No | Filter by version type | "latest" "snapshot" |
history | Boolean | No | Include subscription history | true false |
includes | String | No | Related data to include | "product" "pricetags" "upcomingChanges" |
productDetail | String | No | How much of each product to return. root full includes=product 400 | "full" "root" |
bundled | Boolean | No | Whether the subscription is a bundled component priced as part of its parent. false | true false |
subscriptionLevel | Integer | No | Depth in the bundle hierarchy. 1 | 1 2 "1,2" |
*Either customerIds or name is required
Response Structure
Regular Subscription Response (200 OK)
{
"status": "SUCCESS",
"data": [
{
"actualSubscriptionTerm": 12,
"autoRenew": true,
"billCycleDay": "7th of Month",
"billCycleStartMonth": "03",
"billingAccountId": "81dd1eb9-7a2c-4486-be76-b1ac2f88bbb1",
"billingPeriod": "Annual",
"billingTiming": "In Advance",
"bundled": true,
"createdById": "74ef82c9-a9d7-4262-8331-ba7ac33d1f76",
"createdDate": "2025-07-02T23:12:30.582Z",
"customerId": "81dd1eb9-7a2c-4486-be76-b1ac2f88bbb1",
"evergreen": false,
"id": "f594fd3a-3659-4039-ae5c-b2c6863d98a5",
"lastModifiedById": "74ef82c9-a9d7-4262-8331-ba7ac33d1f76",
"lastModifiedDate": "2025-07-02T23:12:30.621Z",
"listPrice": 19.9,
"name": "SUB-00000309",
"nextBillingDate": "2025-07-01",
"orderOnDate": "2025-07-02",
"orderProductId": "03139976-1cc2-4430-8f61-996e641c8745",
"parentId": "3b573778-89c0-491d-a972-bd3073d9add4",
"parentObjectType": "Subscription",
"priceBookEntryId": "01u7z000005YNBAAA4",
"priceBookId": "01s7z000006Dt4rAAC",
"productId": "01t7z00000DXt09AAD",
"quantity": 2,
"renewalTerm": 12,
"rootId": "3b573778-89c0-491d-a972-bd3073d9add4",
"salesPrice": 19.9,
"status": "Active",
"subscriptionCompositeId": "SUB-00000309_1",
"subscriptionEndDate": "2026-06-30",
"subscriptionLevel": 2,
"subscriptionStartDate": "2025-07-01",
"subscriptionTerm": 12,
"subscriptionVersion": 1,
"taxAmount": 0,
"tcv": 0,
"totalACV": 0,
"totalAmount": 0,
"totalPrice": 0,
"totalTCV": 0,
"uomId": "a0s7z00000HYFPTAA5"
}
],
"warnings": []
}Snapshot Response (with snapshotDate)
{
"status": "SUCCESS",
"data": [
{
"id": "subscription-uuid",
"name": "Enterprise Software License",
"customerId": "customer-uuid",
"snapshotDate": "2025-06-26",
"status": "active",
"quantity": 100,
"upcomingChanges": [
{
"changeType": "UpdateQuantity",
"startDate": "2025-07-01"
}
]
}
],
"warnings": []
}Error Handling
Common Retrieval Errors
Error | Description | Resolution |
|---|---|---|
INVALID_CUSTOMER_ID | Customer ID format invalid | Verify customer ID format |
INVALID_FILTER_VALUE | A filter value does not match the field's type, for example subscriptionLevel=abc bundled=1 | Send a whole number for integer fields and true false |
INVALID_PARAMETER_COMBINATION | productDetail includes=product | Add includes=product productDetail |
Unknown filter field ( 400 | A query parameter does not correspond to any field on the subscription object. Previously such parameters were ignored. | Remove stray parameters from the request. |
FILTER_NOT_SUPPORTED_WITH_SNAPSHOT 207 | A filter was combined with snapshotDate | Not an error. The data is correct but unfiltered on that field. Apply the condition client-side, or drop snapshotDate |
SUBSCRIPTION_NOT_FOUND | Named subscription doesn't exist | Check subscription name spelling |
INVALID_SNAPSHOT_DATE | Snapshot date format invalid | Use YYYY-MM-DD format |
PARAMETER_CONFLICT | Conflicting parameters used | Review parameter restrictions |
Robust Subscription Fetching
async function safeSubscriptionFetch(customerIds, options = {}) {
try {
// Validate inputs
if (!Array.isArray(customerIds) || customerIds.length === 0) {
throw new Error('CustomerIds must be a non-empty array');
}
const encodedIds = encodeURIComponent(JSON.stringify(customerIds));
let url = `https://api.nue.io/subscriptions?customerIds=${encodedIds}`;
// Add optional parameters
if (options.status) {
url += `&status=${options.status}`;
}
if (options.includes) {
url += `&includes=${options.includes}`;
}
if (options.snapshotDate) {
url += `&snapshotDate=${options.snapshotDate}`;
}
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 {
subscriptions: result.data || [],
found: result.data?.length || 0,
requested: customerIds.length,
warnings: result.warnings || []
};
} catch (error) {
console.error('Subscription fetch error:', error);
return {
subscriptions: [],
found: 0,
requested: customerIds.length,
error: error.message
};
}
}Best Practices
Data Retrieval
- Use appropriate filters to reduce data transfer
- Include related data selectively based on needs
- Leverage snapshots for historical analysis
- Monitor upcoming changes proactively
Performance Optimization
- Batch customer queries efficiently
- Cache frequently accessed subscription data
- Use status filters to focus on relevant subscriptions
- Implement pagination for large datasets
Business Intelligence
- Track subscription trends over time
- Monitor renewal patterns and auto-renew rates
- Analyze product adoption across subscriptions
- Generate renewal forecasts from expiration data
This comprehensive guide enables you to efficiently retrieve and analyze subscription data using the Nue Lifecycle Management API, supporting everything from simple lookups to complex portfolio analysis and predictive renewal management.