Validation Errors
Validation Errors
The Nue CPQ Quote API returns structured error and warning responses when a request fails validation or contains conditions that merit attention. Understanding these codes helps you build robust integrations that handle edge cases gracefully.
Error Response Format
A failed request returns an HTTP error status and a body of this shape:
{
"status": 400,
"error": "Failed to create quote",
"message": "The field 'accountId' is required.",
"errorDetails": [
{
"errorCode": "QUOTE_ACCOUNTID_REQUIRED",
"message": "The field 'accountId' is required."
}
]
}Field | Type | Description |
|---|---|---|
status | integer | The numeric HTTP status code |
error | string | Short category. Failed to create quote for pricing and business-rule failures; Bad Request for basic request validation |
message | string | Human-readable description. For a single failure this matches the first entry in errorDetails |
errorDetails | array | One entry per failure, each with errorCode and message. Absent on Bad Request responses |
Basic request validation failures -- a missing name or priceBookId, or two mutually exclusive discount fields on one product -- return "error": "Bad Request" with no errorDetails array:
{
"status": 400,
"error": "Bad Request",
"message": "priceBookId is required."
}Successful requests return the quote and its line items directly, with no status field and no warnings array:
{
"quote": { ... },
"quoteLineItems": [ ... ]
}Detect success from the HTTP status rather than from a field in the body.
Authentication
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");Scenario 1: Missing Quote Name
The name field is required on every quote request.
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
const quoteData = {
opportunityId: "006xx000001abc123",
subscriptionStartDate: "2026-01-01",
subscriptionEndDate: "2027-01-01",
subscriptionTerm: 12,
products: [
{ productSku: "NUE_ON_SALESFORCE", uom: "User/Month", quantity: 10 }
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
console.log('Errors:', result.errorDetails ?? result.message);
// [{ code: "QUOTE_NAME_REQUIRED", message: "Quote name is required", field: "name" }]
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"subscriptionStartDate": "2026-01-01",
"subscriptionEndDate": "2027-01-01",
"subscriptionTerm": 12,
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 }
]
}'Error code: QUOTE_NAME_REQUIRED
Scenario 2: Missing Opportunity ID
The opportunityId field is required to associate the quote with a sales opportunity. Returns HTTP 400.
const quoteData = {
name: "Missing Opportunity Quote",
subscriptionStartDate: "2026-01-01",
subscriptionEndDate: "2027-01-01",
subscriptionTerm: 12,
products: [
{ productSku: "NUE_ON_SALESFORCE", uom: "User/Month", quantity: 10 }
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
console.log('Errors:', result.errorDetails ?? result.message);
// [{ code: "QUOTE_OPPORTUNITYID_REQUIRED", message: "Opportunity ID is required" }]
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"name": "Missing Opportunity Quote",
"subscriptionStartDate": "2026-01-01",
"subscriptionEndDate": "2027-01-01",
"subscriptionTerm": 12,
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 }
]
}'Error code: QUOTE_OPPORTUNITYID_REQUIRED
Scenario 3: Negative or Zero Subscription Term
The subscriptionTerm must be a positive number.
const quoteData = {
opportunityId: "006xx000001abc123",
name: "Negative Term Quote",
subscriptionStartDate: "2026-01-01",
subscriptionTerm: -6,
products: [
{ productSku: "NUE_ON_SALESFORCE", uom: "User/Month", quantity: 10 }
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
console.log('Errors:', result.errorDetails ?? result.message);
// [{ code: "QUOTE_SUBSCRIPTIONTERM_INVALID", message: "Subscription term must be a positive number" }]
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Negative Term Quote",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": -6,
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 }
]
}'Error code: QUOTE_SUBSCRIPTIONTERM_INVALID
Scenario 4: Empty or Null Products List
The products array must contain at least one product.
const quoteData = {
opportunityId: "006xx000001abc123",
name: "Empty Products Quote",
subscriptionStartDate: "2026-01-01",
subscriptionTerm: 12,
products: []
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
console.log('Errors:', result.errorDetails ?? result.message);
// [{ code: "MISSING_PARAMETER", message: "At least one product is required" }]
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Empty Products Quote",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"products": []
}'Error code: MISSING_PARAMETER
Scenario 5: Nonexistent SKU
If the provided SKU does not match any product in the catalog, the API returns an error.
const quoteData = {
opportunityId: "006xx000001abc123",
name: "Bad SKU Quote",
subscriptionStartDate: "2026-01-01",
subscriptionTerm: 12,
products: [
{ productSku: "NONEXISTENT_SKU", uom: "User/Month", quantity: 10 }
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
console.log('Errors:', result.errorDetails ?? result.message);
// [{ code: "INVALID_INPUT", message: "Product not found with SKU: NONEXISTENT_SKU" }]
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Bad SKU Quote",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"products": [
{ "productSku": "NONEXISTENT_SKU", "uom": "User/Month", "quantity": 10 }
]
}'Error code: INVALID_INPUT
Scenario 6: Wrong UOM
Each product has a defined set of valid Units of Measure. Using an invalid UOM triggers an error.
curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Wrong UOM Quote",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "GB/Month", "quantity": 10 }
]
}'Error code: PRICEBOOK_ENTRY_MISMATCH -- No Price Book Entry found for product NUE_ON_SALESFORCE with UOM GB/Month.
Scenario 7: Mix of Valid and Invalid Products (Partial Failure)
When one product in the products array is invalid, the entire quote request fails. There is no partial success.
const quoteData = {
opportunityId: "006xx000001abc123",
name: "Mixed Valid/Invalid Products",
subscriptionStartDate: "2026-01-01",
subscriptionTerm: 12,
products: [
{ productSku: "NUE_ON_SALESFORCE", uom: "User/Month", quantity: 10 },
{ productSku: "NONEXISTENT_SKU", uom: "User/Month", quantity: 5 }
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(async response => {
const result = await response.json();
// The entire request fails -- even the valid product is not priced
console.log(response.status); // 400
console.log(result.errorDetails ?? result.message);
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Mixed Valid/Invalid Products",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 },
{ "productSku": "NONEXISTENT_SKU", "uom": "User/Month", "quantity": 5 }
]
}'Error code: INVALID_INPUT -- The error identifies the invalid product. Fix all product issues and resubmit the entire request.
Scenario 8: Invalid Billing Period
The billingPeriod field only accepts specific values: Monthly, Quarterly, Semi-Annually, Annually.
curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Invalid Billing Period",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"billingPeriod": "Weekly",
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 }
]
}'Error code: PRODUCT_BILLINGPERIOD_INVALID -- Valid values are: Monthly, Quarterly, Semi-Annually, Annually.
Scenario 9: Invalid Billing Timing
The billingTiming field only accepts "In Advance" or "In Arrears".
curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Invalid Billing Timing",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"billingTiming": "On Delivery",
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 }
]
}'Error code: PRODUCT_BILLINGTIMING_INVALID -- Valid values are: In Advance, In Arrears.
Scenario 10: Invalid Add-On Product
If a product specified as an add-on is not a valid option for the parent bundle, the API returns an error.
const quoteData = {
opportunityId: "006xx000001abc123",
name: "Invalid Add-On",
subscriptionStartDate: "2026-01-01",
subscriptionTerm: 12,
products: [
{
productSku: "ENTERPRISE_BUNDLE",
uom: "License/Month",
quantity: 10,
addOns: [
{ productSku: "UNRELATED_PRODUCT", uom: "Unit/Month", quantity: 1 }
]
}
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
console.log('Errors:', result.errorDetails ?? result.message);
// [{ code: "INVALID_ADDON_PRODUCT", message: "Product UNRELATED_PRODUCT is not a valid option for bundle ENTERPRISE_BUNDLE" }]
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Invalid Add-On",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"products": [
{
"productSku": "ENTERPRISE_BUNDLE",
"uom": "License/Month",
"quantity": 10,
"addOns": [
{ "productSku": "UNRELATED_PRODUCT", "uom": "Unit/Month", "quantity": 1 }
]
}
]
}'Error code: INVALID_ADDON_PRODUCT
Scenario 11: Both Discount Percentage and Amount on Same Product
discount, discountAmount and netSalesPrice are mutually exclusive on a product entry. Supplying more than one fails the request with 400; the percentage does not silently win.
const quoteData = {
opportunityId: "006xx000001abc123",
name: "Dual Discount Product",
subscriptionStartDate: "2026-01-01",
subscriptionTerm: 12,
products: [
{
productSku: "NUE_ON_SALESFORCE",
uom: "User/Month",
quantity: 10,
discount: 10,
discountAmount: 500
}
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(async response => {
const result = await response.json();
console.log(response.status); // 400
console.log(result.message);
// "Product at index 0 validation failed: : Only one of [discount, discountAmount, netSalesPrice] should be set"
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Dual Discount Product",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"products": [
{
"productSku": "NUE_ON_SALESFORCE",
"uom": "User/Month",
"quantity": 10,
"discount": 10,
"discountAmount": 500
}
]
}'Response:
{
"status": 400,
"error": "Bad Request",
"message": "Product at index 0 validation failed: : Only one of [discount, discountAmount, netSalesPrice] should be set"
}Send exactly one of the three on any given product entry. Note that a header-level discount and discountAmount on the quote root are both accepted -- there the percentage wins and the amount is recalculated from it.
Scenario 12: Currency Mismatch
When currencyIsoCode is set but a product does not have a PBE in that currency, the request fails.
curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Currency Mismatch",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"currencyIsoCode": "GBP",
"products": [
{ "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 }
]
}'Error code: PRICEBOOK_ENTRY_MISMATCH -- No PBE found for product NUE_ON_SALESFORCE with currency GBP.
Scenario 13: Duplicate Price Tag
The same tag listed more than once on a product is not deduplicated and no warning is returned. Each entry is evaluated, so a repeated discount tag compounds. Send each tag once.
const quoteData = {
opportunityId: "006xx000001abc123",
name: "Duplicate Price Tag",
subscriptionStartDate: "2026-01-01",
subscriptionTerm: 12,
products: [
{
productSku: "NUE_ON_SALESFORCE",
uom: "User/Month",
quantity: 10,
priceTags: [
{ code: "VOLUME_DISCOUNT" },
{ code: "VOLUME_DISCOUNT" }
]
}
]
};
fetch('https://api.nue.io/cpq/quotes:preview', {
method: 'POST',
headers: myHeaders,
body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
const line = result.quoteLineItems[0];
// Both entries are evaluated and the discount compounds.
// A 20% tag listed twice on a $12,000 line yields 36% (1 - 0.8 x 0.8), not 20%.
console.log(line.priceTags.length); // 2 -- both echoed, not deduplicated
console.log(line.systemDiscount); // 36
console.log(line.systemDiscountAmount); // 4320
})
.catch(error => console.log('Error:', error));curl -X POST 'https://api.nue.io/cpq/quotes:preview' \
-H 'nue-api-key: YOUR_API_KEY_HERE' \
-H 'Content-Type: application/json' \
-d '{
"opportunityId": "006xx000001abc123",
"name": "Duplicate Price Tag",
"subscriptionStartDate": "2026-01-01",
"subscriptionTerm": 12,
"products": [
{
"productSku": "NUE_ON_SALESFORCE",
"uom": "User/Month",
"quantity": 10,
"priceTags": [
{ "code": "VOLUME_DISCOUNT" },
{ "code": "VOLUME_DISCOUNT" }
]
}
]
}'Warning code: DUPLICATE_PRICE_TAG -- The tag is applied only once. Always ensure each price tag is specified only once per product.
Error Code Reference
Error Code | HTTP Status | Description |
|---|---|---|
MISSING_PARAMETER | 400 | A required parameter is missing from the request |
QUOTE_NAME_REQUIRED | 400 | The name field is missing or empty |
QUOTE_OPPORTUNITYID_REQUIRED | 400 | The opportunityId field is missing or empty |
QUOTE_SUBSCRIPTIONTERM_INVALID | 400 | The subscriptionTerm is zero, negative, or not a number |
PRODUCT_SKU_OR_NAME_REQUIRED | 400 | A product entry has neither productSku nor productName |
PRODUCT_QUANTITY_INVALID | 400 | The quantity is zero, negative, or not a number |
PRODUCT_BILLINGPERIOD_INVALID | 400 | The billingPeriod value is not one of the valid options |
PRODUCT_BILLINGTIMING_INVALID | 400 | The billingTiming value is not one of the valid options |
PRODUCT_RENEWALTERM_INVALID | 400 | The renewalTerm is zero, negative, or not a number |
PRODUCT_SUBSCRIPTIONTERM_INVALID | 400 | A per-product subscriptionTerm override is invalid |
INVALID_INPUT | 400 | General invalid input (e.g., nonexistent SKU, malformed request) |
INVALID_ADDON_PRODUCT | 400 | The specified add-on is not a valid option for the parent bundle |
PRICEBOOK_ENTRY_MISMATCH | 400 | No PBE found for the given SKU + UOM + currency + pricing attributes |
BUSINESS_LOGIC_ERROR | 400 | A business rule was violated (e.g., conflicting configurations) |
Warnings
The quote endpoints do not return a warnings array -- the response body contains only quote and quoteLineItems. Conditions that the pricing engine treats as non-fatal are therefore not surfaced to API callers, so you cannot detect them from the response. Where a condition matters enough to act on, it is returned as a 400 error instead (see the scenarios above).
Inspect the returned line items to confirm what the engine actually applied -- for example read discount, discountAmount, netSalesPrice and priceTags on each line rather than relying on a warning to tell you.
Best Practices for Error Handling
- Check the HTTP status -- there is no success/failure field in the response body
- Inspect errorDetails -- each entry contains an errorCode and a message; fall back to the top-level message when errorDetails is absent
- Verify what was applied -- read the pricing fields on the returned line items, since non-fatal conditions are not reported
- Log error codes -- Use the machine-readable code for programmatic error handling, not the message
- Use preview mode first -- Validate quote configurations in preview before committing
- Handle all products -- A single invalid product fails the entire request; validate all products before submitting
- Handle 429 responses -- Implement exponential backoff retry logic for rate-limited requests