# Search quotes

Operation ID: `searchQuotes`

Group: CRM › Quotes

[Open in the interactive reference](https://docs.api.lensing.ai/lensing/v1/endpoints/searchQuotes)

`POST /crm/quotes/search`

Read-only: A read that takes its filters in the body. Changes nothing, and ignores Idempotency-Key.

Searches quotes. Use this endpoint for criteria a query string cannot carry: text matches the quote memo, each multi-valued criterion lets you pass several values of one field, and quoteNumber and salesArrangementId are kept out of the URL. Criteria combine with AND, and a list within one criterion is an OR. For example, approvalStatus ['pendingApproval', 'approved'] matches either. A match comes back in full, with subscriptions[] and nonRecurringProducts[], the same as GET crm/quotes/:id. GET crm/quotes returns the header alone.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | string · v1 | yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version `v1`. |

## Request body

Required, `application/json`: searchQuotesInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `text` | string | no | Limits results to quotes whose memo contains this text. The match is case-insensitive. Omit to match every quote. Example: `"renegotiated"`. |
| `quoteNumber` | string | no | Limits results to the quote with this number, for example 'QT-1042'. The match is exact. |
| `quoteType` | array<any> | no | Limits results to quotes of any of these kinds. Possible values: 'new', 'upsell', 'renewal', 'changeOrder'. Omit or pass an empty array for every kind. Example: `["changeOrder"]`. |
| `status` | array<any> | no | Limits results to quotes in any of these business states. Possible values: 'created', 'underApproval', 'approved', 'underSigning', 'signed', 'completed'. Omit or pass an empty array for every state. |
| `approvalStatus` | array<any> | no | Limits results to quotes in any of these approval states. Possible values: 'pendingSubmission', 'pendingApproval', 'approved', 'rejected'. Omit or pass an empty array for every state. Example: `["pendingApproval","approved"]`. |
| `isPrimary` | boolean | no | Limits results to primary quotes when true, or non-primary quotes when false. Omit for both. |
| `opportunityId` | string | no | Limits results to quotes of this opportunity. Omit for every opportunity. |
| `salesArrangementId` | string | no | Limits results to quotes of this sales arrangement. Omit for every arrangement. |
| `quoteDate` | object | no | Limits results by the date the quote was drawn up. Both bounds are inclusive, and omitting one leaves that side open. |
| `quoteDate.start` | string | no | The earliest quote date to include, inclusive. Format YYYY-MM-DD. |
| `quoteDate.end` | string | no | The latest quote date to include, inclusive. Format YYYY-MM-DD. |
| `lastModifiedDate` | object | no | Limits results by when the quote last changed, in UTC. Both bounds are inclusive, and omitting one leaves that side open. |
| `lastModifiedDate.start` | string | no | The earliest change time to include, inclusive. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `lastModifiedDate.end` | string | no | The latest change time to include, inclusive. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `page` | object | no | The page to return. Omit for the first page at the default size. Example: `{"size":50}`. |
| `page.cursor` | string | no | The cursor from the previous response's pagination.nextCursor. Omit on the first request. |
| `page.size` | number | no | The number of quotes per page. Defaults to 50, and a value above 200 is treated as 200, not rejected. |

```json
{
  "text": "renegotiated",
  "quoteNumber": "<quoteNumber>",
  "quoteType": [
    "changeOrder"
  ],
  "status": [
    "created"
  ],
  "approvalStatus": [
    "pendingApproval",
    "approved"
  ],
  "isPrimary": true,
  "opportunityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "salesArrangementId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "quoteDate": {
    "start": "<start>",
    "end": "<end>"
  },
  "lastModifiedDate": {
    "start": "<start>",
    "end": "<end>"
  },
  "page": {
    "size": 50
  }
}
```

## Response

`200` Success

Under `data` (searchQuotesOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | array<object> | yes | The quotes on this page, most recently created first. An empty array when nothing matches. Example: `[{"id":"3b7d5e9c-1a24-4f80-9c6d-2e8f70b13a45","quoteNumber":"QT-1042","quoteType":"new","status":"approved","approvalStatus":"approved","isPrimary":true,"quoteDate":"2026-08-01","serviceStartDate":"2026-09-01","signDate":"2026-08-14","paymentTerm":"NET30","memo":"Two-year platform subscription, renegotiated after the pilot.","needsPSACalculation":false,"currency":"USD","totalPrice":"48000.00","oneTimeDiscountAmount":"2000.00","oneTimeDiscountAllocation":"firstInvoice","opportunityId":"a1c4d7e0-5b62-4c19-8e73-9f0d2a6b4c88","opportunityName":"Northwind — platform expansion","opportunityNumber":"OPP-2291","billToAddressId":"6f9a2b1c-33d4-4a55-9e21-0b7c8d4e1f02","soldToAddressId":"6f9a2b1c-33d4-4a55-9e21-0b7c8d4e1f02","lastModifiedDate":"2026-08-14T09:12:44.000Z","subscriptions":[{"id":"c2e1f4a7-8b90-4d33-9a15-6f7e8d2b0c41","termType":"termed","serviceStartDate":"2026-09-01","serviceEndDate":"2028-08-31","autoRenew":true,"renewalTerm":12,"quoteProducts":[{"id":"d4f2a1b8-3c67-4e05-8b92-1a7d0e6c5f38","quoteProductNumber":"QP-3","productId":"e5a3b2c9-4d78-4f16-9c03-2b8e1f7d6a49","productCode":"PLATFORM-ENT","productName":"Platform — Enterprise","isRampDeal":false,"orderIndex":1,"quoteProductLines":[{"id":"f6b4c3d0-5e89-4a27-8d14-3c9f2a8e7b50","quoteProductLineNumber":"QPL-7","productLineId":"a7c5d4e1-6f90-4b38-9e25-4d0a3b9f8c61","productLineName":"Enterprise seats","lineType":"recurring","quantity":"100.00","listPrice":"480.00","unitPrice":"400.00","discountPercentage":"0.166667","totalPrice":"40000.00","invoiceFrequency":"annually","priceModel":"tiered","pricingTerm":"annually","unitOfMeasure":"seat","serviceStartDate":"2026-09-01","serviceEndDate":"2028-08-31","orderIndex":1,"lineTiers":[{"id":"b8d6e5f2-7a01-4c49-8f36-5e1b4c0a9d72","fromQuantity":"1.00","toQuantity":"50.00","priceModel":"tiered","unitPrice":"440.00","listPrice":"480.00"},{"id":"c9e7f6a3-8b12-4d50-9a47-6f2c5d1b0e83","fromQuantity":"51.00","priceModel":"tiered","unitPrice":"400.00","listPrice":"480.00"}],"rateCardEntries":[],"invoiceMilestones":[]}]}]}],"nonRecurringProducts":[{"id":"d0f8a7b4-9c23-4e61-8b58-7a3d6e2c1f94","quoteProductNumber":"QP-4","productId":"e1a9b8c5-0d34-4f72-9c69-8b4e7f3d2a05","productCode":"ONBOARD","productName":"Onboarding","orderIndex":2,"quoteProductLines":[{"id":"f2b0c9d6-1e45-4a83-8d70-9c5f8a4e3b16","productLineId":"a3c1d0e7-2f56-4b94-9e81-0d6a9b5f4c27","productLineName":"Implementation","lineType":"oneTime","quantity":"1.00","listPrice":"10000.00","unitPrice":"10000.00","totalPrice":"10000.00","invoiceFrequency":"milestone","priceModel":"flat","unitOfMeasure":"each","lineTiers":[],"rateCardEntries":[],"invoiceMilestones":[{"id":"b4d2e1f8-3a67-4c05-9f92-1e7b0c6d5a38","description":"Kick-off","totalPrice":"5000.00","percentage":"50.0000","projectedDate":"2026-09-15"}]}]}]}]`. |
| `items[].id` | string | yes | The unique identifier for the quote. |
| `items[].quoteNumber` | string | no | The system-generated quote number, for example 'QT-1042'. Absent while none has been assigned. |
| `items[].quoteType` | string | yes | The kind of quote. Possible values: 'new', 'upsell', 'renewal', 'changeOrder'. |
| `items[].status` | string | no | Where the quote stands in its business process. Possible values: 'created', 'underApproval', 'approved', 'underSigning', 'signed', 'completed'. Absent on a quote that has not entered the process. |
| `items[].approvalStatus` | string | no | Where the quote stands in its approval process. Possible values: 'pendingSubmission', 'pendingApproval', 'approved', 'rejected'. Absent until the approval process has run. |
| `items[].isPrimary` | boolean | yes | Whether this is the primary quote of its opportunity. |
| `items[].quoteDate` | date | yes | The date the quote was drawn up. Format YYYY-MM-DD. Product prices are validated against the opportunity's price book as of this date. |
| `items[].serviceStartDate` | date | yes | The first date products or services on this quote are delivered. Format YYYY-MM-DD. No subscription on the quote starts before it. |
| `items[].signDate` | date | no | The date the quote was signed by the customer and the legal entity. Format YYYY-MM-DD. Absent when not recorded. |
| `items[].paymentTerm` | string | yes | The code of the payment term applied to invoices created from this quote. See code on GET accounting/payment-terms. |
| `items[].memo` | string | no | A free-text memo recorded on the quote. Absent when none is set. |
| `items[].needsPSACalculation` | boolean | no | Whether the quote takes its product data from a PSA project rather than from the product catalog. |
| `items[].currency` | string | no | The currency of every amount on the quote. A quote takes the currency of its opportunity and has none of its own. Format ISO 4217, for example 'USD'. |
| `items[].totalPrice` | decimal | no | The sum of the quote's lines before tax, less any one-time discount, in the quote's currency. |
| `items[].oneTimeDiscountAmount` | decimal | no | A one-time discount applied to the whole quote, in the quote's currency. |
| `items[].oneTimeDiscountAllocation` | string | no | How oneTimeDiscountAmount is spread across the quote's invoices. Possible values: 'firstInvoice', 'alignToInvoiceFrequency', 'custom'. |
| `items[].billableTimeEstimate` | decimal | no | The estimated total of the quote's billable-time lines, in the quote's currency. |
| `items[].billableTimeCappedPrice` | decimal | no | The ceiling on the total of the quote's billable-time lines, in the quote's currency. |
| `items[].opportunityId` | string | yes | The opportunity this quote belongs to. See id on GET crm/opportunities/:id. |
| `items[].opportunityName` | string | no | The name of the opportunity this quote belongs to. Absent when the opportunity has no name. |
| `items[].opportunityNumber` | string | no | The opportunity number of the opportunity this quote belongs to, for example 'OP-1042'. Absent while none has been assigned. |
| `items[].billToAddressId` | string | no | The bill-to address on the quote, a copy the quote keeps of its own rather than the source it was copied from. There is no endpoint for it. |
| `items[].soldToAddressId` | string | no | The sold-to address on the quote, a copy the quote keeps of its own rather than the source it was copied from. There is no endpoint for it. |
| `items[].salesArrangementId` | string | no | The sales arrangement this quote is assigned to. Set on change-order quotes. There is no endpoint for it. |
| `items[].lastModifiedDate` | date-time | yes | The date and time the quote record last changed, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `items[].subscriptions` | array<object> | yes | The subscriptions on the quote, one per subscription, each with the products under it. An empty array when the quote has no recurring products. GET crm/quotes omits it and returns the quote header alone. |
| `items[].subscriptions[].id` | string | yes | The unique identifier for the quote subscription. |
| `items[].subscriptions[].termType` | string | yes | Whether the subscription runs to a fixed end date or renews indefinitely. Possible values: 'termed', 'evergreen'. |
| `items[].subscriptions[].serviceStartDate` | date | yes | The first date the subscription delivers service. Format YYYY-MM-DD. Never earlier than the quote's serviceStartDate. |
| `items[].subscriptions[].serviceEndDate` | date | no | The last date the subscription delivers service. Format YYYY-MM-DD. Set on a termed subscription and absent on an evergreen one. |
| `items[].subscriptions[].autoRenew` | boolean | no | Whether the subscription renews automatically at the end of its term. |
| `items[].subscriptions[].renewalTerm` | number | no | The renewal term in months. Meaningful only when autoRenew is true. |
| `items[].subscriptions[].quoteProducts` | array<object> | yes | The products in this subscription, each with its priced lines. |
| `items[].subscriptions[].quoteProducts[].id` | string | yes | The unique identifier for the quote product. |
| `items[].subscriptions[].quoteProducts[].quoteProductNumber` | string | no | The system-assigned number of the quote product. Absent while none has been assigned. |
| `items[].subscriptions[].quoteProducts[].productId` | string | no | The catalog product for this quote product. See id on GET accounting/products/:id. |
| `items[].subscriptions[].quoteProducts[].productCode` | string | no | The code of that product in the catalog. |
| `items[].subscriptions[].quoteProducts[].productName` | string | no | The name of that product in the catalog. |
| `items[].subscriptions[].quoteProducts[].isRampDeal` | boolean | no | Whether this product is priced as a ramp deal, with quantities or prices stepping over time. |
| `items[].subscriptions[].quoteProducts[].rampInterval` | string | no | How the ramp steps. Possible values: 'annually', 'specificDates'. Absent unless isRampDeal is true. |
| `items[].subscriptions[].quoteProducts[].rampInvoiceTiming` | string | no | How a ramp deal is invoiced. Possible values: 'allAtOnce', 'perInterval'. Absent unless isRampDeal is true. |
| `items[].subscriptions[].quoteProducts[].orderIndex` | number | no | The position of this product within the quote, for display ordering. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines` | array<object> | yes | The priced lines of this quote product. A product has at least one. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].id` | string | yes | The unique identifier for the quote product line. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].quoteProductLineNumber` | string | no | The system-assigned number of the quote product line. Absent while none has been assigned. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].productLineId` | string | no | The catalog product line this line was priced from. See id on GET accounting/product-lines/:id. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].productLineName` | string | no | The name of that catalog product line. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].parentQuoteProductLineId` | string | no | The parent line this line hangs off, as an allowance or overage line does. Absent on a top-level line. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineType` | string | yes | What this line charges for. Possible values: 'recurring', 'oneTime', 'commitWithOverage', 'payAsYouGo', 'usage', 'prepaid', 'billableTime', 'billableTravelExpense', 'billablePassThrough'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].quantity` | decimal | no | The quantity on this line, as a count, not an amount. For a ramp deal the per-period quantity is on each ramp instead. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].listPrice` | decimal | no | The per-unit list price before discount, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].unitPrice` | decimal | no | The per-unit price after discount, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].discountAmount` | decimal | no | The per-unit discount, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].discountPercentage` | decimal | no | The per-unit discount, as a decimal fraction of listPrice. For example, 0.1 is 10%. It is not an amount. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].extendedListPrice` | decimal | no | The list price extended across the quantity, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].cappedPrice` | decimal | no | The ceiling on what this line may total, in the quote's currency. Set on billable-time lines. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].totalPrice` | decimal | no | The total for this line, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].annualRecurringRevenue` | decimal | no | The annualized recurring revenue attributed to this line, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].invoiceFrequency` | string | no | How often this line is invoiced. Possible values: 'monthly', 'biMonthly', 'quarterly', 'semiAnnually', 'annually', 'once', 'custom', 'template', 'milestone', 'allAtOnce', 'termEnd'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].priceModel` | string | no | How the price is derived from quantity. Possible values: 'flat', 'perUnit', 'tiered', 'volume', 'stairstep', 'rateCard', 'thresholdBased'. For a tiered or volume model the bands are in lineTiers. For a rateCard model they are in rateCardEntries. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].pricingTerm` | string | no | The period the unit price is quoted for. Possible values: 'monthly', 'biMonthly', 'once', 'quarterly', 'semiAnnually', 'annually'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].unitOfMeasure` | string | no | The unit the quantity is counted in. Possible values: 'instance', 'gigabyte', 'terabyte', 'user', 'seat', 'minute', 'hour', 'day', 'month', 'each', 'license', 'activeUser', 'megabyte', 'message', 'text', 'platform', 'employee', 'personnel', 'credit', 'currency', 'package', 'case', 'word', 'record'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].overConsumptionBehavior` | string | no | What happens when consumption exceeds a prepaid balance. Possible values: 'allowNegative', 'overage'. Set on prepaid lines. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rolloverAtRenewal` | boolean | no | Whether an unused prepaid balance carries forward at renewal. Set on prepaid lines. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].serviceStartDate` | date | no | The first date this line delivers service. Format YYYY-MM-DD. Defaults to the quote's serviceStartDate. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].serviceEndDate` | date | no | The last date this line delivers service. Format YYYY-MM-DD. Absent while open-ended. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].orderIndex` | number | no | The position of this line within its quote product, for display ordering. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineTiers` | array<object> | yes | The tiered or volume pricing bands for this line, ordered by fromQuantity. An empty array unless priceModel is a banded model. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineTiers[].id` | string | yes | The unique identifier for the tier band. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineTiers[].fromQuantity` | decimal | yes | The lower bound of the band, inclusive. A quantity, not an amount. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineTiers[].toQuantity` | decimal | no | The upper bound of the band, inclusive. Absent on the open-ended top band. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineTiers[].priceModel` | string | yes | How this band prices. Possible values: 'flat', 'perUnit', 'tiered', 'volume', 'stairstep', 'rateCard', 'thresholdBased'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineTiers[].unitPrice` | decimal | no | The per-unit price for this band after discount, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].lineTiers[].listPrice` | decimal | no | The per-unit list price for this band before discount, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries` | array<object> | yes | The rate-card pricing for this line, one entry per billable component. An empty array unless priceModel is 'rateCard'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].id` | string | yes | The unique identifier for the rate-card entry. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].identifier` | string | yes | The identifier of the entry, unique within its line. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].description` | string | no | A label that distinguishes entries that otherwise have the same data. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].billableComponentId` | string | no | The billable component this entry prices. Absent when it names none. There is no endpoint for it. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].unitPrice` | decimal | no | The per-unit price after discount, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].listPrice` | decimal | no | The per-unit list price before discount, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].unitOfMeasure` | string | yes | The unit the price is quoted per. Possible values: 'instance', 'gigabyte', 'terabyte', 'user', 'seat', 'minute', 'hour', 'day', 'month', 'each', 'license', 'activeUser', 'megabyte', 'message', 'text', 'platform', 'employee', 'personnel', 'credit', 'currency', 'package', 'case', 'word', 'record'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].quantity` | decimal | no | The quantity of billable units on this entry, as a count, not an amount. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].usageType` | string | no | What the usage represents. Possible values: 'consumption', 'overage', 'thresholdCounter', 'payg'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].conversion` | decimal | no | The factor that converts usage units to billable units. For example, 0.1 when 1 gigabyte is 0.1 credits. It is a ratio, not an amount. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].rateCardEntries[].billableUnitOfMeasure` | string | no | The unit the entry is billed in, which may differ from unitOfMeasure. Possible values: 'instance', 'gigabyte', 'terabyte', 'user', 'seat', 'minute', 'hour', 'day', 'month', 'each', 'license', 'activeUser', 'megabyte', 'message', 'text', 'platform', 'employee', 'personnel', 'credit', 'currency', 'package', 'case', 'word', 'record'. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].invoiceMilestones` | array<object> | yes | The invoice milestones for this line, when it is invoiced against milestones rather than on a frequency. An empty array otherwise. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].invoiceMilestones[].id` | string | yes | The unique identifier for the invoice milestone. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].invoiceMilestones[].description` | string | no | What the milestone covers, as recorded on the quote. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].invoiceMilestones[].totalPrice` | decimal | no | The amount invoiced at this milestone, in the quote's currency. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].invoiceMilestones[].percentage` | decimal | no | The share of the line total this milestone invoices as a percentage. For example, 25 is 25%. It is not an amount. |
| `items[].subscriptions[].quoteProducts[].quoteProductLines[].invoiceMilestones[].projectedDate` | date | no | The date the milestone is expected to be reached. Format YYYY-MM-DD. |
| `items[].nonRecurringProducts` | array<object> | yes | The products on the quote that belong to no subscription, such as one-time charges, billable time and pass-through expenses. An empty array when every product is under a subscription. GET crm/quotes omits it and returns the quote header alone. |
| `items[].nonRecurringProducts[].id` | string | yes | The unique identifier for the quote product. |
| `items[].nonRecurringProducts[].quoteProductNumber` | string | no | The system-assigned number of the quote product. Absent while none has been assigned. |
| `items[].nonRecurringProducts[].productId` | string | no | The catalog product for this quote product. See id on GET accounting/products/:id. |
| `items[].nonRecurringProducts[].productCode` | string | no | The code of that product in the catalog. |
| `items[].nonRecurringProducts[].productName` | string | no | The name of that product in the catalog. |
| `items[].nonRecurringProducts[].isRampDeal` | boolean | no | Whether this product is priced as a ramp deal, with quantities or prices stepping over time. |
| `items[].nonRecurringProducts[].rampInterval` | string | no | How the ramp steps. Possible values: 'annually', 'specificDates'. Absent unless isRampDeal is true. |
| `items[].nonRecurringProducts[].rampInvoiceTiming` | string | no | How a ramp deal is invoiced. Possible values: 'allAtOnce', 'perInterval'. Absent unless isRampDeal is true. |
| `items[].nonRecurringProducts[].orderIndex` | number | no | The position of this product within the quote, for display ordering. |
| `items[].nonRecurringProducts[].quoteProductLines` | array<object> | yes | The priced lines of this quote product. A product has at least one. |
| `items[].nonRecurringProducts[].quoteProductLines[].id` | string | yes | The unique identifier for the quote product line. |
| `items[].nonRecurringProducts[].quoteProductLines[].quoteProductLineNumber` | string | no | The system-assigned number of the quote product line. Absent while none has been assigned. |
| `items[].nonRecurringProducts[].quoteProductLines[].productLineId` | string | no | The catalog product line this line was priced from. See id on GET accounting/product-lines/:id. |
| `items[].nonRecurringProducts[].quoteProductLines[].productLineName` | string | no | The name of that catalog product line. |
| `items[].nonRecurringProducts[].quoteProductLines[].parentQuoteProductLineId` | string | no | The parent line this line hangs off, as an allowance or overage line does. Absent on a top-level line. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineType` | string | yes | What this line charges for. Possible values: 'recurring', 'oneTime', 'commitWithOverage', 'payAsYouGo', 'usage', 'prepaid', 'billableTime', 'billableTravelExpense', 'billablePassThrough'. |
| `items[].nonRecurringProducts[].quoteProductLines[].quantity` | decimal | no | The quantity on this line, as a count, not an amount. For a ramp deal the per-period quantity is on each ramp instead. |
| `items[].nonRecurringProducts[].quoteProductLines[].listPrice` | decimal | no | The per-unit list price before discount, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].unitPrice` | decimal | no | The per-unit price after discount, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].discountAmount` | decimal | no | The per-unit discount, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].discountPercentage` | decimal | no | The per-unit discount, as a decimal fraction of listPrice. For example, 0.1 is 10%. It is not an amount. |
| `items[].nonRecurringProducts[].quoteProductLines[].extendedListPrice` | decimal | no | The list price extended across the quantity, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].cappedPrice` | decimal | no | The ceiling on what this line may total, in the quote's currency. Set on billable-time lines. |
| `items[].nonRecurringProducts[].quoteProductLines[].totalPrice` | decimal | no | The total for this line, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].annualRecurringRevenue` | decimal | no | The annualized recurring revenue attributed to this line, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].invoiceFrequency` | string | no | How often this line is invoiced. Possible values: 'monthly', 'biMonthly', 'quarterly', 'semiAnnually', 'annually', 'once', 'custom', 'template', 'milestone', 'allAtOnce', 'termEnd'. |
| `items[].nonRecurringProducts[].quoteProductLines[].priceModel` | string | no | How the price is derived from quantity. Possible values: 'flat', 'perUnit', 'tiered', 'volume', 'stairstep', 'rateCard', 'thresholdBased'. For a tiered or volume model the bands are in lineTiers. For a rateCard model they are in rateCardEntries. |
| `items[].nonRecurringProducts[].quoteProductLines[].pricingTerm` | string | no | The period the unit price is quoted for. Possible values: 'monthly', 'biMonthly', 'once', 'quarterly', 'semiAnnually', 'annually'. |
| `items[].nonRecurringProducts[].quoteProductLines[].unitOfMeasure` | string | no | The unit the quantity is counted in. Possible values: 'instance', 'gigabyte', 'terabyte', 'user', 'seat', 'minute', 'hour', 'day', 'month', 'each', 'license', 'activeUser', 'megabyte', 'message', 'text', 'platform', 'employee', 'personnel', 'credit', 'currency', 'package', 'case', 'word', 'record'. |
| `items[].nonRecurringProducts[].quoteProductLines[].overConsumptionBehavior` | string | no | What happens when consumption exceeds a prepaid balance. Possible values: 'allowNegative', 'overage'. Set on prepaid lines. |
| `items[].nonRecurringProducts[].quoteProductLines[].rolloverAtRenewal` | boolean | no | Whether an unused prepaid balance carries forward at renewal. Set on prepaid lines. |
| `items[].nonRecurringProducts[].quoteProductLines[].serviceStartDate` | date | no | The first date this line delivers service. Format YYYY-MM-DD. Defaults to the quote's serviceStartDate. |
| `items[].nonRecurringProducts[].quoteProductLines[].serviceEndDate` | date | no | The last date this line delivers service. Format YYYY-MM-DD. Absent while open-ended. |
| `items[].nonRecurringProducts[].quoteProductLines[].orderIndex` | number | no | The position of this line within its quote product, for display ordering. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineTiers` | array<object> | yes | The tiered or volume pricing bands for this line, ordered by fromQuantity. An empty array unless priceModel is a banded model. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineTiers[].id` | string | yes | The unique identifier for the tier band. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineTiers[].fromQuantity` | decimal | yes | The lower bound of the band, inclusive. A quantity, not an amount. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineTiers[].toQuantity` | decimal | no | The upper bound of the band, inclusive. Absent on the open-ended top band. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineTiers[].priceModel` | string | yes | How this band prices. Possible values: 'flat', 'perUnit', 'tiered', 'volume', 'stairstep', 'rateCard', 'thresholdBased'. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineTiers[].unitPrice` | decimal | no | The per-unit price for this band after discount, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].lineTiers[].listPrice` | decimal | no | The per-unit list price for this band before discount, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries` | array<object> | yes | The rate-card pricing for this line, one entry per billable component. An empty array unless priceModel is 'rateCard'. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].id` | string | yes | The unique identifier for the rate-card entry. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].identifier` | string | yes | The identifier of the entry, unique within its line. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].description` | string | no | A label that distinguishes entries that otherwise have the same data. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].billableComponentId` | string | no | The billable component this entry prices. Absent when it names none. There is no endpoint for it. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].unitPrice` | decimal | no | The per-unit price after discount, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].listPrice` | decimal | no | The per-unit list price before discount, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].unitOfMeasure` | string | yes | The unit the price is quoted per. Possible values: 'instance', 'gigabyte', 'terabyte', 'user', 'seat', 'minute', 'hour', 'day', 'month', 'each', 'license', 'activeUser', 'megabyte', 'message', 'text', 'platform', 'employee', 'personnel', 'credit', 'currency', 'package', 'case', 'word', 'record'. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].quantity` | decimal | no | The quantity of billable units on this entry, as a count, not an amount. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].usageType` | string | no | What the usage represents. Possible values: 'consumption', 'overage', 'thresholdCounter', 'payg'. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].conversion` | decimal | no | The factor that converts usage units to billable units. For example, 0.1 when 1 gigabyte is 0.1 credits. It is a ratio, not an amount. |
| `items[].nonRecurringProducts[].quoteProductLines[].rateCardEntries[].billableUnitOfMeasure` | string | no | The unit the entry is billed in, which may differ from unitOfMeasure. Possible values: 'instance', 'gigabyte', 'terabyte', 'user', 'seat', 'minute', 'hour', 'day', 'month', 'each', 'license', 'activeUser', 'megabyte', 'message', 'text', 'platform', 'employee', 'personnel', 'credit', 'currency', 'package', 'case', 'word', 'record'. |
| `items[].nonRecurringProducts[].quoteProductLines[].invoiceMilestones` | array<object> | yes | The invoice milestones for this line, when it is invoiced against milestones rather than on a frequency. An empty array otherwise. |
| `items[].nonRecurringProducts[].quoteProductLines[].invoiceMilestones[].id` | string | yes | The unique identifier for the invoice milestone. |
| `items[].nonRecurringProducts[].quoteProductLines[].invoiceMilestones[].description` | string | no | What the milestone covers, as recorded on the quote. |
| `items[].nonRecurringProducts[].quoteProductLines[].invoiceMilestones[].totalPrice` | decimal | no | The amount invoiced at this milestone, in the quote's currency. |
| `items[].nonRecurringProducts[].quoteProductLines[].invoiceMilestones[].percentage` | decimal | no | The share of the line total this milestone invoices as a percentage. For example, 25 is 25%. It is not an amount. |
| `items[].nonRecurringProducts[].quoteProductLines[].invoiceMilestones[].projectedDate` | date | no | The date the milestone is expected to be reached. Format YYYY-MM-DD. |
| `pagination` | object | yes | The pagination details for this page. Example: `{"hasMore":false}`. |
| `pagination.nextCursor` | string | no | The cursor for the next page. Pass it back as page.cursor. Absent on the last page. |
| `pagination.hasMore` | boolean | yes | Whether another page follows. A full page may still be the last, so branch on this. |

```json
{
  "data": {
    "items": [
      {
        "id": "3b7d5e9c-1a24-4f80-9c6d-2e8f70b13a45",
        "quoteNumber": "QT-1042",
        "quoteType": "new",
        "status": "approved",
        "approvalStatus": "approved",
        "isPrimary": true,
        "quoteDate": "2026-08-01",
        "serviceStartDate": "2026-09-01",
        "signDate": "2026-08-14",
        "paymentTerm": "NET30",
        "memo": "Two-year platform subscription, renegotiated after the pilot.",
        "needsPSACalculation": false,
        "currency": "USD",
        "totalPrice": "48000.00",
        "oneTimeDiscountAmount": "2000.00",
        "oneTimeDiscountAllocation": "firstInvoice",
        "opportunityId": "a1c4d7e0-5b62-4c19-8e73-9f0d2a6b4c88",
        "opportunityName": "Northwind — platform expansion",
        "opportunityNumber": "OPP-2291",
        "billToAddressId": "6f9a2b1c-33d4-4a55-9e21-0b7c8d4e1f02",
        "soldToAddressId": "6f9a2b1c-33d4-4a55-9e21-0b7c8d4e1f02",
        "lastModifiedDate": "2026-08-14T09:12:44.000Z",
        "subscriptions": [
          {
            "id": "c2e1f4a7-8b90-4d33-9a15-6f7e8d2b0c41",
            "termType": "termed",
            "serviceStartDate": "2026-09-01",
            "serviceEndDate": "2028-08-31",
            "autoRenew": true,
            "renewalTerm": 12,
            "quoteProducts": [
              {
                "id": "d4f2a1b8-3c67-4e05-8b92-1a7d0e6c5f38",
                "quoteProductNumber": "QP-3",
                "productId": "e5a3b2c9-4d78-4f16-9c03-2b8e1f7d6a49",
                "productCode": "PLATFORM-ENT",
                "productName": "Platform — Enterprise",
                "isRampDeal": false,
                "orderIndex": 1,
                "quoteProductLines": [
                  {
                    "id": "f6b4c3d0-5e89-4a27-8d14-3c9f2a8e7b50",
                    "quoteProductLineNumber": "QPL-7",
                    "productLineId": "a7c5d4e1-6f90-4b38-9e25-4d0a3b9f8c61",
                    "productLineName": "Enterprise seats",
                    "lineType": "recurring",
                    "quantity": "100.00",
                    "listPrice": "480.00",
                    "unitPrice": "400.00",
                    "discountPercentage": "0.166667",
                    "totalPrice": "40000.00",
                    "invoiceFrequency": "annually",
                    "priceModel": "tiered",
                    "pricingTerm": "annually",
                    "unitOfMeasure": "seat",
                    "serviceStartDate": "2026-09-01",
                    "serviceEndDate": "2028-08-31",
                    "orderIndex": 1,
                    "lineTiers": [
                      {
                        "id": "b8d6e5f2-7a01-4c49-8f36-5e1b4c0a9d72",
                        "fromQuantity": "1.00",
                        "toQuantity": "50.00",
                        "priceModel": "tiered",
                        "unitPrice": "440.00",
                        "listPrice": "480.00"
                      },
                      {
                        "id": "c9e7f6a3-8b12-4d50-9a47-6f2c5d1b0e83",
                        "fromQuantity": "51.00",
                        "priceModel": "tiered",
                        "unitPrice": "400.00",
                        "listPrice": "480.00"
                      }
                    ],
                    "rateCardEntries": [],
                    "invoiceMilestones": []
                  }
                ]
              }
            ]
          }
        ],
        "nonRecurringProducts": [
          {
            "id": "d0f8a7b4-9c23-4e61-8b58-7a3d6e2c1f94",
            "quoteProductNumber": "QP-4",
            "productId": "e1a9b8c5-0d34-4f72-9c69-8b4e7f3d2a05",
            "productCode": "ONBOARD",
            "productName": "Onboarding",
            "orderIndex": 2,
            "quoteProductLines": [
              {
                "id": "f2b0c9d6-1e45-4a83-8d70-9c5f8a4e3b16",
                "productLineId": "a3c1d0e7-2f56-4b94-9e81-0d6a9b5f4c27",
                "productLineName": "Implementation",
                "lineType": "oneTime",
                "quantity": "1.00",
                "listPrice": "10000.00",
                "unitPrice": "10000.00",
                "totalPrice": "10000.00",
                "invoiceFrequency": "milestone",
                "priceModel": "flat",
                "unitOfMeasure": "each",
                "lineTiers": [],
                "rateCardEntries": [],
                "invoiceMilestones": [
                  {
                    "id": "b4d2e1f8-3a67-4c05-9f92-1e7b0c6d5a38",
                    "description": "Kick-off",
                    "totalPrice": "5000.00",
                    "percentage": "50.0000",
                    "projectedDate": "2026-09-15"
                  }
                ]
              }
            ]
          }
        ]
      }
    ],
    "pagination": {
      "hasMore": false
    }
  },
  "diagnostics": [
    {
      "code": "<code>",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": "<details>"
    }
  ]
}
```

## Statuses

- `400` — Invalid request or business failure
- `401` — The request carries no valid access token.
- `403` — Not authorized
- `404` — Not found
- `429` — Too many requests from this client address. The response has no body and no `Retry-After` header.
- `500` — Internal server error
