---
title: Validation Errors
slug: validation-errors
createdAt: 2026-03-17T21:13:54.077Z
---

# 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:

```json
{
  "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:

```json
{
  "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:

```json
{
  "quote": { ... },
  "quoteLineItems": [ ... ]
}
```

Detect success from the HTTP status rather than from a field in the body.

## Authentication

```javascript
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.

```javascript
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));
```

```bash
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.

```javascript
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));
```

```bash
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.

```javascript
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));
```

```bash
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.

```javascript
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));
```

```bash
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.

```javascript
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));
```

```bash
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.

```bash
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.

```javascript
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));
```

```bash
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`.

```bash
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"`.

```bash
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.

```javascript
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));
```

```bash
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.

```javascript
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));
```

```bash
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:**

```json
{
  "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.

```bash
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.

```javascript
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));
```

```bash
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

1. **Check the HTTP status** -- there is no success/failure field in the response body
2. **Inspect&#x20;**`errorDetails` -- each entry contains an `errorCode` and a `message`; fall back to the top-level `message` when `errorDetails` is absent
3. **Verify what was applied** -- read the pricing fields on the returned line items, since non-fatal conditions are not reported
4. **Log error codes** -- Use the machine-readable `code` for programmatic error handling, not the `message`
5. **Use preview mode first** -- Validate quote configurations in preview before committing
6. **Handle all products** -- A single invalid product fails the entire request; validate all products before submitting
7. **Handle 429 responses** -- Implement exponential backoff retry logic for rate-limited requests
