Projects
A job. Every other object hangs off a project, so one ID gets you the whole record: budget, commitments, changes, billing.
The project 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.
- numberstringYour job number, e.g. 24-118.
- namestringDisplay name.
- statusenumbid · active · closeout · closed.
- contract_amountintegerOriginal contract, cents.
- approved_changesintegerSum of approved change orders, cents.
- revised_contractintegercontract_amount + approved_changes.
- cost_to_dateintegerPosted job cost, cents.
- percent_completenumberCost-to-cost percent complete.
- projected_margin_pctnumberForecast margin at completion.
- retainage_pctnumberDefault retainage held on billings.
- 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.
object{17}
- id"prj_24118"id
- object"project"string
- number"24-118"string
- name"Riverside Medical Center — Phase 2"string
- status"active"string
- owner"own_riverside_health"id
- contract_amount4820000000integer$48,200,000.00
- approved_changes110000000integer$1,100,000.00
- revised_contract4930000000integer$49,300,000.00
- cost_to_date3170000000integer$31,700,000.00
- percent_complete66integer
- projected_margin_pct11.4number
- retainage_pct10integer
- currency"usd"string
- created_at
- updated_at
- audit_trail"/v1/projects/prj_24118/audit"string
/v1/projectsList projects
Returns projects you can see, newest first.
Parameters
- statusenumqueryFilter by status.
- limitintegerqueryPage size, 1 to 100. Default 25.
- cursorstringqueryCursor from a previous page’s next_cursor.
Returns
A paginated list of project objects. Errors use the standard error shape.
curl https://api.os.construction/v1/projects \
-H "Authorization: Bearer $OS_API_KEY"const res = await fetch("https://api.os.construction/v1/projects", {
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/projects",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
},
)
res.raise_for_status()
data = res.json(){
"object": "list",
"url": "/v1/projects",
"has_more": true,
"next_cursor": "cur_9f2c",
"data": [
{
"id": "prj_24118",
"object": "project",
"number": "24-118",
"name": "Riverside Medical Center — Phase 2",
"status": "active",
"owner": "own_riverside_health",
"contract_amount": 4820000000,
"approved_changes": 110000000,
"revised_contract": 4930000000,
"cost_to_date": 3170000000,
"percent_complete": 66,
"projected_margin_pct": 11.4,
"retainage_pct": 10,
"currency": "usd",
"created_at": "2026-01-12T15:04:00Z",
"updated_at": "2026-09-30T21:18:42Z",
"audit_trail": "/v1/projects/prj_24118/audit"
}
]
}/v1/projects/{id}Retrieve a project
One project with live rollups.
Parameters
- idstringpathrequiredThe project ID, e.g. prj_24118.
Returns
The project object. Errors use the standard error shape.
curl https://api.os.construction/v1/projects/prj_24118 \
-H "Authorization: Bearer $OS_API_KEY"const res = await fetch("https://api.os.construction/v1/projects/prj_24118", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
},
});
const obj = await res.json();import os, uuid, requests
res = requests.get(
"https://api.os.construction/v1/projects/prj_24118",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
},
)
res.raise_for_status()
data = res.json(){
"id": "prj_24118",
"object": "project",
"number": "24-118",
"name": "Riverside Medical Center — Phase 2",
"status": "active",
"owner": "own_riverside_health",
"contract_amount": 4820000000,
"approved_changes": 110000000,
"revised_contract": 4930000000,
"cost_to_date": 3170000000,
"percent_complete": 66,
"projected_margin_pct": 11.4,
"retainage_pct": 10,
"currency": "usd",
"created_at": "2026-01-12T15:04:00Z",
"updated_at": "2026-09-30T21:18:42Z",
"audit_trail": "/v1/projects/prj_24118/audit"
}/v1/projects/{id}Update a project
Change name, status or default retainage. Money rollups are computed and read-only.
Idempotent. Send an Idempotency-Key; retries within 24 hours return the original result and never write twice.
Parameters
- idstringpathrequiredThe project ID, e.g. prj_24118.
- Idempotency-KeystringheaderrequiredAny unique string. Replays return the first result instead of writing twice.
- statusenumbodyNew status.
Returns
The project object. Errors use the standard error shape.
curl -X PATCH https://api.os.construction/v1/projects/prj_24118 \
-H "Authorization: Bearer $OS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"status": "closeout"
}'const res = await fetch("https://api.os.construction/v1/projects/prj_24118", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"status": "closeout"
}),
});
const obj = await res.json();import os, uuid, requests
res = requests.patch(
"https://api.os.construction/v1/projects/prj_24118",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"status": "closeout"
},
)
res.raise_for_status()
data = res.json(){
"id": "prj_24118",
"object": "project",
"number": "24-118",
"name": "Riverside Medical Center — Phase 2",
"status": "closeout",
"owner": "own_riverside_health",
"contract_amount": 4820000000,
"approved_changes": 110000000,
"revised_contract": 4930000000,
"cost_to_date": 3170000000,
"percent_complete": 66,
"projected_margin_pct": 11.4,
"retainage_pct": 10,
"currency": "usd",
"created_at": "2026-01-12T15:04:00Z",
"updated_at": "2026-09-30T21:18:42Z",
"audit_trail": "/v1/projects/prj_24118/audit"
}Events
Webhooks fire on every state change of a project.