What it is
On many contracts the owner pays each progress billing less a percentage, the retainage (or retention). It is released later, often at substantial or final completion. The contractor usually also holds retainage from its subcontractors.
Retainage outstanding is what owners still hold. Retainage payable is what the contractor holds from subs.
Why operators track it
- Cash. Retainage can be a large share of what the contractor is owed.
- Closeout. Retainage on finished jobs often waits on punch list, closeout documents, or lien waivers.
- Working capital. Lenders and sureties look at receivables, and retainage is part of them.
- Net position. Comparing retainage receivable with retainage payable to subs shows the real exposure.
How it is calculated
Retainage outstanding = retainage billed − retainage collected
- Summed by job, then by customer and company.
- Age it two ways: days since billed, and days since the job was substantially complete.
- Show retainage payable to subs next to it for the net position.
Worked example
Four jobs hold $390,500 of retainage. Three are in progress. Job 2402 finished on June 30 and its $90,000 has been held 123 days since.
How to read it
- Retainage on open jobs is expected. Retainage on finished jobs is the actionable part.
- Watch net retainage. If the contractor holds similar amounts from subs, the cash exposure is smaller.
- Rates and release terms vary by contract and by state law. Read the contract rather than assuming a rate. We do not publish a benchmark.
- Reduced retainage. Some contracts lower the rate after a milestone. Check the rate in the data matches the contract.
Definitional choices and common pitfalls
- Inside AR or separate. Some reports include retainage in accounts receivable; others show it separately. Separate is clearer.
- Aging basis. Age from billing date or from completion. Completion is more useful for collection.
- Gross or net. Show receivable, payable, and the net.
- Retainage billed vs held. Some systems record retainage only when it is billed for release. Confirm what the field means.
How different roles use it
| Role | How they use it |
|---|---|
| CFO / controller | Tracks retainage by job and age, and chases release on finished jobs. |
| Project manager | Completes closeout items that hold up release. |
| Owner / CEO | Sees how much cash is tied up after jobs finish. |
| Surety / lender | Reviews receivables, including retainage, as part of working capital. |
Questions it answers
- How much cash is held back by owners?
- Which finished jobs still have retainage outstanding?
- What is our net retainage after what we hold from subs?
- Is retainage being billed for release on time?
How to present it in a report
- KPIs for total retainage outstanding, amount on completed jobs, and net after payable.
- One row per job with rate, billed, collected, outstanding, and days since completion.
- Highlight completed jobs older than your chosen threshold.
- A trend of total retainage outstanding by month.
| Job | Customer | Rate | Outstanding | Status | Days since completion |
|---|---|---|---|---|---|
| 2402 | Lakeview Schools | 10% | $90,000 | Complete | 123 |
| 2431 | County | 10% | $170,000 | In progress | |
| 2417 | Main St LLC | 10% | $110,000 | In progress | |
| 2420 | Pine Medical | 10% | $20,500 | In progress |
Data you need
Fields
| Field | What it means | Example value |
|---|---|---|
| Job and customer | Project and the owner who holds retainage | Job 2402, Lakeview Schools |
| Retainage rate | Contract percentage held | 10% |
| Retainage billed | Retainage withheld on billings to date | $90,000 |
| Retainage collected | Retainage paid by the owner | $0 |
| Completion date | Substantial or final completion | Jun 30 |
| Retainage payable | Retainage held from subcontractors on the job | $61,000 |
What the raw data looks like
Example data A few rows as they might come out of the source system.
| Job | Rate | Billed to date | Retainage billed | Collected | Status | Completed |
|---|---|---|---|---|---|---|
| 2417 | 10% | $1,100,000 | $110,000 | $0 | In progress | |
| 2420 | 10% | $205,000 | $20,500 | $0 | In progress | |
| 2431 | 10% | $1,700,000 | $170,000 | $0 | In progress | |
| 2402 | 10% | $900,000 | $90,000 | $0 | Complete | Jun 30 |
Where this data usually lives
- Construction ERP and accounting holds job cost, budgets, billing, AP, and AR. For example: Sage 300 Construction and Real Estate, Viewpoint Vista (Trimble), and Foundation. Some smaller contractors use general accounting software such as QuickBooks with jobs set up as projects.
- Project management often holds change orders, commitments, and project documents. For example: Procore.
The same field can live in more than one system. Agree with the customer which one is the source of truth.
Common data gaps and traps
- Net billings only. If only net invoices are stored, retainage must be derived from the rate.
- Completion date missing. Many systems have job status but not the date it changed.
- Release billed as a new invoice. Retainage release may be a separate invoice that is hard to link back.
- Payable side in a different module. Subcontract retainage often lives in AP or a subcontract module.
Related metrics
- WIP over/under billing: billed to date includes retainage.
- Cash flow by job: retainage is part of the gap between billed and collected.
- Subcontractor compliance: lien waivers often gate release.
FAQ
- Is retainage an asset?
- Retainage receivable is usually shown as part of receivables. Confirm presentation with a CPA.
- What is a typical retainage rate?
- It depends on the contract and state rules. We do not publish a figure without a source.
- What holds up retainage release?
- Commonly punch list items, closeout documents, and lien waivers. Check the contract terms.
For your data teamFormula variants, tables, SQL sketch, and dashboard spec
Formula details
retainage_outstanding = SUM(retainage_billed) - SUM(retainage_collected)
days_since_completion = :as_of - completion_date
net_retainage = retainage_outstanding - retainage_payable
Worked example detail
| Job | Outstanding | Days since completion |
|---|---|---|
| 2417 | $110,000 | |
| 2420 | $20,500 | |
| 2431 | $170,000 | |
| 2402 | $90,000 | 123 |
| Total | $390,500 |
Tables and fields
| Table | Fields | Notes |
|---|---|---|
ar_invoices |
invoice_id, job_id, invoice_date, gross_amount, retainage_amount | |
ar_payments |
invoice_id, paid_at, amount, applied_to_retainage | |
jobs |
job_id, customer_id, status, completion_date, retainage_rate |
SQL sketch
SELECT j.job_id, j.completion_date,
SUM(i.retainage_amount) - COALESCE(SUM(p.retainage_paid),0) AS retainage_outstanding,
CASE WHEN j.completion_date IS NOT NULL THEN :as_of - j.completion_date END AS days_since_completion
FROM jobs j JOIN ar_invoices i USING (job_id)
LEFT JOIN (SELECT invoice_id, SUM(amount) AS retainage_paid FROM ar_payments WHERE applied_to_retainage GROUP BY 1) p USING (invoice_id)
GROUP BY j.job_id, j.completion_date;
Build spec
Copy this into a ticket. It lists the grain, inputs, logic, and edge cases.
metric: construction.retainage_outstanding
grain: one row per job
inputs:
- retainage_amount per invoice
- payments applied to retainage
- completion_date
- retainage payable
logic:
- outstanding = billed retainage - collected retainage
- days_since_completion = as_of - completion_date
- net = outstanding - payable
edge_cases:
- retainage derived from rate when not stored: label as derived
- partial release: apply payment to retainage, not to progress
- job reopened: keep original completion date
As a dashboard card
- KPI for total and completed-job retainage, a bar per job colored by status, and a table sorted by days since completion.
Common mistakes
- Hiding retainage inside AR so it is never chased.
- Aging from invoice date only.
- Ignoring retainage payable to subs.