RFIs
Questions with a clock. Linked to the daily log that raised them and the change order they cause.
The rfi 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.
- numberintegerRFI number.
- subjectstringSubject line.
- statusenumdraft · open · answered · closed.
- ball_in_courtenumgc · architect · engineer · owner.
- questionstringThe question.
- answerstringOfficial response.
- due_datedateResponse due.
- cost_impactbooleanFlags a likely change order.
- change_ordersarrayResulting COs.
- 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.
rfi · sample data
object{17}
- id"rfi_212"id
- object"rfi"string
- project"prj_24118"id
- number212integer
- subject"Missing fire dampers at L3 corridor rated wall"string
- status"answered"string
- ball_in_court"gc"string
- question"Drawings show a 1-hr rated corridor wall at L3 with no fire dampers at duct penetrations. Confirm dampers required and spec."string
- answer"Provide UL 555 fire dampers at all rated penetrations per M-301. Price as change."string
- due_date"2026-09-23"date
- answered_at
- cost_impacttrueboolean
change_ordersarray[1]
- 0"co_14"id
- daily_log"dl_20260918"id
- created_at
- updated_at
- audit_trail"/v1/rfis/rfi_212/audit"string
GET
/v1/rfisList RFIs
Use overdue=true for the morning list.
Parameters
- projectstringqueryID of the project, e.g. prj_24118.
- statusenumqueryStatus filter.
- overduebooleanqueryPast due and unanswered.
- limitintegerqueryPage size, 1 to 100. Default 25.
- cursorstringqueryCursor from a previous page’s next_cursor.
Returns
A paginated list of rfi objects. Errors use the standard error shape.
Request
curl https://api.os.construction/v1/rfis \
-H "Authorization: Bearer $OS_API_KEY"const res = await fetch("https://api.os.construction/v1/rfis", {
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/rfis",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
},
)
res.raise_for_status()
data = res.json()Response · 200
{
"object": "list",
"url": "/v1/rfis",
"has_more": true,
"next_cursor": "cur_9f2c",
"data": [
{
"id": "rfi_212",
"object": "rfi",
"project": "prj_24118",
"number": 212,
"subject": "Missing fire dampers at L3 corridor rated wall",
"status": "answered",
"ball_in_court": "gc",
"question": "Drawings show a 1-hr rated corridor wall at L3 with no fire dampers at duct penetrations. Confirm dampers required and spec.",
"answer": "Provide UL 555 fire dampers at all rated penetrations per M-301. Price as change.",
"due_date": "2026-09-23",
"answered_at": "2026-09-22T19:02:00Z",
"cost_impact": true,
"change_orders": [
"co_14"
],
"daily_log": "dl_20260918",
"created_at": "2026-09-18T20:14:00Z",
"updated_at": "2026-09-22T19:02:00Z",
"audit_trail": "/v1/rfis/rfi_212/audit"
}
]
}POST
/v1/rfisCreate an RFI
Opens an RFI and starts the clock.
Idempotent. Send an Idempotency-Key; retries within 24 hours return the original result and never write twice.
Parameters
- Idempotency-KeystringheaderrequiredAny unique string. Replays return the first result instead of writing twice.
- projectstringbodyrequiredProject ID.
- subjectstringbodyrequiredSubject.
- questionstringbodyrequiredQuestion.
- due_datedatebodyDue date.
Returns
The rfi object. Errors use the standard error shape.
Request
curl -X POST https://api.os.construction/v1/rfis \
-H "Authorization: Bearer $OS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"project": "prj_24118",
"subject": "Missing fire dampers at L3 corridor rated wall",
"question": "Drawings show a 1-hr rated corridor wall at L3 with no fire dampers at duct penetrations. Confirm dampers required and spec.",
"due_date": "2026-09-23",
"daily_log": "dl_20260918"
}'const res = await fetch("https://api.os.construction/v1/rfis", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"project": "prj_24118",
"subject": "Missing fire dampers at L3 corridor rated wall",
"question": "Drawings show a 1-hr rated corridor wall at L3 with no fire dampers at duct penetrations. Confirm dampers required and spec.",
"due_date": "2026-09-23",
"daily_log": "dl_20260918"
}),
});
const obj = await res.json();import os, uuid, requests
res = requests.post(
"https://api.os.construction/v1/rfis",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"project": "prj_24118",
"subject": "Missing fire dampers at L3 corridor rated wall",
"question": "Drawings show a 1-hr rated corridor wall at L3 with no fire dampers at duct penetrations. Confirm dampers required and spec.",
"due_date": "2026-09-23",
"daily_log": "dl_20260918"
},
)
res.raise_for_status()
data = res.json()Response · 201
{
"id": "rfi_212",
"object": "rfi",
"project": "prj_24118",
"number": 212,
"subject": "Missing fire dampers at L3 corridor rated wall",
"status": "open",
"ball_in_court": "gc",
"question": "Drawings show a 1-hr rated corridor wall at L3 with no fire dampers at duct penetrations. Confirm dampers required and spec.",
"answer": null,
"due_date": "2026-09-23",
"answered_at": null,
"cost_impact": true,
"change_orders": [],
"daily_log": "dl_20260918",
"created_at": "2026-09-18T20:14:00Z",
"updated_at": "2026-09-22T19:02:00Z",
"audit_trail": "/v1/rfis/rfi_212/audit"
}Events
Webhooks fire on every state change of a rfi.