Owner reports
Run the reports from TruTec's Reports page (money owed, cash forecast, KPIs, win / loss, bid vs actual and more), read labor hours by person, day and job, and a month's revenue and direct cost per job.
View as MarkdownThese are the reports an owner reads on TruTec's Reports page, built by the same code as the page, its CSV and Excel exports and the scheduled report emails. Use them to answer questions like "who owes us money and how late is it?", "what did we win this month?" or "which jobs are over budget?".
All of them are read-only and need Read jobs. They run as the user who created the API key, so they show what that person sees: their job visibility applies, and management reports need a user who may view them (owners always; members when an owner turned on Can view management reports for them in Settings › Members). Otherwise GET /v1/reports/{key} returns 403 Reports not available.
List reports#
GET /v1/reports
Permission: Read jobs
The reports this key can run, with key, title, section, description and date_basis (which date the range filters on: bid, decided, created, or all when each figure uses its own date).
| Key | Report |
|---|---|
weekly-summary |
Every week of the year: new leads, dollars quoted, work won, invoiced and collected, with past years side by side. |
week-glance |
One week: new leads, estimates sent, work won, change orders, invoices and payments. |
receivables |
Money owed to you: open invoices by customer and age (current, 1–30, 31–60, 61–90, over 90 days), plus billed and collected in the range. |
cash-forecast |
Cash likely to come in over the next weeks. |
top-customers |
Customers ranked by work won, with bids, hit rate, billed, collected and owed. |
zip-code |
Bids, wins and dollars by zip code and city. |
bid-vs-actual |
Jobs with costs logged: bid cost against actual and forecast, flagged when outside tolerance. |
material-usage |
Materials on won work, estimated against used. |
completed-work |
Pay items marked Completed on the estimate, counted on the day they were finished: revenue (their price on the estimate), estimated cost and profit, plus what is still to do on won jobs. view groups by job (blank), worktype, division or salesperson. Needs no invoices. |
bid-schedule, win-loss, win-loss-graph, work-type, market-analysis |
Sales. |
data-dump, estimate-totals, item-history, material-history, resource-utilization |
Estimating. |
sub-utilization, vendor-utilization, minority |
Subcontractors and vendors. |
weekly-sales, kpi |
Weekly and monthly sales KPIs against the previous period, last year and goals. |
lead-pipeline, brochure-scans |
Listed only for workspaces that use them. |
labor-hours |
Listed when the workspace runs crews and the user has Approve payroll. See below. |
month-end-job-cost |
Listed when the workspace runs crews and the user may see job costs (Estimating Pro and pricing access). See below. |
Run a report#
GET /v1/reports/{key}
Permission: Read jobs (and the management reports permission above)
| Name | Type | Description |
|---|---|---|
start, end |
date | The range (YYYY-MM-DD). Default 1 January this year to today. kpi, weekly-sales and week-glance cover the whole week or month containing end. |
date_basis |
string | bid, decided or created, for reports with a "Dates are" picker. |
salesperson_ids, estimator_ids, customer_ids, division_ids |
string | Comma-separated ids. |
statuses, work_types, outcomes |
string | Comma-separated pipeline stage keys, job types, and won / lost / open / excluded. |
group_by |
string | Up to 3, comma-separated: work_type, salesperson, estimator, customer, division, status, outcome, month, quarter, year, customer_type, property_type, source, zip, city. |
period |
string | week or month (KPI report). |
weeks |
integer | Cash forecast: 4, 8, 12 or 26. |
net_days |
integer | Receivables: an invoice with no due date is due this many days after it's sent (0, 15, 30, 45, 60 or 90; default 30). |
q, view, columns |
string | The page's search box, view and column picker, where the report has them. |
rows_limit |
integer | Rows per table. Default 200, maximum 2,000. rows_total says how many there were. |
curl "https://trutec.ai/api/v1/reports/receivables" \
-H "X-API-Key: sk_your_api_key_here"
{
"data": {
"key": "receivables",
"title": "Money owed to you",
"section": "Owner dashboard",
"description": "Every open invoice, how late it is and who owes the most — plus what you billed and collected.",
"start": "2026-01-01",
"end": "2026-10-14",
"date_basis": "all",
"summary": [
{"label": "Owed to you now", "value": "$62,410"},
{"label": "Overdue", "value": "$18,200"}
],
"tables": [
{
"key": "aging",
"title": "Who owes you",
"columns": [
{"key": "customer", "label": "Customer", "kind": "text"},
{"key": "count", "label": "Invoices", "kind": "int"},
{"key": "current", "label": "Current", "kind": "money"},
{"key": "d1_30", "label": "1-30 days", "kind": "money"},
{"key": "total", "label": "Total owed", "kind": "money"},
{"key": "oldest", "label": "Oldest invoice", "kind": "date"}
],
"rows": [
{"customer": "Northgate Properties", "count": 2, "current": 12125.0, "d1_30": 6075.0, "total": 18200.0, "oldest": "2026-08-29"}
],
"rows_total": 1,
"total": {"customer": "Total", "count": 2, "total": 18200.0},
"note": null
}
],
"truncated": false
}
}
summary values are display text ("$62,410"), as on the page. Table rows hold raw values: money and numbers as numbers, pct columns as fractions (0.25 = 25%), dates as YYYY-MM-DD. Rows about one job also carry job_id; a customer's division rows carry is_division_row: true under the customer's row. truncated is true when more than 20,000 jobs matched (narrow the range).
Labor hours#
GET /v1/reports/labor-hours
Permission: Read jobs; the workspace runs crews and the key's user has the Approve payroll permission
Payroll hours from crew clock-ins, per person, per day and job: regular, overtime and double-time, with overtime worked out per worker-week (the crew accounting Time entry report). Approved time only unless approved_only=false. start and end cover up to 93 days; with neither, this week. pay (dollars per line) is included only when the key's user may see actual pay (includes_pay).
{
"data": {
"start": "2026-10-05",
"end": "2026-10-11",
"approved_only": true,
"includes_pay": false,
"people": [
{
"user_id": 412,
"name": "Luis Ortega",
"total_hours": 46.5,
"lines": [
{"date": "2026-10-06", "job_id": 70412, "cost_code": "01-SEAL", "regular_hours": 9.5,
"overtime_hours": 0.0, "doubletime_hours": 0.0, "total_hours": 9.5}
]
}
],
"totals": {"regular_hours": 40.0, "overtime_hours": 6.5, "doubletime_hours": 0.0, "total_hours": 46.5}
}
}
job_id is null for shop or yard time. For the raw clock-ins, see Timesheets; for one job's costs and labor, see Job costing.
Month-end job cost#
GET /v1/reports/month-end-job-cost
Permission: Read jobs; the workspace runs crews and the key's user may see job costs (Estimating Pro and pricing access), as for Job Cost Out in the app
The crew accounting Month-end report: every job with revenue or direct cost in one month, with what was invoiced, the direct cost by type, gross profit and GP %, and the same job to date, plus a totals row. Use it to close the month or to reconcile against your accounting system job by job.
| Name | Type | Description |
|---|---|---|
month |
string | YYYY-MM. Default last month. |
- Revenue is the job's invoices dated in the month (by the day each was created, in your workspace's time zone). Draft, void, uncollectible and cancelled invoices are left out; a credit is a negative invoice and nets out.
- Direct cost is the job's Job Cost Out for the month: the same cost entries and not-yet-posted clock labor.
direct_costequals that report's period cost andcost_to_dateits cost to date through the last day of the month. direct_costssplits it by type:labor(labor & burden),material,trucking(truck rental),equipment,dump,subcontract(subs) andother. A cost lands by its category; an other cost on a cost code named for dumping (for example "Dump Fees") goes todump.gross_profitis revenue minus direct cost;gross_profit_pctis that over revenue as a fraction (0.2823= 28.23%),nullwith no revenue.contractis the revised contract (the job's value when it has no estimate).job_numberis the accounting job number when one is set.- Up to 200 jobs, by job number;
truncatedis true when there were more.
curl "https://trutec.ai/api/v1/reports/month-end-job-cost?month=2026-09" \
-H "X-API-Key: sk_your_api_key_here"
{
"data": {
"month": "2026-09",
"start": "2026-09-01",
"end": "2026-09-30",
"cost_types": [{"key": "labor", "label": "Labor & burden"}, {"key": "material", "label": "Material"}],
"jobs": [
{
"job_id": 70412, "job_number": "25995", "job_name": "Mission Plaza",
"customer": "Northgate Properties", "customer_number": "1140", "contract": 47350.0,
"revenue": 47350.0,
"direct_costs": {"labor": 14743.87, "material": 15566.96, "trucking": 0.0, "equipment": 3673.0,
"dump": 0.0, "subcontract": 0.0, "other": 0.0},
"direct_cost": 33983.83, "gross_profit": 13366.17, "gross_profit_pct": 0.282284,
"revenue_to_date": 47350.0, "cost_to_date": 33983.83,
"gross_profit_to_date": 13366.17, "gross_profit_pct_to_date": 0.282284
}
],
"totals": {"jobs": 1, "contract": 47350.0, "revenue": 47350.0, "direct_cost": 33983.83,
"gross_profit": 13366.17, "gross_profit_pct": 0.282284},
"truncated": false
}
}
totals carries every money field of a job row (summed), with GP % worked out from the sums. To look at one job's cost codes for the month, export its Job Cost Out: POST /v1/exports with kind: "job_cost_out", job_id, from and to. The same report as a spreadsheet is the month_end_job_cost export.