Query a sales cohort
Leads and marketing sources
Job leads preserve the lowercase statesopen, converted, and archived, including all by default. Their contact/address objects contain nullable captured submission values, not live CRM records or fabricated contact/address IDs. request_type, submitted_account_type, and legacy submitted_service_type retain recorded text; submitted_job_type is a separate configured reference. source_text is free-form intake text and does not identify a configured /lead-sources record. The example’s Booking Website text and its job’s Referral source are distinct recorded facts. Do not map one to the other automatically.
Lead filters include repeated status, job_id, existing_location_id, submitted_job_type_id, request_type, and exact case-sensitive source_text. A lead’s job_id is a recorded conversion link, not proof of a sale or payment. Two leads can reference one job: count lead IDs for requests and distinct linked job IDs for converted jobs. There is no stored conversion timestamp or conversion actor; updated_at also changes for summary and other edits. Do not use it or job creation time as a conversion event date. Provider recordings, transcripts, signatures, arbitrary platform links, and availability blobs are not part of these reporting records.
Estimate options and accepted value
Estimates contain complete orderedoptions, each with its own line_items, discounts, selection/recommendation flags, and recorded total_cents. Options are alternatives: never add their values together as one estimate’s value. The example’s selected repair option is $990, while its recommended replacement alternative is $8,800. The estimate’s accepted_total_cents is $990. A recommendation alone does not establish selection or acceptance. For a current ACCEPTED estimate with zero or multiple selected options, accepted value is null. Other statuses also return null, even if an acceptance timestamp or selected flag remains populated.
Option totals are the current saved proposal values. They are not recalculated from the current catalog or invoice calculations. Constituent unit prices, fractional quantities, configured flat/rate discounts, the estimate tax name/rate, option tax-rounding context, and presentation/conversion pricing multiplier remain available. Saved option totals can differ from invoice calculations, including their dynamic-pricing treatment; do not manufacture a reconciled tax/subtotal breakdown or multiply an option total again to establish accepted value. Tax rounding follows company configuration at the option’s creation, with the application’s ceil fallback. Invoice amounts retain their separately documented calculated basis.
Acceptance and other dates
accepted_at is the recorded acceptance timestamp and can remain after reopening. Accepted proposals can also be edited, so accepted_total_cents is the current selected-option value, not a frozen sale event at that timestamp. created_by identifies the original proposal creator. accepted_on_behalf_by preserves the recorded staff proxy and may remain from an earlier acceptance; it is not a seller or signing history. last_sent_at records the latest send attempt, not confirmed delivery. Loss category/note are current recorded fields; later status changes can clear them. The API provides no inferred review, first-send, expiration, loss, or status-transition dates. Messages and option/discount descriptions use plain text.
Estimate filters include job_id, account_id, business_unit_id, job_type_id, job_class, created_by_id, and repeated status. accepted_from/accepted_before are inclusive/exclusive company-local dates on the recorded acceptance timestamp, independent of current status. Add status=ACCEPTED for currently accepted proposals. Creation-date cohorts and acceptance-date activity answer different questions. Both estimates and job leads support creation instant bounds and inclusive parent-only updated_since; option, line, discount, and invoice-link edits may not advance the estimate parent time.
Join conversions
An estimate’sjob_id, account, business unit, type, and class describe its original job. invoice_ids includes every recorded tenant-owned conversion, which may belong to another linked job/account. Do not assume there is exactly one invoice or that its job equals the original estimate job. Conversion copies line items, so estimate and invoice line IDs differ. Replace the complete embedded option/line/discount sets when refreshing a proposal, including children that have disappeared.
Accumulate and refresh
For sales reporting, accumulate leads and estimates by ID, join their job IDs to/jobs, and use estimate invoice_ids to find the corresponding invoices. Calculate creation cohorts from created_at, current conversion from lead status/link, and current accepted value from estimates. Retain null accepted values as unavailable rather than zero. On refresh, reread known open/outstanding IDs without the old status filter so accepted, archived, or closed records are observed, and periodically reconcile broader scopes for corrected children and hard deletions. These are customer-controlled refresh strategies, not a guaranteed synchronization feed or historical pipeline snapshot.