---
title: Custom Fields
slug: custom-fields
createdAt: 2026-03-17T21:13:54.077Z
---

# Custom Fields

The Nue CPQ Quote API supports custom fields on **quote headers** and on **individual line items**. Custom field names in the request body map directly to fields on the Quote and QuoteLineItem objects. This allows you to set business-specific metadata -- such as Salesforce custom fields, department codes, regions, cost centers, or approval flags -- directly when creating, previewing, or updating a quote.

Everything on this page applies identically to orders: swap `/cpq/quotes` for `/cpq/orders`, `quote` for `order`, and `QuoteLineItem` for `OrderProduct`.

## How Custom Fields Work

When the API receives a Create Quote request, it processes all standard fields (like `name`, `opportunityId`, `products`) through the pricing engine. Any additional top-level fields in the request body are treated as **header** custom fields and stored on the quote header. Inside the `products` array, a `customFields` object sets **line-level** custom fields on the line that product creates.

### Key Behaviors

| Behavior                           | Details                                                                                                  |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
| **Flat JSON mapping**              | Header custom field names in the request map directly to Quote object fields                             |
| **Line-level&#x20;**`customFields` | A `customFields` object on any product (and recursively on `addOns`) writes straight onto that line item |
| **Pricing engine passthrough**     | Custom fields survive the pricing engine -- they are not overwritten during pricing calculations         |
| **Preview and commit**             | Custom fields appear in both preview and commit responses                                                |
| **Supported types**                | String, number, boolean, and date values are all supported                                               |
| **Updating existing records**      | Header and line custom fields are both patchable through the Update Quote API (see Use Case 5)           |

## Authentication

```javascript
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
```

## Use Case 1: Custom Fields on Quote Header (Committed)

Set custom fields as top-level properties in the request body when creating a committed quote. The field names must match the API names of the custom fields on your Quote object (e.g., Salesforce custom field API names like `Ruby__CustomText__c`).

```javascript
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");

const quoteData = {
  opportunityId: "006xx000001abc123",
  name: "Enterprise License -- Custom Fields",
  subscriptionStartDate: "2026-01-01",
  subscriptionEndDate: "2027-01-01",
  subscriptionTermDimension: "Month",
  subscriptionTerm: 12,
  "Ruby__CustomText__c": "Hello",
  "Ruby__CustomNumber__c": 42.50,
  "Ruby__CustomDate__c": "2026-06-15",
  "Ruby__CustomCheckbox__c": true,
  products: [
    { productSku: "NUE_ON_SALESFORCE", uom: "User/Month", quantity: 25 }
  ]
};

fetch('https://api.nue.io/cpq/quotes', {
  method: 'POST',
  headers: myHeaders,
  body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
  console.log(`Quote ID: ${result.quote.id}`);
  console.log(`Custom Text: ${result.quote['Ruby__CustomText__c']}`);      // "Hello"
  console.log(`Custom Number: ${result.quote['Ruby__CustomNumber__c']}`);  // 42.50
  console.log(`Custom Date: ${result.quote['Ruby__CustomDate__c']}`);      // "2026-06-15"
  console.log(`Custom Checkbox: ${result.quote['Ruby__CustomCheckbox__c']}`);  // true
  console.log(`Total Amount: $${result.quote.totalAmount}`);
})
.catch(error => console.log('Error:', error));
```

```bash
curl -X POST 'https://api.nue.io/cpq/quotes' \
  -H 'nue-api-key: YOUR_API_KEY_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "opportunityId": "006xx000001abc123",
    "name": "Enterprise License -- Custom Fields",
    "subscriptionStartDate": "2026-01-01",
    "subscriptionEndDate": "2027-01-01",
    "subscriptionTermDimension": "Month",
    "subscriptionTerm": 12,
    "Ruby__CustomText__c": "Hello",
    "Ruby__CustomNumber__c": 42.50,
    "Ruby__CustomDate__c": "2026-06-15",
    "Ruby__CustomCheckbox__c": true,
    "products": [
      { "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 25 }
    ]
  }'
```

