Custom Fields
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 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
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).
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));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.
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));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.
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));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.
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.
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"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 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 |
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.
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