Skip to main content
GET
Retrieve an underwriting run (poll for status)

Authorizations

Authorization
string
header
required

Partner API key issued by Cactus.

Path Parameters

underwriting_id
string
required

Query Parameters

wait
integer
default:0

Seconds to hold the request open waiting for the job to reach a terminal state (long polling). 0 (the default) returns the current state immediately.

The response returns as soon as the job completes or fails, so a short job costs one request instead of a sleep loop. If the budget runs out first the request still returns 200 with the job's current state — a still-running job is not an error. Check status and call again.

Capped at 45 seconds, below the 60-second request timeout.

Required range: 0 <= x <= 45

Response

Current run state; report link and summary when complete.

id
string
required
Example:

"uw_01J9ZK3M"

object
string
required
Allowed value: "underwriting"
status
enum<string>
required

queued → ingesting (reading the documents and the market) → underwriting (analysis) → calculating (the financial model) → completed. progress.stage carries the finer-grained pipeline step behind each.

Available options:
queued,
ingesting,
underwriting,
calculating,
completed,
failed
sandbox
boolean
required
created_at
string<date-time>
required
progress
object
property
object

Echo of the submitted property.

end_user_ref
string

Echo of the submitted end-user identifier, when one was supplied.

version
integer

Report version. 1 for a first run; a confirmed change creates a new underwriting with the next number.

previous_underwriting_id
string

The version this one changed (absent on version 1).

documents
object[]

Echo of the submitted selection, with each role settled.

options
object

Echo of the submitted options, with defaults applied.

billed_total_usd
string
completed_at
string<date-time>
elapsed_seconds
integer

Whole seconds since created_at. Present while the run is active, so a caller can tell a run that just started from one approaching the 4-hour ceiling without doing date math.

Example:

640

poll_after_seconds
integer

Recommended seconds to wait before polling again. Present while the run is active; mirrors the Retry-After header. Prefer this over a hard-coded interval.

Example:

30

report
object

Present when completed. Private delivery omits url.

summary
object

Headline metrics as plain numbers, lifted out of results for convenience: value_base_usd, cap_rate, normalized_noi_usd, levered_irr, dscr, equity_multiple, total_units and so on, plus readiness_status and disclosure_count.

A metric the run could not establish is absent, never null — the reason it is missing is in results, beside the figure.

results
object

The whole report, in the same shape the public report page is rendered from (broker-public-underwriting-delivery-v1). Present on a completed run when fetched individually; omitted from list responses and webhook payloads, which would otherwise carry megabytes.

Every figure is a {state, value, reason} triple, because the product distinguishes a known zero from something it could not establish. state is one of known, zero, unavailable, not_applicable; when it is not known or zero, value is null and reason says why in a sentence you can show a user. Collections carry {state, items, reason}, where state is available, partial, confirmed_empty or unavailable — "we found none" and "we could not look" are different answers.

Top level: schema_version, generated_at, property, chapters, critical_assumptions, readiness, release.

The chapters follow the argument the report makes: answer (what it is worth, and the cap rate and NOI behind that), income (unit mix and rent comparables), revenue (the trailing-twelve income statement and the year-one revenue waterfall), cost (the expense statement and the normalizations applied to it), value (sale comparables and a cap-rate sensitivity grid), buyer (returns, financing and the annual cash-flow table), risk (market watch), next (talking points).

readiness.disclosures is the part to surface next to any figure you quote: each names something the run had to assume or could not establish, with copy written to be shown to an end user. critical_assumptions.items ranks the assumptions the valuation is most sensitive to, with the dollar and IRR impact of moving each.

error
object