Budgets
One budget line per project and cost code. Original, approved changes, committed, actual and cost-to-complete in one row.
The budget_line object
Every object carries an id, timestamps, and an audit_trail. Money is integer cents. Expand any node in the explorer to see how it links to the rest of the record.
- idstringUnique, stable identifier. Prefixed by object type.
- objectstringString naming the object type.
- projectstringProject ID.
- cost_codestringCost code ID.
- originalintegerOriginal budget, cents.
- approved_changesintegerFrom approved change orders, cents.
- revisedintegeroriginal + approved_changes.
- committedintegerSum of commitment lines on this code.
- cost_to_dateintegerPosted cost.
- cost_to_completeintegerForecast remaining spend.
- projected_finalintegercost_to_date + cost_to_complete.
- varianceintegerrevised − projected_final. Negative means fade.
- created_attimestampISO 8601, UTC.
- updated_attimestampISO 8601, UTC. Changes on every write.
- audit_trailstringPath to the append-only history of who changed what, and when.
budget_line · sample data
object{15}
- id"bud_24118_233300"id
- object"budget_line"string
- project"prj_24118"id
- cost_code"cc_233300"id
- original21400000integer$214,000.00
- approved_changes8640000integer$86,400.00
- revised30040000integer$300,400.00
- committed28720000integer$287,200.00
- cost_to_date19310000integer$193,100.00
- cost_to_complete10730000integer$107,300.00
- projected_final30040000integer$300,400.00
- variance0integer$0.00
- created_at
- updated_at
- audit_trail"/v1/budgets/bud_24118_233300/audit"string
GET
/v1/budgetsList budget lines
All lines for a project.
Parameters
- projectstringqueryrequiredID of the project, e.g. prj_24118.
- over_budgetbooleanqueryOnly lines with negative variance.
- limitintegerqueryPage size, 1 to 100. Default 25.
- cursorstringqueryCursor from a previous page’s next_cursor.
Returns
A paginated list of budget_line objects. Errors use the standard error shape.
Request
curl https://api.os.construction/v1/budgets \
-H "Authorization: Bearer $OS_API_KEY"const res = await fetch("https://api.os.construction/v1/budgets", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
},
});
const page = await res.json();import os, uuid, requests
res = requests.get(
"https://api.os.construction/v1/budgets",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
},
)
res.raise_for_status()
data = res.json()Response · 200
{
"object": "list",
"url": "/v1/budgets",
"has_more": false,
"next_cursor": null,
"data": [
{
"id": "bud_24118_233300",
"object": "budget_line",
"project": "prj_24118",
"cost_code": "cc_233300",
"original": 21400000,
"approved_changes": 8640000,
"revised": 30040000,
"committed": 28720000,
"cost_to_date": 19310000,
"cost_to_complete": 10730000,
"projected_final": 30040000,
"variance": 0,
"created_at": "2026-01-12T15:10:00Z",
"updated_at": "2026-09-30T21:18:42Z",
"audit_trail": "/v1/budgets/bud_24118_233300/audit"
}
]
}PATCH
/v1/budgets/{id}Update cost-to-complete
Re-forecast a line. Writes an audit entry with your reason.
Idempotent. Send an Idempotency-Key; retries within 24 hours return the original result and never write twice.
Parameters
- idstringpathrequiredThe budget line ID, e.g. bud_24118_233300.
- Idempotency-KeystringheaderrequiredAny unique string. Replays return the first result instead of writing twice.
- cost_to_completeintegerbodyrequiredNew forecast, cents.
- reasonstringbodyrequiredWhy it moved. Stored on the audit trail.
Returns
The budget_line object. Errors use the standard error shape.
Request
curl -X PATCH https://api.os.construction/v1/budgets/bud_24118_233300 \
-H "Authorization: Bearer $OS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"cost_to_complete": 10730000,
"reason": "CO #14 dampers added"
}'const res = await fetch("https://api.os.construction/v1/budgets/bud_24118_233300", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"cost_to_complete": 10730000,
"reason": "CO #14 dampers added"
}),
});
const obj = await res.json();import os, uuid, requests
res = requests.patch(
"https://api.os.construction/v1/budgets/bud_24118_233300",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"cost_to_complete": 10730000,
"reason": "CO #14 dampers added"
},
)
res.raise_for_status()
data = res.json()Response · 200
{
"id": "bud_24118_233300",
"object": "budget_line",
"project": "prj_24118",
"cost_code": "cc_233300",
"original": 21400000,
"approved_changes": 8640000,
"revised": 30040000,
"committed": 28720000,
"cost_to_date": 19310000,
"cost_to_complete": 10730000,
"projected_final": 30040000,
"variance": 0,
"created_at": "2026-01-12T15:10:00Z",
"updated_at": "2026-09-30T21:18:42Z",
"audit_trail": "/v1/budgets/bud_24118_233300/audit"
}Events
Webhooks fire on every state change of a budget_line.