The response includes all custom fields on the `quote` object alongside the standard pricing fields. The pricing engine calculates `totalAmount`, `listTotalPrice`, etc., without affecting the custom field values.

***

## Use Case 2: Custom Fields on Quote Header (Preview)

Custom fields work identically in preview mode. They appear in the response even though no data is persisted.

```javascript
const quoteData = {
  opportunityId: "006xx000001abc123",
  name: "Preview with Custom Fields",
  subscriptionStartDate: "2026-01-01",
  subscriptionEndDate: "2027-01-01",
  subscriptionTermDimension: "Month",
  subscriptionTerm: 12,
  "Ruby__CustomText__c": "Preview Value",
  "Ruby__CustomNumber__c": 99.99,
  "Ruby__CustomDate__c": "2026-12-31",
  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(`quote.id: ${result.quote.id}`);                                // undefined -- preview mode
  console.log(`Custom Text: ${result.quote['Ruby__CustomText__c']}`);          // "Preview Value"
  console.log(`Custom Number: ${result.quote['Ruby__CustomNumber__c']}`);      // 99.99
  console.log(`Custom Date: ${result.quote['Ruby__CustomDate__c']}`);          // "2026-12-31"
  console.log(`Total Amount: $${result.quote.totalAmount}`);
})
.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": "Preview with Custom Fields",
    "subscriptionStartDate": "2026-01-01",
    "subscriptionEndDate": "2027-01-01",
    "subscriptionTermDimension": "Month",
    "subscriptionTerm": 12,
    "Ruby__CustomText__c": "Preview Value",
    "Ruby__CustomNumber__c": 99.99,
    "Ruby__CustomDate__c": "2026-12-31",
    "products": [
      { "productSku": "NUE_ON_SALESFORCE", "uom": "User/Month", "quantity": 10 }
    ]
  }'
```

The custom fields survive the pricing engine and appear in the preview response. Since this is preview mode, the `quote` object has no `id` and nothing is persisted.

***

## Use Case 3: Custom Fields with Bundle Products

When the pricing engine processes bundles, it calculates list prices, discounts, and totals for parent and child line items. Custom fields on the quote header are not affected by these calculations -- they pass through untouched.

```javascript
const quoteData = {
  opportunityId: "006xx000001abc123",
  name: "Bundle Quote with Custom Fields",
  subscriptionStartDate: "2026-01-01",
  subscriptionEndDate: "2027-01-01",
  subscriptionTermDimension: "Month",
  subscriptionTerm: 12,
  "Ruby__CustomText__c": "Bundle Deal",
  "Ruby__CustomNumber__c": 1500.00,
  "Ruby__CustomDate__c": "2026-03-01",
  discount: 10,
  products: [
    {
      productSku: "ENTERPRISE_BUNDLE",
      uom: "License/Month",
      quantity: 50,
      addOns: [
        { productSku: "ADDON_SUPPORT", uom: "License/Month", quantity: 1 },
        { productSku: "ADDON_STORAGE", uom: "GB/Month", quantity: 200 }
      ]
    }
  ]
};

fetch('https://api.nue.io/cpq/quotes:preview', {
  method: 'POST',
  headers: myHeaders,
  body: JSON.stringify(quoteData)
})
.then(response => response.json())
.then(result => {
  // Custom fields preserved after bundle pricing
  console.log(`Custom Text: ${result.quote['Ruby__CustomText__c']}`);      // "Bundle Deal"
  console.log(`Custom Number: ${result.quote['Ruby__CustomNumber__c']}`);  // 1500.00
  console.log(`Custom Date: ${result.quote['Ruby__CustomDate__c']}`);      // "2026-03-01"
  // Pricing fields calculated normally
  console.log(`Total Amount: $${result.quote.totalAmount}`);
  console.log(`Discount: ${result.quote.discount}%`);
})
.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": "Bundle Quote with Custom Fields",
    "subscriptionStartDate": "2026-01-01",
    "subscriptionEndDate": "2027-01-01",
    "subscriptionTermDimension": "Month",
    "subscriptionTerm": 12,
    "Ruby__CustomText__c": "Bundle Deal",
    "Ruby__CustomNumber__c": 1500.00,
    "Ruby__CustomDate__c": "2026-03-01",
    "discount": 10,
    "products": [
      {
        "productSku": "ENTERPRISE_BUNDLE",
        "uom": "License/Month",
        "quantity": 50,
        "addOns": [
          { "productSku": "ADDON_SUPPORT", "uom": "License/Month", "quantity": 1 },
          { "productSku": "ADDON_STORAGE", "uom": "GB/Month", "quantity": 200 }
        ]
      }
    ]
  }'
```

