Integration · Revenue operations
How to build a CPQ to ERP sync
Finance adds a SKU on Monday, sales quotes it on Tuesday, and the order fails on Wednesday because the mapping table has never heard of it. The thinking behind a sync that holds, the prompts that build it, and what it takes to keep it running.
Built with Tray Headless
- System Salesforce CPQ
- Step Mapping table
- Step Ramp as periods
- System NetSuite
Each ramp period becomes its own line, and an unmapped SKU blocks rather than falling through to a default.
The short answer
What is a CPQ to ERP sync?
A CPQ to ERP sync is four parts: a product mapping held as data instead of in code, ramp and proration structures carried through intact instead of flattened to a single figure, catalogue alignment that runs in both directions, and a hard failure on an unmapped SKU rather than a guess. The mistake that costs most is flattening the ramp. A three year deal with escalating pricing booked as one annual figure is wrong in year two and wrong for the renewal.
What matters here
- Hold the product mapping as data. The catalogue changes monthly and the integration should not need republishing when it does.
- Never flatten a ramp. A deal with escalating pricing booked as one figure is wrong from year two onward and corrupts the renewal.
- Reconcile the catalogues both ways. A SKU in CPQ that the ERP has never heard of is a booking failure waiting for a quarter end.
- Fail loudly on an unmapped SKU. A guess produces revenue booked against the wrong account, and that is found by audit rather than by you.
- Carry the CPQ line id onto the ERP line. Without it, reconciliation compares totals instead of lines and misses the offsetting errors.
Who this is for
You run revenue operations or finance systems. Quoting and booking use different product catalogues, somebody maintains the mapping in a spreadsheet, and a new SKU takes a week to become quotable.
How it works in practice
What has to happen between a product existing in one catalogue and being bookable in the other.
- 1
A new ERP item appears in the mapping queue
Detected rather than announced, because the catalogue changes without anybody telling the integration.
- 2
Somebody maps it, once, as data
A row in a table, not a code change. The mapping is owned by finance systems, not by whoever wrote the workflow.
- 3
Quote lines resolve through the mapping at booking time
Every line, every time. An unmapped SKU stops the booking rather than falling through.
- 4
Ramps and proration are carried as structure
Each period is its own line with its own dates and price, not an average across the term.
- 5
The CPQ line id travels onto the ERP line
So reconciliation compares line to line rather than total to total.
- 6
Both catalogues are reconciled nightly
Items in one and not the other, in both directions, reported before a deal hits the gap.
What the sync is made of
Two catalogues that will never be identical, and one commercial agreement that has to survive the translation.
Mapping as data
A table keyed on CPQ product code, holding the ERP item, the revenue account and the tax treatment. Editable by finance systems without a deployment.
Ramps carried intact
Multi-year and escalating structures kept as separate periods with their own dates and prices. Flattening is the most common and most expensive shortcut here.
Two-way catalogue alignment
Nightly comparison in both directions, because a SKU added on either side is a booking failure nobody sees coming.
A hard fail on unmapped
No fallback item, no best guess. An unmapped SKU stops the booking and raises it, because the alternative is revenue on the wrong account.
The Tray Headless prompts
Paste these into Claude Code or Codex with the Tray Headless plugin installed. Each stage runs on its own. The systems named in them are the worked example rather than a requirement, and every prompt says so.
Once per project, run
/tray-workflows:set-workspace
to pick the workspace these build in. Point it at a sandbox first.
- 1
First, pull both catalogues
The mapping cannot be designed until both sides are visible.
Headless skills
build-workflowUse build-workflow. The systems in play are Salesforce CPQ and NetSuite, or whatever we run in those seats. I need the CPQ model: SBQQ__Quote__c, SBQQ__QuoteLine__c and SBQQ__Subscription__c, plus Product2 and PricebookEntry, and how ramp and subscription pricing are represented across them. From NetSuite I need the Item records, their revenue accounts and their tax schedules. Then show me both catalogues side by side with anything that looks like a match already. The differences are the work.
- 2
Build the mapping as a table, not as logic
Because the catalogue changes more often than the integration will.
Headless skills
build-workflowtray-patternsUse build-workflow. Create a mapping table keyed on CPQ product code, holding: the NetSuite item id, the revenue account, the tax schedule, the income deferral treatment, and whether the item is subscription or one-off. Read it at booking time, every time. Do not compile the mapping into the workflow, and do not cache it beyond a few minutes. A new SKU should be quotable and bookable the same day somebody maps it. Detect unmapped items on both sides and put them in a queue for finance systems, with everything needed to map them in one screen.
- 3
Carry ramps and proration through as structure
The step that decides whether year two of a deal is correct.
Headless skills
tray-patternsNever flatten a ramp to a single annual figure. For each ramp period, create a separate ERP line with its own start date, end date, quantity and unit price. A three year deal at 100k, 120k and 140k is three lines, not one line at 120k. Handle proration explicitly: a mid-month start prorates the first period by day count, and the term end aligns to the contract instead of to a calendar year. Co-terminated amendments take the parent contract end date. Then assert that the sum of the ERP lines equals the CPQ total to the cent. If it does not, stop. A rounding difference across periods is a real difference and finance will find it.
Flattening a ramp is the single most expensive shortcut in this integration. It books correctly in year one, which is why it survives testing.
- 4
Fail loudly on anything unmapped
Because the alternative is revenue booked against the wrong account.
Headless skills
tray-gotchasUse tray-gotchas, then make unmapped a stop condition. If a quote line has no mapping, the booking stops. No fallback item, no default revenue account, no closest match. Raise it to finance systems and to the deal desk with the product code and the deal it is blocking. Also stop on: a price book entry whose currency the ERP is not configured for, an item mapped to an inactive ERP record, and a discount that pushes a line price below a floor finance has set. A guess here produces revenue on the wrong account, and that is found in an audit instead of by anybody watching.
- 5
Reconcile the catalogues in both directions
A SKU added on either side is a booking failure waiting for quarter end.
Headless skills
tray-patternsNightly, compare both catalogues and report: Active CPQ products with no mapping Mapped ERP items that have been deactivated ERP items with no CPQ equivalent, which is usually fine but worth seeing Mappings pointing at a revenue account that no longer exists Price book entries in currencies the ERP cannot accept Alert on the first two, which block bookings. Report the rest. Also carry the CPQ line id onto every ERP line, so reconciliation can compare line to line. Comparing totals hides two errors that offset each other, and that pair is exactly what an auditor finds.
- 6
Validate, then hand the table to finance systems
Because the catalogue belongs to them.
Run the per-step schema checks and the whole-workflow audit before this touches production. Then open the same workflow in Tray Build so finance systems can maintain the mapping table, the revenue accounts and the tax treatments in the visual canvas. Every catalogue change touches this, and none of them should be an engineering request.
What it connects to
Two catalogues, one mapping, and the people who need to know when they diverge.
Salesforce CPQ
Read products, price books, quote lines and ramp structures with their period dates and prices.
Reads
NetSuite
Read the item master and revenue accounts, and create order lines that mirror the quote structure exactly.
Reads and writes
Salesforce
Write the booking result and any blocking mapping gap onto the opportunity, where the deal team will see it.
Writes
Slack
Tell finance systems about an unmapped SKU immediately, because it is blocking a deal right now.
Writes
Snowflake
Hold the mapping history and the nightly reconciliation, so catalogue drift is visible over time.
Reads and writes
Same build, other stacks
The design does not change if you run something else in one of these seats. The same prompts build it against Microsoft Dynamics 365, SAP S/4HANA, Google BigQuery, Microsoft Teams, HubSpot or Oracle.
Named systems are the ones most teams run, not the only ones that work. Each is an authentication in your Tray workspace, referenced by name, so the workflow never holds a credential. Where we have a connector page, the name links to it.
Running it in production
This decides which revenue account a booking lands on. It is audited every quarter.
It runs where production runs, not on a laptop
Booking-time lookups and the nightly catalogue reconciliation execute on the same engine, with a record of which mapping version produced which line.
Every mapping decision is recorded
Which mapping row resolved which line, and when. That is what makes a revenue misposting traceable, not a mystery.
Credentials are managed, never in code
The ERP credential writes revenue-bearing order lines. It lives in your workspace, scoped tightly, and separately from anything that reads the catalogue.
Finance systems own the table
Mappings, revenue accounts and tax treatments open in Tray Build, maintained by the team that owns the catalogue.
Unmapped blocks, it does not warn
A booking that proceeds on a guess is a misposting. Stopping is the correct behaviour even though it is the noisier one.
Questions people ask
Why hold the mapping as data rather than in code?
Because the product catalogue changes monthly and the integration does not. A new SKU should be bookable the day finance maps it, not the day somebody deploys.
What is wrong with flattening a ramp?
It books correctly in year one, which is why it survives testing, then it is wrong in every subsequent year and it corrupts the renewal figure. Each ramp period needs its own line with its own dates and price.
What should happen to an unmapped SKU?
The booking stops. No fallback item and no closest match, because a guess puts revenue on the wrong account and that surfaces in an audit instead of in a report you read.
Why reconcile the catalogues in both directions?
Because a product added in CPQ that the ERP has never seen blocks a booking, and a deactivated ERP item that is still mapped fails in a different way. Both are found by a nightly comparison and by nothing else.
Can finance systems maintain this without engineering?
Yes. The mapping table, revenue accounts and tax treatments open in Tray Build, so the team that owns the catalogue owns the translation.
Related guides
Revenue operations
How to build an order-to-cash integration
Closed-won to sales order to invoice to cash applied, as a CRM to ERP integration. Idempotent writes, a reconciliation loop, and the Headless prompts that build it.
Revenue operations
How to build a quote-to-order integration
Approved quote to signed contract to booked order, with the approval state carried through and amendments handled. The Headless prompts that build it.
Last reviewed September 2026.