{"openapi": "3.1.0", "info": {"title": "DFX real estate intelligence", "version": "0.3.0", "description": "Machine readable United States real estate intelligence. NATIONAL: federal programme debt with maturity dates across 52 states, LIHTC compliance period endings, HUD subsidy contract expiries, distress and foreclosure flags, and commercial tenancy: who occupies a building, how much of it, and when the lease expires. MASSACHUSETTS ONLY: parcels, ownership and management relationships, and recorded sales. Building permits and certificates of occupancy are Boston only. Call dfx_coverage for the measured grid of every event family and the states each one covers."}, "servers": [{"url": "https://exchange-production-9123.up.railway.app", "description": "The public DFX exchange. No key for the free capabilities."}], "paths": {"/need": {"post": {"operationId": "need", "summary": "whether DFX can help, which capability to call, and a free sample", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"objective": {"type": "string", "description": "What you are trying to do, in one sentence and in your own words, for example 'commercial real estate loans in Ohio maturing in the next year'. A place named in the sentence is what the free sample is drawn from, so name one."}, "constraints": {"type": "object", "description": "Structured overrides for what was parsed out of `objective`, applied last so they outrank the prose. ONLY `state`, `event_type`, `within_days` and `limit` are honoured; any other key is ignored without warning.", "properties": {"state": {"type": "string", "description": "Two letter state code"}, "event_type": {"type": "string", "enum": ["BANKRUPTCY_EVENT", "CERTIFICATE_OF_OCCUPANCY", "COMPLIANCE_PERIOD_ENDING", "DEMOLITION_FILED", "DISTRESS_FLAG_RAISED", "FORECLOSURE_EVENT", "LEASE_EXPIRING", "LOAN_MATURITY_SCHEDULED", "LOAN_MODIFIED", "PERMIT_ISSUED", "PERMIT_STATUS_CHANGED", "PORTFOLIO_CONTRACTED", "PORTFOLIO_EXPANDED", "PROPERTY_SOLD", "SUBSIDY_CONTRACT_EXPIRING", "USE_CONVERSION_PERMITTED"], "description": "Same vocabulary as search_property_events"}, "within_days": {"type": "integer", "description": "Forward window in days"}, "limit": {"type": "integer", "description": "Max 50"}}}}}}}}, "responses": {"200": {"description": "whether DFX can help, which capability to call, and a free sample", "content": {"application/json": {"schema": {"type": "object"}}}}}}}, "/resolve": {"post": {"operationId": "resolve", "summary": "canonical DFX object ids with the match basis and ambiguity", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"value": {"type": "string", "description": "The street address when `kind` is 'address', the company name when it is 'organization'."}, "city": {"type": "string", "description": "City or town, for example 'Cambridge'. Optional: a one-line address carrying its own city and state is split here, so pass the whole line rather than splitting it yourself. Given explicitly it wins over anything parsed out of `address`."}, "state": {"type": "string", "description": "Two letter state code"}, "limit": {"type": "integer", "default": 10, "description": "Max 50. This is candidates for ONE address, not a page of a search. Raise it only when `address_group_size` on a result says several published records share the address."}, "kind": {"type": "string", "enum": ["address", "organization"], "description": "Which population to resolve against. MCP splits this into two tools; REST is one endpoint and this chooses."}}, "required": ["value"]}}}}, "responses": {"200": {"description": "canonical DFX object ids with the match basis and ambiguity", "content": {"application/json": {"schema": {"type": "object"}}}}}}}, "/verify": {"post": {"operationId": "verify", "summary": "current state, dated events, relationships, provenance", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"dfx_id": {"type": "string", "description": "A DFX id for a property OR a parcel. Both work and both come back in full. Sources: `dfx_id` on any object from resolve_address, `dfx_id` on any row from search_parcels, or the `verify.dfx_id` handle carried on every event a search returns."}}, "required": ["dfx_id"]}}}}, "responses": {"200": {"description": "current state, dated events, relationships, provenance", "content": {"application/json": {"schema": {"type": "object"}}}}}}}, "/search": {"post": {"operationId": "search", "summary": "dated events over properties, with provenance", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"event_type": {"type": "string", "enum": ["BANKRUPTCY_EVENT", "CERTIFICATE_OF_OCCUPANCY", "COMPLIANCE_PERIOD_ENDING", "DEMOLITION_FILED", "DISTRESS_FLAG_RAISED", "FORECLOSURE_EVENT", "LEASE_EXPIRING", "LOAN_MATURITY_SCHEDULED", "LOAN_MODIFIED", "PERMIT_ISSUED", "PERMIT_STATUS_CHANGED", "PORTFOLIO_CONTRACTED", "PORTFOLIO_EXPANDED", "PROPERTY_SOLD", "SUBSIDY_CONTRACT_EXPIRING", "USE_CONVERSION_PERMITTED"], "description": "ONE family per call. Omit it and every family is searched together, which mixes populations of very different sizes and is rarely what you want: name the family. The list is generated from what this server actually publishes today, so it grows without a release. Call dfx_coverage for how many of each family exist in a given state before reading an empty result as an absent market."}, "state": {"type": "string", "description": "Two letter state code"}, "within_days": {"type": "integer", "description": "FORWARD ONLY: it filters to events occurring between today and N days from now, and it cannot reach the past. For a backward-looking question (\"recent sales\", \"foreclosures that already happened\") OMIT this argument entirely. A historical event fails every forward window, so passing one returns an empty list that reads like an absent market. 548 is eighteen months. FORWARD FAMILIES, which this argument is for: COMPLIANCE_PERIOD_ENDING, LEASE_EXPIRING, LOAN_MATURITY_SCHEDULED, SUBSIDY_CONTRACT_EXPIRING. ENTIRELY HISTORICAL as of 2026-09-13, where ANY value returns nothing: BANKRUPTCY_EVENT, CERTIFICATE_OF_OCCUPANCY, DEMOLITION_FILED, DISTRESS_FLAG_RAISED, FORECLOSURE_EVENT, LOAN_MODIFIED, PERMIT_ISSUED, PORTFOLIO_CONTRACTED, PORTFOLIO_EXPANDED, PROPERTY_SOLD, USE_CONVERSION_PERMITTED."}, "limit": {"type": "integer", "default": 25, "description": "Max 50. Results are ordered by occurred_at ASCENDING. For the expiry families (maturities, compliance periods, subsidy contracts, leases) a call with no `within_days` now returns events dated TODAY OR LATER, soonest first; the envelope reports this as `applied_date_floor`. Pass `include_past=true` for the full history."}, "include_past": {"type": "boolean", "default": false, "description": "Return the whole tape including events already past, instead of the default forward window applied to expiry families. Has no effect when `within_days` is given, which sets its own window, and none on historical families, which are never floored."}, "cursor": {"type": "string", "description": "Continue a previous page. Pass the `next_cursor` returned by the last call, with EVERY other argument identical, to get the rows after it. Repeat until `next_cursor` is null, which is the only signal that the whole result set has been read: a short page is not the end, because a page can shrink when two sources publish the same event. Opaque; do not construct or edit one. An unreadable cursor is REFUSED rather than ignored, so a caller can never be silently restarted at page one."}}}}}}, "responses": {"200": {"description": "dated events over properties, with provenance", "content": {"application/json": {"schema": {"type": "object"}}}}}}}, "/occupancy": {"post": {"operationId": "occupancy", "summary": "who occupies a property, or where a company is observed to operate, with the evidence tier and the freshness of each claim", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"dfx_id": {"type": "string", "description": "One property, by the DFX id resolve_address or search_parcels returned. Answers who occupies it. SUFFICIENT ON ITS OWN, and the simplest way to call this."}, "company": {"type": "string", "description": "A tenant name, for example 'Starbucks'. NOT SUFFICIENT ALONE: it must be sent WITH `address`, and a bare company name is refused. This does not answer 'where does this company occupy space' across the country; it asks whether that tenant is recorded at that place."}, "address": {"type": "string", "description": "Street address including the house number. Required whenever you pass `company`, and not a substitute for `dfx_id` on its own."}, "city": {"type": "string", "description": "City or town. Optional, and it narrows an address that is ambiguous across municipalities."}, "state": {"type": "string", "description": "Two letter state code. Optional, same purpose as `city`."}, "limit": {"type": "integer", "default": 25, "description": "Max 50"}}}}}}, "responses": {"200": {"description": "who occupies a property, or where a company is observed to operate, with the evidence tier and the freshness of each claim", "content": {"application/json": {"schema": {"type": "object"}}}}}}}, "/changes": {"post": {"operationId": "changes", "summary": "events DFX has LEARNED since your cursor, ordered by learn time rather than by when they occurred", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"since": {"type": "string", "description": "Opaque cursor from a previous call. Omit on the first call to establish a position; that call returns no events by design."}, "event_type": {"type": "string", "enum": ["BANKRUPTCY_EVENT", "CERTIFICATE_OF_OCCUPANCY", "COMPLIANCE_PERIOD_ENDING", "DEMOLITION_FILED", "DISTRESS_FLAG_RAISED", "FORECLOSURE_EVENT", "LEASE_EXPIRING", "LOAN_MATURITY_SCHEDULED", "LOAN_MODIFIED", "PERMIT_ISSUED", "PERMIT_STATUS_CHANGED", "PORTFOLIO_CONTRACTED", "PORTFOLIO_EXPANDED", "PROPERTY_SOLD", "SUBSIDY_CONTRACT_EXPIRING", "USE_CONVERSION_PERMITTED"], "description": "One family. Same vocabulary as search_property_events."}, "state": {"type": "string", "description": "Two letter state code"}, "place_dfx_id": {"type": "string", "description": "Watch one property or parcel"}, "limit": {"type": "integer", "default": 50, "description": "Max 50. A full page sets `complete: false` in the envelope, which means the backlog was longer than one call: go again immediately with the returned cursor rather than waiting for your next poll interval. The cursor is the last row on the page, never the present moment, so nothing is skipped by doing so."}}}}}}, "responses": {"200": {"description": "events DFX has LEARNED since your cursor, ordered by learn time rather than by when they occurred", "content": {"application/json": {"schema": {"type": "object"}}}}}}}, "/have": {"post": {"operationId": "have", "summary": "acknowledgement and what DFX would need to know next", "requestBody": {"content": {"application/json": {"schema": {"type": "object"}}}}, "responses": {"200": {"description": "acknowledgement and what DFX would need to know next", "content": {"application/json": {"schema": {"type": "object"}}}}}}}}}