ConnectWise CPQ Quotes

SkillCommerce & finance

The ConnectWise CPQ quote lifecycle over the real tool surface: searching quotes, the GUID-vs-quoteNumber dual addressing, versions, creating quotes by copying a template (the API's only create path), patching quote fields, the per-quote customer records and payment/financing terms, tabs as the section structure, and the deletes that cascade.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the ConnectWise CPQ Quotes skill

What this skill tells your AI

The instructions your AI receives, as published by wyre-ai/msp-claude-plugins in msp-claude-plugins/connectwise/cpq/skills/quotes/SKILL.md and read by ahel’s review.

Overview

A CPQ quote is the proposal an MSP sends a client: a header (account, status, dates, totals), one or more tabs that section the proposal, line items on those tabs, customer records synced from the attached CRM/PSA, and optional payment/financing terms. Quotes are versioned — a revision creates a new version under the same quote number rather than mutating the old one.

Everything an MSP does after the quote is built — publishing to Order Porter, e-signature, porting a won quote into the PSA as a sales order — happens in the CPQ web app. The API covers building and reading quotes, not delivering them.

Anti-triggers

CPQ owns one stage of the quote funnel: composing and pricing the quote document. It does not source the SKUs that go into it, it cannot deliver it, and it is not the only tool in the stack that holds something called a quote.

  • The priced SKU master list rather than this deal's document — catalog items, costs, categories and manufacturers live in ConnectWise PSA; use connectwise-psa-product-catalog.
  • "Send the customer the quote", e-signature, or signed-document status — CPQ's API has no publish, send or e-sign verb. isSent and the orderPorter* fields are state you can read (and patch), not actions you can trigger. Delivery and signature tracking are pandadoc-documents.
  • A quote that was built in a different quoting product — Kaseya Quote Manager and SalesBuildr each keep their own quotes, numbering and APIs; use kaseya-quote-manager-quotes or salesbuildr-quotes. Route on the product the quote lives in, not on the word "quote".
  • Adding, repricing or removing a line — the quote header and its line items are separate tool sets; use connectwise-cpq-quote-items.

Key Concepts

Two ways to address a quote

AddressingUsed byNotes
GUID id (string)cpq_get_quote, cpq_update_quote, cpq_delete_quote, cpq_create_quote_from_templateUnique to one version of one quote
quoteNumber (int) + quoteVersion (int)cpq_get_quote_versions, cpq_delete_quote_versionThe human-facing number; identifies the quote across its versions

Searches return both, so pull the id out of a search result before doing anything that needs a GUID. A quote number alone is not enough to patch or delete a quote.

Versions

cpq_get_quote_versions takes a quoteNumber and behaves three ways:

  • omit version → list every version
  • version: "latest" → the current version
  • version: 3 → that specific version

Searches return latest versions only unless you pass showAllVersions: true. If a search seems to be missing a quote a colleague is looking at, they are probably on a superseded version.

Quote status and flags

Status lives across several fields rather than one enum. The ones that decide what a quote means:

FieldMeaning
quoteStatusTenant-configurable status text (values vary per install — read them from live data, do not assume)
isSentDelivered to the customer
isAcceptedCustomer accepted
isLostMarked lost
isArchiveArchived; excluded from most working views
requiresApproval / approvalStatus / approvedByUserInternal approval workflow
expirationDate / expectedCloseDateProposal validity and forecast date
orderPorter*Order Porter publishing state (passcode, template, signed date, upload state)

See references/fields.md for the fuller QuoteView field reference.

Sub-resources

Sub-resourceToolsNotes
Tabscpq_search_quote_tabsRead-only over the API. Sections of the proposal; every line item belongs to one.
Line itemssee the quote-items skillFull CRUD
Customerscpq_list_quote_customers, cpq_update_quote_customer, cpq_delete_quote_customerExist only in the context of a quote — there is no global customer directory to search
Termscpq_list_quote_terms, cpq_create_quote_term, cpq_update_quote_term, cpq_delete_quote_termPayment/financing options: periods, interestRate, downPayment, periodPaymentAmount, isSelected, leasing fields

Common Workflows

Find a quote

  1. cpq_search_quotes with conditions and a tight includeFields, e.g. conditions: accountName contains "Acme" AND isArchive = False, includeFields: id,name,quoteNumber,quoteVersion,quoteStatus,quoteTotal,createDate
  2. Page with page/pageSize until a page returns fewer rows than pageSize.
  3. cpq_get_quote with the GUID for the full record.

Called with no conditions the search asks for a created-since date, falling back to the last 90 days and noting that in the result. Give it a condition when you know one — a 90-day window silently hides older quotes.

Create a quote

There is no create-from-scratch endpoint. Every new quote is a copy of a template or of an existing quote, then patched into shape.

  1. cpq_list_templates to see what is available (templates are themselves quotes).
  2. cpq_create_quote_from_template with templateId, or with templateName to resolve by name — an ambiguous name prompts a selection, and with no way to prompt you get an error listing the candidates. Pass newName to rename the copy in the same call.
  3. The copy inherits the template's tabs, line items and terms. Patch the header with cpq_update_quote (account, dates, custom fields).
  4. Add or adjust lines with the quote-items tools.

To revise an existing deal, copy the existing quote instead of a template — cpq_create_quote_from_template accepts any quote GUID as its source.

Update a quote

cpq_update_quote takes either fields (a partial object, turned into replace ops) or patch (raw RFC 6902 ops) — never both.

cpq_update_quote  id=<guid>  fields={ "name": "Acme — Managed Services FY27",
                                      "expectedCloseDate": "2026-09-30T00:00:00Z" }

This tool is flagged high-impact for a reason: patching quoteStatus, isArchive, isLost, expirationDate, orderPorter* or the approval fields changes workflow state and, for the Order Porter fields, what the customer sees. Confirm intent before touching those; renaming or setting a forecast date is routine.

Work the customers and terms on a quote

  1. cpq_list_quote_customers with the quote GUID — the records are already there, synced from the CRM/PSA. Correct them with cpq_update_quote_customer.
  2. cpq_list_quote_terms for financing options; cpq_create_quote_term to add one. isSelected marks the term the customer is being offered.

Delete

cpq_delete_quote removes the quote and every tab, line item and term on it. Confirmation depends on how you are connected: an interactive client is prompted, while a non-interactive one — the gateway included — is refused until the call is re-invoked with confirm_destructive_action: true. cpq_delete_quote_version removes a single version by quoteNumber + quoteVersion. Neither is recoverable — archive (isArchive: True) is almost always the right move for a dead deal instead.

Gotchas

  • count in a tool result is the page length, not a total. CPQ returns bare arrays with no collection count. Keep paging until a short page arrives.
  • Searching by quote number needs a number, not a string: quoteNumber = 1042, not quoteNumber = "1042". Dates go in square brackets and are date-only: createDate >= [2026-07-01].
  • Every line item needs a tab. A freshly copied quote has the template's tabs; a quote with no tabs cannot take line items, and tabs cannot be created over the API.
  • Deleting a quote version is not the same as deleting a quote. The version delete targets quoteNumber + quoteVersion; the quote delete targets a GUID and takes everything.
  • crmOpportunityId is a pointer, not a join. There are no opportunity endpoints in CPQ — resolve it against the PSA/CRM (the connectwise-psa plugin) if you need the opportunity itself.
  • QuoteView is huge and mostly empty. 204 properties, 60+ of them unused zCustom* slots. Always send includeFields on searches or the results bury the useful fields.

Related Skills

Signals

GitHub stars
45
Forks
24
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
connectwise-cpq-quotes
Source
github.com/wyre-ai/msp-claude-plugins