Proposal Data Keys
A proposal template does not run queries. It declares the sources it needs and names a key wherever a value should appear on the page, and the server assembles those values once, scoped to your org and checked against what your plan includes.
This page lists every key that exists, and what each one is actually for.
How a key is written
A key is a source, a dot, and a field:
solarDesign.systemKw
customer.name
customer.quote.totalAmount
financing.apr
products.0.name
Lists are reachable by number — products.0 is the first product, ordered by
name so it means the same thing on every proposal from that template.
Three rules are worth knowing before you author against them.
A key names one printable value, never a record. solarDesign.pricePerWatt
is a key; solarDesign.pricing is not, and a template pointing at a whole
record is refused at authoring time rather than printing [object Object] to a
customer.
A missing value is never a blank. If a key has no value on this job, the field arrives on the fill form marked blocked, with the reason, and somebody answers it. It does not quietly print an empty box.
A source your plan does not include is withheld, not broken. The proposal says which feature it needed instead of rendering a hole — so a template degrades rather than failing at somebody's kitchen table.
solarDesign — the roof, the system and the price
Requires the Solar Designer feature. Reads the design attached to this quote, at its current revision — a proposal quotes a version of a design, and the version is the point.
If the quote has no solar design at all, the whole source is absent. A solar template used on a non-solar quote says so instead of printing zeros.
What the design is
| Key | What it is for |
|---|---|
solarDesign.id | The design's identifier. For a reference line or a QR link back to it. |
solarDesign.name | What the design is called — "Roof design", "South array", whatever the rep named it. |
The system
These come from the engine, recomputed and copied on every save, so they are never a stale figure typed by hand.
| Key | What it is for |
|---|---|
solarDesign.panelCount | Number of modules, after any hand adjustment on the pricing step. The number a customer counts on the picture. |
solarDesign.systemKw | System size in kWp, to three decimals. The headline "an 8.4 kW system". |
solarDesign.systemWatts | The same size in watts, rounded. Use it where a per-watt calculation is being shown. |
solarDesign.annualKwh | First-year production. Absent until a simulation has been run — a drawn-but-unsimulated design has no annual figure, and inventing one is worse than asking. |
solarDesign.specificYield | kWh per kW installed. What lets one design be compared against another regardless of size. |
solarDesign.solarAccessPercent | How much of the available sun this roof actually receives, 0–100. The honest way to show a shaded roof. |
solarDesign.tsrfPercent | Total solar resource fraction, 0–100 — orientation and shade together. |
solarDesign.simulated | True once the annual simulation has been run. Use it to hide a production block on a design that has not been simulated. |
solarDesign.computedOn | The date those figures were computed. A production number without its date cannot be checked later. |
The module
| Key | What it is for |
|---|---|
solarDesign.moduleManufacturer | The module's maker, from the equipment library. |
solarDesign.moduleModel | The model designation. Together with the maker, the line a customer checks against a datasheet. |
solarDesign.moduleWatts | Nameplate watts per module. panelCount × moduleWatts is the system, which is the arithmetic a customer does in their head. |
solarDesign.moduleDatasheetUrl | A link to the manufacturer's specification PDF. Absent when the library entry has none. |
What they use, and what this covers
| Key | What it is for |
|---|---|
solarDesign.annualUsageKwh | The customer's annual consumption, from the quick bid. |
solarDesign.monthlyBill | The bill the system was sized against. |
solarDesign.offsetPercent | The offset the design achieves — production over usage — not the offset that was aimed at. A proposal printing the target rather than the result promises a number the array does not make. |
The price
Every figure here is the pricing form applied to that system, computed by the same function the pricing screen uses. The rep and the customer read the same net price because there is only one place that works one out.
| Key | What it is for |
|---|---|
solarDesign.pricePerWatt | The rate this job is sold at. |
solarDesign.systemPrice | The system alone — price per watt × watts, before adders. |
solarDesign.adders | Everything added to the base system: a main panel upgrade, a tree removal, trenching. |
solarDesign.salePrice | What the job sells for — system plus adders, before any credit or deposit. This is the contract number. |
solarDesign.downPayment | What is paid up front. |
solarDesign.federalCreditPercent | The credit rate applied, e.g. 30. A number rather than a constant because it is legislated and has already changed twice. |
solarDesign.federalCredit | The credit the system earns — the rate applied to the sale price. |
solarDesign.federalCreditReceived | The part the customer will actually receive, given their stated liability and carry-forward window. This is the number a net price may honestly subtract. |
solarDesign.federalCreditForfeited | The part that runs out of years and is never received. Printing this is what stops a proposal quietly over-promising. |
solarDesign.federalCreditYears | The carry-forward window used, in years. Print it beside the credit so the assumption travels with the number. |
solarDesign.netPrice | Sale price, less the credit they will actually get, less the deposit. |
The federal credit is not a rebate. federalCredit is what the system earns;
federalCreditReceived is what lands. On a customer whose tax liability is
smaller than the credit those are different numbers, and a proposal that
advertises the first as though it were the second is the one they come back
about in April.
The long-term case
Present only when a bill and a rate were captured on the quick bid — there is nothing to project against otherwise, so these keys are absent rather than zero.
| Key | What it is for |
|---|---|
solarDesign.firstYearSavings | What the system saves in year one, capped at the bill it offsets. |
solarDesign.lifetimeSavings | Total savings over the projection, with module degradation and utility escalation both applied. |
solarDesign.lifetimeYears | The term that projection covers, normally 25. Print it — a savings figure without its term means nothing. |
solarDesign.paybackYears | The year cumulative savings first cover the net price. Absent when it never pays back inside the term — that is a fact to state plainly, not a large number to imply. |
The picture
| Key | What it is for |
|---|---|
solarDesign.imageCapturedOn | The date the design image was frozen. A proposal prints this beside the image. |
solarDesign.imageView | 2d or 3d — which view was captured. |
solarDesign.imageMatchesDesign | False when the design has gained or lost panels since the picture was taken. An image showing 32 panels beside a price for 28 is the failure this exists to catch. |
The image itself is not a key — it is placed with a solar view field bound to the design, so the platform renders it rather than the template.
customer / lead — the person, and their quote
The same contact under both names, because a residential template author writes "lead" and a commercial one writes "customer", and it is the same row either way. Pick one and use it consistently.
| Key | What it is for |
|---|---|
customer.name | The person or business the proposal is addressed to. |
customer.company | Their company, where the contact has one. |
customer.email · customer.phone · customer.mobilePhone | Contact details for the header or the signature block. |
customer.street · customer.city · customer.state · customer.postalCode · customer.country | The primary address, flattened one level so a template writes customer.city rather than digging through an address list. |
customer.status | Where they sit in the pipeline. |
The quote it hangs on
| Key | What it is for |
|---|---|
customer.quote.number | The quote number. The reference everything else in the job is filed under. |
customer.quote.subtotalAmount | Before discount and tax. |
customer.quote.discountAmount | What was taken off. |
customer.quote.taxRate · customer.quote.taxAmount | The rate applied and what it came to. |
customer.quote.totalAmount | The quote total — the value templates reach for most. |
customer.quote.validUntil | The expiry date. A proposal with no expiry is a price you are still honouring next year. |
products — what you sell
Live products only, ordered by name, up to 25. A proposal naming
products.40.name is not a proposal.
| Key | What it is for |
|---|---|
products.0.name | The product's name. |
products.0.description | Its description, for a line item or a spec block. |
products.0.cost | Cost in dollars — money reaches a template in the units the rest of a quote uses, so a currency field never has to know which table a number came from. |
products.0.id | Its identifier. |
financing — how it gets paid for
Only products your org can actually offer today: inactive ones, and ones a lender has withdrawn, never reach a proposal a customer could try to accept.
The cheapest option by APR is surfaced flat as well as listed, because that is
the one a template reaches for and making an author write
financing.options.0.apr for the common case is how a template ends up
hard-coding a rate instead.
| Key | What it is for |
|---|---|
financing.name | The best (lowest-APR) option's name. |
financing.apr | Its APR. |
financing.termMonths | Its term. |
financing.options.0.name · .apr · .termMonths · .minCreditScore · .id | Any option by position, cheapest first. Use these to print a comparison table. |
schema — questions this template asks
Anything a template needs that no source can answer is defined on the template
itself and reached as schema.<key> — schema.roof_pitch,
schema.hoa_name. Those become questions on the fill form.
Two halves worth keeping straight: a template may also read custom fields your org already has, and ticking one of those means the proposal reads it, not that it asks for it again. A proposal that asks for something the org already knows is a worse proposal.
Keys are lowercase with underscores, and a handful are reserved because they
would be ambiguous with a real source: schema, customer, lead, quote,
products, financing, solar, solardesign, org.
Sources that are declared but not yet assembled
utilityRatePlans, inventory, collections, orgFunctions and
platformFunctions are part of the design and have no assembler yet. A template
asking for one is told, rather than left to wonder why a field is empty.
Business functions are worth a note for when they land: a platform function exposes its signature — name, inputs, outputs — and never its expression. The proposal calls it on the server and receives only the result, which is what lets a proprietary calculation be used without being read.