The pricing engine calculates bundle totals, applies the 10% header discount to all eligible lines, and returns the custom fields unchanged on the quote header.

***

## Use Case 4: Line Item Custom Fields

Custom fields on individual line items are supported **directly in the REST request**, on the same footing as the Apex global methods. Add a `customFields` object to any entry in the `products` array. Keys are the API names of custom fields on the line object (`QuoteLineItem` for quotes, `OrderProduct` for orders); values are written straight onto the line the product creates.

`customFields` works recursively -- an entry inside `addOns` carries its own `customFields` onto that add-on's line.

:::hint{type="info"}
**This closes the two-step workaround.** Earlier releases had no line-level slot in the REST request body, so integrations had to create the quote, read back the line item ids, and then patch each line through the Objects API or GraphQL. That second round trip is no longer needed. Line-level `customFields` is accepted on `POST /cpq/quotes`, `POST /cpq/quotes:preview`, `POST /cpq/orders`, `POST /cpq/orders:preview`, and the `addLineItems` action of the `:update` endpoints.
:::

```javascript
const quoteData = {
  opportunityId: "006xx000001abc123",
  name: "Quote with Line Item Custom Fields",
  subscriptionStartDate: "2026-01-01",
  subscriptionEndDate: "2027-01-01",
  subscriptionTermDimension: "Month",
  subscriptionTerm: 12,
  "Ruby__CustomText__c": "Header value",          // header custom field
  products: [
    {
      productSku: "NUE_ON_SALESFORCE",
      uom: "User/Month",
      quantity: 10,
      customFields: {                              // line-level custom fields
        "CostCenter__c": "CC-1042",
        "ContractRef__c": "MSA-2026-0417"
      }
    },
    {
      productSku: "ENTERPRISE_BUNDLE",
      uom: "License/Month",
      quantity: 50,
      customFields: { "CostCenter__c": "CC-2000" },
      addOns: [
        {
          productSku: "ADDON_SUPPORT",
          uom: "License/Month",
          quantity: 1,
          customFields: { "SupportTier__c": "Platinum" }
        }
      ]
    }
  ]
};

const res = await fetch('https://api.nue.io/cpq/quotes', {
  method: 'POST',
  headers: myHeaders,
  body: JSON.stringify(quoteData)
});
const result = await res.json();

console.log(result.quote['Ruby__CustomText__c']);                    // "Header value"
console.log(result.quoteLineItems[0]['CostCenter__c']);        // "CC-1042"
```

```bash
curl -X POST 'https://api.nue.io/cpq/quotes' \
  -H 'nue-api-key: YOUR_API_KEY_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "opportunityId": "006xx000001abc123",
    "name": "Quote with Line Item Custom Fields",
    "subscriptionStartDate": "2026-01-01",
    "subscriptionEndDate": "2027-01-01",
    "subscriptionTermDimension": "Month",
    "subscriptionTerm": 12,
    "products": [
      {
        "productSku": "NUE_ON_SALESFORCE",
        "uom": "User/Month",
        "quantity": 10,
        "customFields": {
          "CostCenter__c": "CC-1042",
          "ContractRef__c": "MSA-2026-0417"
        }
      },
      {
        "productSku": "NUE_PLATFORM",
        "uom": "User/Year",
        "quantity": 5,
        "customFields": { "CostCenter__c": "CC-2000" }
      }
    ]
  }'
```

