Create Draft Orders
This guide provides comprehensive instructions for creating draft orders using the Nue Lifecycle Management API. Learn how to create simple orders, configure complex bundles with add-ons, and implement efficient order management workflows.
Prerequisites
Before you begin, ensure you have:
- A valid Nue API key with order creation permissions
- Valid customer IDs for order creation
- Price book entry IDs for products you want to order
- Basic knowledge of REST APIs and JSON
- Understanding of subscription and pricing concepts
Finding Price Book Entry IDs
Price book entry IDs (priceBookEntryId) are retrieved from the GET /catalog/products API. Each product in the catalog response includes a priceBookEntries array containing the available pricing options with their unique identifiers. See the Fetching Products guide for details on retrieving product catalog data.
Authentication
All order creation 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");Order Options
The optional options object on the request controls invoice preview and tax calculation.
Option | Type | Default | Description |
|---|---|---|---|
previewInvoice | boolean | true | Whether to return preview invoices for the draft order |
calculateTax | boolean | false | Whether to calculate tax for the order |
When previewInvoice is true, the response includes a data.previewInvoices array. When it is false, the previewInvoices key is omitted from the response entirely.
Preview invoices never carry tax: each entry reports taxAmount: 0 and taxStatus: "NotCalculated" regardless of the calculateTax setting. Calculated tax appears on the order header instead, in data.order.tax, with data.order.totalAmount equal to totalAmountWithoutTax plus tax.
What the preview invoice represents
data.previewInvoices contains the first invoice the order will generate, not every invoice over the subscription term. Its amount depends on the subscription's billing settings (billing period, billing timing, bill cycle day and bill cycle start month):
- On an annual plan billed in advance, where the start date lines up with the bill cycle, the first invoice covers the whole year, so its amount equals data.order.totalAmountWithoutTax.
- On a monthly or quarterly plan, the first invoice covers only the first billing period.
- When the start date does not line up with the bill cycle, the first invoice covers only the partial period up to the first bill cycle date. For example, a 12-month order starting January 1 with a bill cycle starting in April produces a first invoice for January through March (25% of the order), and the next invoice carries the balance.
- When the subscription starts in the future, the first invoice is dated on the start date, not today.
Showing buyers what they will pay
data.order.totalAmount is the value of the whole order, including tax. data.previewInvoices[0].amount is the first invoice, before tax. Neither is guaranteed to be the amount charged to the buyer's card at checkout: the card is charged the first invoice plus the tax calculated when that invoice is generated. If your checkout shows the preview invoice amount, label it as the next invoice amount before tax, for example "Next invoice (before tax)".
Calculating tax
calculateTax: true has two prerequisites. Both return 400 when unmet:
- A tax engine integration must be configured for the tenant. Otherwise the request fails with ORDER_INVALID_CONFIGURATION -- "Cannot calculate tax for this draft order request because tax integration is not enabled."
- The order must resolve to a valid shipping address. Otherwise the request fails with "Cannot calculate tax for order without a valid shipping address." The address can come from either the customer record (shippingStreet, shippingCity, shippingState, shippingPostalCode, shippingCountry) or the shippingAddress field on the order request. Note that shippingAddress is a string, not an object -- passing an object returns ORDER_INVALID_FIELD_VALUE_FORMAT.
const orderData = {
customer: { id: "d2e04653-ae90-49df-a986-134cf64f6d03" },
effectiveDate: "2026-09-01",
shippingAddress: "525 Market St, San Francisco, CA 94105, US",
options: {
calculateTax: true
},
orderProducts: [
{
priceBookEntryId: "01uQL000009Tsn7YAC",
quantity: 10,
subscriptionStartDate: "2026-09-01",
subscriptionTerm: 12
}
]
};Basic Draft Order Creation
Create Simple Draft Order
Try it now: Create Draft Order →
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
// Create a simple draft order
const orderData = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-01-01",
orderProducts: [
{
priceBookEntryId: "01uQL000009Tsn7YAC",
quantity: 1,
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12 // 12 months
}
]
};
fetch('https://api.nue.io/orders', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(orderData)
})
.then(response => response.json())
.then(result => {
console.log('Draft order created successfully:', result);
if (result.status === 'SUCCESS') {
const order = result.data.order;
console.log(`Order ID: ${order.id}`);
console.log(`Order Number: ${order.orderNumber}`);
console.log(`Status: ${order.status}`);
console.log(`List Total: $${order.listTotal}`);
console.log(`Discount: $${order.discountAmount}`);
// Display invoice preview
if (result.data.previewInvoices && result.data.previewInvoices.length > 0) {
const invoice = result.data.previewInvoices[0];
console.log(`\nInvoice Preview:`);
console.log(`Amount: $${invoice.amount}`);
console.log(`Due Date: ${invoice.dueDate}`);
}
}
})
.catch(error => console.log('Error:', error));Create Draft Order with Complete Configuration
Create a draft order with comprehensive configuration including billing details:
const comprehensiveOrder = {
// Customer information
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
// Order details
effectiveDate: "2025-01-01",
name: "Q1 2025 Software License Order",
description: "Annual software license renewal with additional users",
orderReferenceNumber: "ORD-2025-001",
orderType: "New",
// Purchase order information
poNumber: "PO-2025-12345",
poDate: "2024-12-20",
// Billing configuration
billToContact: {
id: "123e4567-e89b-12d3-a456-426614174000"
},
shipToContact: "456e7890-e89b-12d3-a456-426614174000",
paymentMethod: "Credit Card",
// Products being ordered
orderProducts: [
{
priceBookEntryId: "01uQL000009Tsn8YAC",
quantity: 50,
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12,
// Apply pricing tags
priceTagCodes: ["ENTERPRISE2025"]
},
{
priceBookEntryId: "01uQL000009Tsn9YAC",
quantity: 40,
subscriptionStartDate: "2025-01-15",
subscriptionTerm: 6
}
],
// Options for order processing
options: {
previewInvoice: true,
calculateTax: true
}
};
fetch('https://api.nue.io/orders', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(comprehensiveOrder)
})
.then(response => response.json())
.then(result => {
console.log('Comprehensive draft order created:', result);
const order = result.data.order;
console.log(`\n✅ Draft Order Created Successfully`);
console.log(`Order ID: ${order.id}`);
console.log(`Order Number: ${order.orderNumber}`);
console.log(`Customer: ${order.customerName}`);
console.log(`Effective Date: ${order.orderStartDate}`);
console.log(`Total Products: ${order.orderProducts?.length || 0}`);
console.log(`List Total: $${order.listTotal}`);
console.log(`Net Total: $${order.netTotal}`);
// Display assets preview
if (result.data.assets && result.data.assets.length > 0) {
console.log(`\n📦 Assets to be Created: ${result.data.assets.length}`);
result.data.assets.forEach(asset => {
console.log(`- ${asset.productName}: ${asset.quantity} units`);
console.log(` Duration: ${asset.startDate} to ${asset.endDate}`);
});
}
})
.catch(error => console.log('Error:', error));Specifying the Subscription Term
Every termed subscription line in a draft order is driven by a term, not an end date. Each orderProducts entry must provide either subscriptionTerm (with subscriptionStartDate) or coTermAsset — these are mutually exclusive, and one is required for any product that is not evergreen or one-time. A subscriptionEndDate you send on the line is not used to set the term: for a normal subscription the engine recalculates the end date from subscriptionStartDate + subscriptionTerm.
This means if your system only knows the start and end dates a customer wants, you must convert those dates into a term before building the order payload.
Converting dates to a term
Call the Calculate Term endpoint with the start and end dates to get the subscriptionTerm value the order requires:
curl -X POST "https://api.nue.io/pricing/terms/calculateTerm" \
-H "nue-api-key: YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{ "startDate": "2026-01-01", "endDate": "2026-12-31", "termType": "month", "termBasis": "30/360" }
]
}'{
"results": [
{ "term": 12, "termType": "Month", "termBasis": "30/360" }
]
}Then use the returned term on the order line:
orderProducts: [
{
priceBookEntryId: "PBE-000123",
quantity: 50,
subscriptionStartDate: "2026-01-01",
subscriptionTerm: 12 // from calculateTerm
}
]termType accepts year, month, quarter, semi-annual, or week (case-insensitive). termBasis is the day-count convention — 30/360, eu30/360, actual/actual, actual/360, or actual/365. Most subscription products use 30/360.
Previewing the end date
The inverse endpoint, Calculate End Date, takes a start date and term and returns the end date — useful for showing the customer where the subscription will land before you submit the order:
curl -X POST "https://api.nue.io/pricing/terms/calculateEndDate" \
-H "nue-api-key: YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{ "startDate": "2026-01-01", "term": 12, "termType": "month", "termBasis": "30/360" }
]
}'{
"results": [
{ "endDate": "2026-12-31", "termType": "Month", "termBasis": "30/360" }
]
}Both endpoints are batch — send multiple calculations in the requests array and receive a results array in the same order. See the Commerce API reference under Term Calculations for the full request and response schemas.
Payment Method Integration
Using Nue Payment Links with Self Service
For self service use cases where customers need to pay for invoices immediately upon order generation, you can integrate with different payment systems. The requirements depend on whether you're using Stripe Invoicing or Nue Collections (Payment Links).
Payment System Requirements:
- Stripe Invoicing: Only requires transactionHub object (no payment method needed)
- Nue Collections (Payment Links): Requires both transactionHub object AND paymentMethodObject
Important Notes:
- The transactionHub object is only required once per customer. If passed again for the same customer, it won't overwrite or create duplicates
- If the same external payment method ID already exists for the customer, it will update the existing payment method instead of creating a duplicate
For Stripe Invoicing (Transaction Hub Only)
const stripeInvoicingOrder = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-01-01",
// Only transaction hub needed for Stripe Invoicing
transactionHub: {
externalSystem: "Stripe",
externalId: "cus_RsS1MZKCy2PmJ7", // Stripe customer ID
transactionType: "Customer"
},
orderProducts: [
{
priceBookEntryId: "01uQL000009Tsn8YAC",
quantity: 1,
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12
}
],
options: {
previewInvoice: true,
calculateTax: false
}
};For Nue Collections/Payment Links (Transaction Hub + Payment Method)
const nueCollectionsOrder = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-01-01",
// Required: Transaction hub linking Nue customer to Stripe customer
transactionHub: {
externalSystem: "Stripe",
externalId: "cus_RsS1MZKCy2PmJ7", // Stripe customer ID
transactionType: "Customer"
},
// Required for Nue Collections: External payment method
paymentMethodObject: {
externalPaymentMethodId: "pm_1RCqTHRqbULxAkCMLnnv6B1R", // Stripe payment method ID
name: "Primary Credit Card",
paymentMethodType: "Credit Card",
paymentSystem: "Stripe",
autoChargeEnabled: true
},
orderProducts: [
{
priceBookEntryId: "01uQL000009Tsn8YAC",
quantity: 1,
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12
}
],
options: {
previewInvoice: true,
calculateTax: false
}
};
fetch('https://api.nue.io/orders', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(nueCollectionsOrder)
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS') {
console.log('✅ Draft Order Created with Payment Method for Nue Collections');
console.log(`Order ID: ${result.data.order.id}`);
console.log(`Payment Method will be created during activation`);
}
})
.catch(error => console.log('Error:', error));Using Existing Nue Payment Method
const orderWithExistingPayment = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-01-01",
// Transaction hub (only needed once per customer)
transactionHub: {
externalSystem: "Stripe",
externalId: "cus_RsS1MZKCy2PmJ7",
transactionType: "Customer"
},
// Reference existing Nue payment method
paymentMethodObject: {
id: "d540ce64-c096-4ac4-aed2-f050ba04ceea" // Existing Nue payment method ID
},
orderProducts: [
{
priceBookEntryId: "01uQL000009TsnaYAC",
quantity: 1,
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12
}
]
};Product-Level Payment Method Configuration
You can also specify payment methods at the individual product or add-on level, which will override the order-level payment method for those specific items:
const orderWithProductPayments = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-01-01",
// Order-level transaction hub
transactionHub: {
externalSystem: "Stripe",
externalId: "cus_RsS1MZKCy2PmJ7",
transactionType: "Customer"
},
// Default payment method for the order
paymentMethodObject: {
externalPaymentMethodId: "pm_default_card",
name: "Default Payment Method",
paymentMethodType: "Credit Card",
paymentSystem: "Stripe",
autoChargeEnabled: true
},
orderProducts: [
{
priceBookEntryId: "01uQL000009TsnbYAC",
quantity: 1,
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12,
// Product-specific payment method (overrides order-level)
paymentMethodObject: {
externalPaymentMethodId: "pm_1RCqTHRqbULxAkCMLnnv6B1R",
name: "Product Payment Card",
paymentMethodType: "Credit Card",
paymentSystem: "Stripe",
autoChargeEnabled: true
},
addOns: [
{
productOptionId: "opt-premium-support",
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12,
// Add-on specific payment method
paymentMethodObject: {
id: "existing-payment-method-id"
}
}
]
}
]
};Payment Method Fallback Logic
The payment method assignment follows this hierarchy:
- Add-on level: If an add-on has a paymentMethodObject, it uses that payment method
- Product level: If no add-on payment method, falls back to the product's paymentMethodObject
- Order level: If no product payment method, falls back to the order's paymentMethodObject
- No payment method: If no payment method is specified at any level, no default payment method will be set on the subscription
How Payment Methods Are Processed
When you include payment method information in your draft order:
- During Draft Creation: The payment method object information is stored with the draft order
- During Order Activation:
- Nue creates or updates the payment method using the /billing/payment-methods:upsert endpoint
- The payment method is then assigned to the created subscriptions as defaultPaymentMethodId
- If the same external payment method ID already exists for the customer, the existing payment method is updated instead of creating a duplicate
CUSTOMER CONSENT REQUIRED
When payment method objects are passed into Nue from self-service applications, they are automatically saved and stored within Nue Collections for processing the current transaction as autopay and for future use. It is the customer's responsibility to ensure they have obtained appropriate user permissions before submitting payment method information.
Consider implementing a consent popup or checkbox in your self-service interface that clearly informs users their payment method will be saved before submitting the order.
Payment Method Object Schema
Field | Type | Required | Description | Valid Values |
|---|---|---|---|---|
externalPaymentMethodId | String | Yes* | External payment method ID (e.g., Stripe PM ID) | Any string |
name | String | Yes* | Display name for the payment method | Any string |
paymentMethodType | String | Yes* | Type of payment method | "Electronic", "NonElectronic", "ACH", "Bank Transfer", "Cash", "Check", "Credit Card", "Debit Card", "Paypal", "Wire Transfer", "Other" |
paymentSystem | String | Yes* | Payment system identifier (e.g., "Stripe") | Any string |
autoChargeEnabled | Boolean | Yes* | Whether automatic charging is enabled - must be set to true to charge users for the transaction | true or false |
id | String | Yes** | Existing Nue payment method ID | UUID format |
*Required when creating a new payment method from external system **Required when referencing an existing Nue payment method
Note: The autoChargeEnabled field is used in the order payload but maps to internal payment method behavior. The actual Payment Method object stores the customer reference via accountId (maps to customerId) and other system fields.
Nue Payment Links vs Stripe Invoicing Integration Summary
When to use each approach:
Use Case | Required Fields | Description |
|---|---|---|
Stripe Invoicing | Only transactionHub | Traditional Stripe billing where Stripe manages invoicing directly |
Nue Collections (Payment Links) | transactionHub + paymentMethodObject | Modern approach using Nue Payment Links for customer payment collection |
Key Benefits of Nue Collections:
- Centralized payment method management in Nue
- Consistent payment experience across all billing scenarios
- Enhanced payment method controls and automation
- Better integration with Nue subscription lifecycle management
Migration Path: If you're currently using Stripe Invoicing and want to migrate to Nue Collections, you can gradually transition by:
- Adding paymentMethodObject to new orders while keeping existing transactionHub
- The system will automatically create Nue payment methods linked to your Stripe payment methods
- Future billing will use the Nue Collections system instead of Stripe Invoicing
Custom Fields on Orders
Setting Custom Fields at Order Level
You can define custom fields on the order object during draft order creation. These fields must be synced via the business objects page in Nue settings before they can be used.
const orderWithCustomFields = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-07-29",
// Custom fields at order level
"Ruby__Custom__c": "Value1",
"Department__c": "Engineering",
"ProjectCode__c": "PROJ-2025-001",
orderProducts: [
{
priceBookEntryId: "01uE100000DlOw9IAF",
quantity: 2,
subscriptionStartDate: "2025-07-29",
subscriptionTerm: 12
}
],
options: {
previewInvoice: true,
calculateTax: false
}
};
fetch('https://api.nue.io/orders', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(orderWithCustomFields)
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS') {
console.log('✅ Order created with custom fields');
console.log(`Order ID: ${result.data.order.id}`);
}
})
.catch(error => console.log('Error:', error));Setting Custom Fields at Order Product Level
Custom fields can also be set on individual order products. These fields will be inherited by the generated subscriptions, allowing for product-specific metadata.
const orderWithProductCustomFields = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-07-29",
// Order-level custom fields
"Department__c": "Engineering",
orderProducts: [
{
priceBookEntryId: "01uE100000DlOw9IAF",
quantity: 2,
subscriptionStartDate: "2025-07-29",
subscriptionTerm: 12,
// Product-level custom fields (inherited by subscription)
"Ruby__Custom__c": "ProductValue1",
"ServiceTier__c": "Premium",
"Region__c": "US-West"
},
{
priceBookEntryId: "01uE100000DlOw8IAF",
quantity: 1,
subscriptionStartDate: "2025-07-29",
subscriptionTerm: 12,
// Different custom fields for this product
"Ruby__Custom__c": "ProductValue2",
"ServiceTier__c": "Standard",
"Region__c": "US-East"
}
],
options: {
previewInvoice: true,
calculateTax: false
}
};Custom Field Requirements
Business Objects Sync Required
Custom fields must be synced via the business objects page in Nue settings before they can be used in API requests. This ensures field validation and proper data type handling.
Key Points:
- Custom fields can be set at both order and order product levels
- Order product custom fields are inherited by the generated subscriptions
- Field names must match exactly as configured in Nue settings
- Custom fields support various data types (text, number, date, boolean, etc.)
- Invalid custom field names will result in API validation errors
Custom Field Inheritance Flow
Order Level Custom Fields
├── Applied to: Order record
└── Scope: Order-wide metadata
Order Product Custom Fields
├── Applied to: Order Product record
├── Inherited by: Generated Subscription
└── Scope: Order Product/Subscription-specific metadataAdvanced Bundle Configuration
Create Order with Bundle Products
Configure complex bundle products with multiple add-ons:
const bundleOrder = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-01-01",
name: "Enterprise Software Bundle Order",
orderProducts: [
{
// Main bundle product
priceBookEntryId: "01uQL000009TsnaYAC",
quantity: 1,
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12,
// Configure bundle add-ons (Level 1)
addOns: [
{
productOptionId: "opt-additional-users",
productOptionQuantity: 25, // 25 additional user licenses
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12,
priceTagIds: ["volume-discount-users"]
},
{
productOptionId: "opt-premium-support",
// No quantity needed for fixed add-ons
subscriptionStartDate: "2025-01-01",
subscriptionTerm: 12
},
{
productOptionId: "opt-integration-pack",
subscriptionStartDate: "2025-02-01", // Later start date
subscriptionTerm: 10, // Co-terminate with main bundle
// Level 2 add-ons within integration pack
addOns: [
{
productOptionId: "opt-salesforce-connector",
subscriptionStartDate: "2025-02-01",
subscriptionTerm: 10
},
{
productOptionId: "opt-custom-apis",
productOptionQuantity: 5, // 5 custom API endpoints
subscriptionStartDate: "2025-03-01",
subscriptionTerm: 9
}
]
}
]
}
],
options: {
previewInvoice: true,
calculateTax: true
}
};
fetch('https://api.nue.io/orders', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(bundleOrder)
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS') {
const order = result.data.order;
console.log(`\n🎯 Bundle Order Created Successfully`);
console.log(`Order ID: ${order.id}`);
console.log(`Bundle Components: ${order.orderProducts?.length || 0} main products`);
// Display bundle breakdown
if (result.data.assets) {
console.log(`\n📦 Bundle Components (${result.data.assets.length} assets):`);
result.data.assets.forEach(asset => {
console.log(`- ${asset.productName}`);
console.log(` Quantity: ${asset.quantity}`);
console.log(` Period: ${asset.startDate} to ${asset.endDate}`);
console.log(` Status: ${asset.status}`);
});
}
// Display pricing breakdown
console.log(`\n💰 Pricing Breakdown:`);
console.log(`List Total: $${order.listTotal}`);
console.log(`Discount: $${order.discountAmount}`);
console.log(`Net Total: $${order.netTotal}`);
console.log(`Tax: $${order.taxAmount || 0}`);
console.log(`Grand Total: $${order.grandTotal}`);
}
})
.catch(error => {
if (error.message.includes('INVALID_ADDON_HIERARCHY')) {
console.error('Bundle configuration error: Invalid add-on hierarchy');
} else if (error.message.includes('MISSING_ADDON_QUANTITY')) {
console.error('Bundle configuration error: Missing required add-on quantity');
} else {
console.error('Order creation failed:', error);
}
});Dynamic Product Options
When ordering bundles with dynamic product options, pass the composite ID from the catalog response as the productOptionId. The composite ID combines the dynamic option ID with the price book entry ID in the format {dynamicOptionId}_{priceBookEntryId}.
const dynamicOptionOrder = {
customer: {
id: "65a7cb55-9510-4a83-9431-e81d15b7ae43"
},
effectiveDate: "2025-07-29",
orderProducts: [
{
// Main bundle product
priceBookEntryId: "01uQL00000CeQPtYAN",
quantity: 1,
subscriptionStartDate: "2025-07-29",
subscriptionTerm: 12,
// Add dynamic option using composite ID from catalog
addOns: [
{
// Composite ID: {dynamicOptionId}_{priceBookEntryId}
productOptionId: "a0jQL00000JP4IPYA1_01uQL00000CXUGXYA5"
// productOptionQuantity only needed if quantityEditable is true
}
]
}
],
options: {
previewInvoice: true,
calculateTax: false
}
};
fetch('https://api.nue.io/orders', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(dynamicOptionOrder)
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS') {
console.log('✅ Order with dynamic option created');
// The system automatically resolves the composite ID
result.data.orderProducts.forEach(product => {
console.log(`Product: ${product.productName}`);
console.log(` productOptionId: ${product.productOptionId}`);
console.log(` priceBookEntryId: ${product.priceBookEntryId}`);
});
}
})
.catch(error => console.log('Error:', error));How Dynamic Options Work:
- Fetch the catalog to get available dynamic options with their composite IDs
- Pass the composite ID as productOptionId in the addOns array
- The system resolves the composite ID and creates the order product with the correct pricing
Finding Dynamic Option IDs
Query GET /catalog/products to retrieve bundle products. Dynamic product options are identified by the optionProductFilter field and have composite IDs in the format {dynamicOptionId}_{priceBookEntryId}. See the Fetching Products guide for details.
Co-Termination with Existing Assets
Create orders that co-terminate with existing customer assets:
const coTermOrder = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-01-01",
name: "Mid-Term License Addition",
description: "Adding licenses to co-terminate with existing subscription",
orderProducts: [
{
priceBookEntryId: "01uQL000009TsncYAC",
quantity: 10,
subscriptionStartDate: "2025-01-01",
// Co-terminate with existing asset instead of fixed term
coTermAsset: "asset-123e4567-e89b-12d3-a456-426614174000",
priceTagCodes: ["PRORATED2025"]
}
],
options: {
previewInvoice: true,
calculateTax: true
}
};
fetch('https://api.nue.io/orders', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(coTermOrder)
})
.then(response => response.json())
.then(result => {
if (result.status === 'SUCCESS') {
console.log(`✅ Co-Termination Order Created`);
const assets = result.data.assets;
if (assets && assets.length > 0) {
const asset = assets[0];
console.log(`New Asset End Date: ${asset.endDate}`);
console.log(`Co-terminated with: ${asset.coTermAsset}`);
console.log(`Prorated Amount: $${asset.proratedAmount || 'N/A'}`);
}
}
})
.catch(error => console.log('Co-termination order failed:', error));Order Management Workflows
Draft Order Creation Service
Implement a comprehensive order creation service with validation:
class DraftOrderService {
constructor(apiKey) {
this.apiKey = apiKey;
this.headers = new Headers();
this.headers.append("nue-api-key", apiKey);
this.headers.append("Content-Type", "application/json");
}
async createDraftOrder(orderInput) {
try {
// Step 1: Validate order data
const validatedOrder = this.validateOrderData(orderInput);
// Step 2: Enrich with business defaults
const enrichedOrder = this.enrichOrderData(validatedOrder);
// Step 3: Create draft order
const draftOrder = await this.submitDraftOrder(enrichedOrder);
// Step 4: Process creation result
const processedResult = this.processDraftOrderResult(draftOrder);
return {
success: true,
order: processedResult.order,
assets: processedResult.assets,
invoices: processedResult.invoices,
warnings: processedResult.warnings,
summary: this.generateOrderSummary(processedResult)
};
} catch (error) {
console.error('Draft order creation failed:', error);
return {
success: false,
error: error.message,
orderInput
};
}
}
validateOrderData(orderInput) {
const errors = [];
// Required field validation
if (!orderInput.customer?.id) {
errors.push('Customer ID is required');
}
if (!orderInput.effectiveDate) {
errors.push('Effective date is required');
}
if (!orderInput.orderProducts || orderInput.orderProducts.length === 0) {
errors.push('At least one order product is required');
}
// Product validation
orderInput.orderProducts?.forEach((product, index) => {
if (!product.priceBookEntryId) {
errors.push(`Product ${index + 1}: Price book entry ID is required`);
}
if (!product.quantity || product.quantity <= 0) {
errors.push(`Product ${index + 1}: Quantity must be greater than 0`);
}
if (!product.subscriptionStartDate) {
errors.push(`Product ${index + 1}: Subscription start date is required`);
}
if (!product.subscriptionTerm && !product.coTermAsset) {
errors.push(`Product ${index + 1}: Either subscription term or co-term asset is required`);
}
// Validate bundle add-ons
if (product.addOns) {
this.validateAddOns(product.addOns, `Product ${index + 1}`, errors);
}
});
// Date validation
if (orderInput.effectiveDate) {
const effectiveDate = new Date(orderInput.effectiveDate);
const today = new Date();
if (effectiveDate < today.setHours(0, 0, 0, 0)) {
errors.push('Effective date cannot be in the past');
}
}
if (errors.length > 0) {
throw new Error(`Validation failed:\n${errors.join('\n')}`);
}
return orderInput;
}
validateAddOns(addOns, parentPath, errors) {
addOns.forEach((addOn, index) => {
const addOnPath = `${parentPath} Add-on ${index + 1}`;
if (!addOn.productOptionId) {
errors.push(`${addOnPath}: Product option ID is required`);
}
// If it's a quantity-based add-on, quantity is required
if (addOn.productOptionQuantity !== undefined && addOn.productOptionQuantity <= 0) {
errors.push(`${addOnPath}: Quantity must be greater than 0`);
}
// Validate nested add-ons (up to 3 levels deep)
if (addOn.addOns && addOn.addOns.length > 0) {
this.validateAddOns(addOn.addOns, addOnPath, errors);
}
});
}
enrichOrderData(orderInput) {
const enriched = {
...orderInput,
// Set default options if not provided
options: {
previewInvoice: true,
calculateTax: false,
...orderInput.options
},
// Generate order name if not provided
name: orderInput.name || `Order for ${orderInput.customer.id}`,
// Set order type default
orderType: orderInput.orderType || "New"
};
// Enrich product data
enriched.orderProducts = orderInput.orderProducts.map(product => ({
...product,
// Ensure subscription start date is not before effective date
subscriptionStartDate: this.adjustStartDate(
product.subscriptionStartDate,
orderInput.effectiveDate
)
}));
return enriched;
}
adjustStartDate(startDate, effectiveDate) {
const start = new Date(startDate);
const effective = new Date(effectiveDate);
// If start date is before effective date, use effective date
return start < effective ? effectiveDate : startDate;
}
async submitDraftOrder(orderData) {
const response = await fetch('https://api.nue.io/orders', {
method: 'POST',
headers: this.headers,
body: JSON.stringify(orderData)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API Error ${response.status}: ${errorText}`);
}
const result = await response.json();
if (result.status !== 'SUCCESS') {
throw new Error(`Order creation failed: ${result.message || 'Unknown error'}`);
}
return result;
}
processDraftOrderResult(apiResult) {
return {
order: apiResult.data.order,
assets: apiResult.data.assets || [],
previewInvoices: apiResult.data.previewInvoices || [],
entitlements: apiResult.data.entitlements || [],
warnings: apiResult.warnings || []
};
}
generateOrderSummary(result) {
const order = result.order;
const assets = result.assets;
const invoices = result.invoices;
return {
orderId: order.id,
orderNumber: order.orderNumber,
customerName: order.customerName,
totalProducts: order.orderProducts?.length || 0,
totalAssets: assets.length,
totalInvoices: invoices.length,
listTotal: order.listTotal,
netTotal: order.netTotal,
grandTotal: order.grandTotal,
status: order.status,
effectiveDate: order.orderStartDate,
hasWarnings: result.warnings.length > 0
};
}
}
// Usage example
const orderService = new DraftOrderService("YOUR_API_KEY_HERE");
const newOrder = {
customer: {
id: "d2e04653-ae90-49df-a986-134cf64f6d03"
},
effectiveDate: "2025-02-01",
name: "Q1 2025 License Expansion",
orderProducts: [
{
priceBookEntryId: "01uQL000009TsndYAC",
quantity: 100,
subscriptionStartDate: "2025-02-01",
subscriptionTerm: 12,
addOns: [
{
productOptionId: "opt-premium-support",
subscriptionStartDate: "2025-02-01",
subscriptionTerm: 12
}
]
}
],
options: {
previewInvoice: true,
calculateTax: true
}
};
orderService.createDraftOrder(newOrder)
.then(result => {
if (result.success) {
console.log('\n🎉 Draft order created successfully!');
console.log(`Order Number: ${result.summary.orderNumber}`);
console.log(`Total Assets: ${result.summary.totalAssets}`);
console.log(`Grand Total: $${result.summary.grandTotal}`);
if (result.summary.hasWarnings) {
console.log('\n⚠️ Warnings:');
result.warnings.forEach(warning => {
console.log(`- ${warning.message}`);
});
}
} else {
console.error('❌ Order creation failed:', result.error);
}
});Request Body Schema
Required Fields
Field | Type | Description | Example |
|---|---|---|---|
customer.id | String | Customer identifier (UUID) | "d2e04653-ae90-49df-a986-134cf64f6d03" |
effectiveDate | String | Order effective date (ISO 8601) | "2025-01-01" |
orderProducts | Array | List of products to order | See product schema below |
Order Product Schema
Field | Type | Required | Description |
|---|---|---|---|
priceBookEntryId | String | Yes | Price book entry identifier |
quantity | Number | Yes | Product quantity (must be > 0) |
subscriptionStartDate | String | Yes | Subscription start date (ISO 8601) |
subscriptionTerm | Number | * | Subscription term in months |
coTermAsset | String | * | Asset ID to co-terminate with |
addOns | Array | No | Bundle add-on configurations |
priceTagIds | Array | No | Price tag identifiers for discounts |
priceTagCodes | Array | No | Price tag codes for discounts |
*Either subscriptionTerm or coTermAsset is required
Bundle Add-On Schema
Field | Type | Required | Description |
|---|---|---|---|
productOptionId | String | Yes | Product option identifier. For dynamic options, use the composite ID from the catalog in the format {dynamicOptionId}_{priceBookEntryId} |
productOptionQuantity | Number | * | Quantity (required for editable options) |
subscriptionStartDate | String | No | Override start date |
subscriptionTerm | Number | No | Override subscription term |
addOns | Array | No | Nested add-ons (up to 3 levels) |
Response Structure
Success Response (201 Created)
{
"status": "SUCCESS",
"data": {
"order": {
"id": "363ed8be-86b1-41b9-980d-f8dbe682c2df",
"orderNumber": "O-00005025",
"customerId": "001HE00000KY3w7YAD",
"billingAccountId": "001HE00000KY3w7YAD",
"status": "Draft",
"orderStartDate": "2026-09-01",
"subscriptionStartDate": "2026-09-01",
"subscriptionEndDate": "2027-08-31",
"subscriptionTerm": 12,
"subscriptionTermDimension": "Month",
"billingPeriod": "Month",
"priceBookId": "01sHE000000gRYTYA2",
"listTotal": 240,
"discount": 0,
"discountAmount": 0,
"systemDiscount": 10,
"systemDiscountAmount": 24,
"subtotal": 216,
"totalPrice": 216,
"tax": 0,
"totalAmountWithoutTax": 216,
"totalAmount": 216,
"orderACV": 216,
"orderTCV": 216
},
"orderProducts": [
{
"id": "802HE000006QJuvYAG",
"orderId": "363ed8be-86b1-41b9-980d-f8dbe682c2df",
"productId": "01tHE000008ST7WYAW",
"priceBookEntryId": "01uHE0000013Bj7YAE",
"sku": "NUE_PLATFORM",
"productName": "Nue Platform",
"quantity": 10,
"subscriptionStartDate": "2026-09-01",
"subscriptionEndDate": "2027-08-31",
"subscriptionTerm": 12,
"listPrice": 2,
"listTotalPrice": 240,
"systemDiscount": 10,
"systemDiscountAmount": 24,
"subtotal": 216,
"totalPrice": 216
}
],
"previewInvoices": [
{
"accountId": "001HE00000KY3w7YAD",
"status": "Draft",
"amount": 19,
"balance": 19,
"amountWithoutTax": 19,
"taxAmount": 0,
"taxStatus": "NotCalculated",
"invoiceDate": "2026-07-25",
"dueDate": "2026-08-24",
"startDate": "2026-09-01",
"endDate": "2026-09-30",
"items": []
}
],
"subscriptions": [],
"assets": [],
"entitlements": []
},
"warnings": []
}subscriptions, assets and entitlements are present but empty on a draft order -- they are provisioned when the order is activated. Preview invoice entries carry no id, because nothing has been persisted yet.
Error Handling
Common Error Scenarios
Error Code | Description | Resolution |
|---|---|---|
INVALID_ADDON_HIERARCHY | Invalid add-on configuration | Check product option availability |
MISSING_ADDON_QUANTITY | Required add-on quantity missing | Provide quantity for editable options |
INVALID_ADDON_START_DATE | Add-on start date invalid | Ensure start date is before parent end date |
SUBSCRIPTION_TERM_NOT_ALLOWED | Invalid subscription term | Check if term is allowed for product |
Bundle Configuration Errors
// Handle bundle-specific errors
try {
const result = await orderService.createDraftOrder(bundleOrder);
} catch (error) {
if (error.message.includes('INVALID_ADDON_HIERARCHY')) {
console.error('Bundle Error: Product option not available at this level');
// Guide user to correct bundle configuration
} else if (error.message.includes('INSUFFICIENT_FEATURE_OPTIONS')) {
console.error('Bundle Error: Minimum required options not met');
// Show minimum requirements for product group
} else if (error.message.includes('EXCESSIVE_FEATURE_OPTIONS')) {
console.error('Bundle Error: Too many options selected');
// Show maximum allowed options for product group
}
}Best Practices
Order Design
- Validate bundle configurations before submission
- Use co-termination for mid-term additions
- Apply appropriate pricing tags for discounts
- Set realistic start dates considering processing time
Performance
- Batch product lines efficiently in single orders
- Use preview invoices to validate pricing
- Implement proper error handling for validation failures
- Cache price book entries to reduce API calls
Business Logic
- Validate customer eligibility before order creation
- Check product availability and prerequisites
- Implement approval workflows for high-value orders
- Generate order references for tracking
This comprehensive guide enables you to efficiently create and manage draft orders using the Nue Lifecycle Management API, supporting everything from simple product orders to complex bundle configurations with multiple add-on levels.