A field name that does not exist on the line object is rejected -- it is not silently dropped. Line-level custom fields survive the pricing engine exactly as header custom fields do, and they appear in both preview and commit responses.

***

## Use Case 5: Custom Fields on an Existing Quote

The [Update Quote API](docId\:J9W7UfHJvphiEJ93BX5y9) patches custom fields on a quote that already exists -- on the header, on existing lines, and on lines you add in the same call.

Note the naming difference between the two slots:

| Slot                                                     | Field naming                                                                                                                  |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `updateHeaderFields` / `updateLineItems` → `fieldValues` | Nue field names for standard fields (`quantity`, `paymentTerm`); **Salesforce API names for custom fields** (`CostCenter__c`) |
| `addLineItems` → `products[].customFields`               | Salesforce API names on the line object                                                                                       |

```bash
curl -X POST 'https://api.nue.io/cpq/quotes/0Q0cU000001AbcXUAV:update' \
  -H 'nue-api-key: YOUR_API_KEY_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "isCommit": true,
    "recalc": "AUTO",
    "actions": [
      {
        "action": "updateHeaderFields",
        "payload": {
          "fieldValues": { "Region__c": "EMEA" }
        }
      },
      {
        "action": "updateLineItems",
        "payload": {
          "lineItems": [
            {
              "lineItemId": "0QLcU00000AbcDeFGH",
              "fieldValues": { "CostCenter__c": "CC-1042" }
            }
          ]
        }
      },
      {
        "action": "addLineItems",
        "payload": {
          "products": [
            {
              "productSku": "NUE_PLATFORM",
              "uom": "User/Month",
              "quantity": 5,
              "customFields": { "CostCenter__c": "CC-2000" }
            }
          ]
        }
      }
    ]
  }'
```

Setting a plain custom field does not re-price the line -- it is persisted as-is. A custom field mapped as a **line-direct Quantity Tier Attribute** is the exception: changing it can move the resolved pricing tier, so it triggers a re-price. See [Quantity Tier Attributes](docId\:pLcj0tpEnt2LihN0h6QGj).

## Custom Field Summary

| Behavior                                    | Supported                  | Notes                                                                          |
| ------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------ |
| Quote header custom fields (commit)         | Yes                        | Include as top-level properties in the request body                            |
| Quote header custom fields (preview)        | Yes                        | Fields appear in the response, not persisted                                   |
| Line item custom fields on create           | Yes                        | `customFields` object on any entry in `products`, recursively through `addOns` |
| Line item custom fields on add (update API) | Yes                        | `customFields` on `addLineItems` → `products[]`                                |
| Line item custom fields on an existing line | Yes                        | `updateLineItems` → `fieldValues`, keyed by Salesforce API name                |
| Header custom fields on an existing quote   | Yes                        | `updateHeaderFields` → `fieldValues`                                           |
| Preserved during bundle pricing             | Yes                        | Pricing engine does not overwrite custom fields                                |
| Preserved during discount calculation       | Yes                        | Custom fields are independent of discount logic                                |
| Triggers a re-price                         | Only for QTA-mapped fields | A plain custom field is persisted without re-pricing                           |

## Field Naming Conventions

| Convention               | Example               | Notes                                                             |
| ------------------------ | --------------------- | ----------------------------------------------------------------- |
| Salesforce custom fields | `Ruby__CustomText__c` | Use the full API name including namespace prefix and `__c` suffix |
| Standard custom fields   | `customField1`        | Use camelCase for non-Salesforce custom fields                    |
| Date format              | `"2026-06-15"`        | Use ISO 8601 date format (YYYY-MM-DD)                             |
| Number format            | `42.50`               | Use standard JSON number format                                   |
| Boolean format           | `true` / `false`      | Use JSON boolean values                                           |

## Important Notes

- Custom field names must not conflict with standard field names (e.g., avoid using `name`, `discount`, `products`, `opportunityId`)
- If a custom field name matches a standard field, the standard field behavior takes precedence
- Custom fields that do not exist on the Quote object in your org are silently ignored during commit but still appear in preview responses
