{"openapi":"3.0.3","info":{"title":"ForVue Partner API","version":"1.2.0","description":"Predictive maintenance intelligence for multifamily real estate. USPTO Provisional 64/032,704 (pending). Six write endpoints support the Idempotency-Key header for safe retry: POST /v1/properties, POST /v1/appliances, POST /v1/appliances/bulk, POST /v1/service-events/bulk, POST /v1/property/{id}/claims, POST /v1/meter-event. PUT / DELETE endpoints and the two smart-meter variants do NOT honor Idempotency-Key — retries may double-apply.","contact":{"email":"wise@investwisecap.com","url":"https://forvue.io"}},"servers":[{"url":"https://platform.investwisecap.com/api","description":"Production"}],"security":[{"apiKey":[]},{"oauth2":[]}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"X-Api-Key"},"oauth2":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"/oauth/token","scopes":{}}}}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Optional client-supplied key to make the request idempotent. When present, the server caches the response for 24 hours and returns the cached response on any subsequent request with the same key. Applies only to the endpoints where this parameter is documented; other write endpoints do not honor it."},"Sandbox":{"name":"sandbox","in":"query","required":false,"schema":{"type":"string","enum":["true"]},"description":"When set to \"true\" on a read endpoint, restricts the response to demo properties (is_demo=true) only, letting partners test integrations without touching real portfolios. Same authentication, same rate limits, same response shape. Composes with owner scoping (a partner scoped to owner X sees only X's demo data). Sandbox is READ-ONLY: any write endpoint called with ?sandbox=true returns HTTP 400 with error code sandbox_write_forbidden. To seed test data, sign in as an operator and call POST /onboarding/demo-property, then integrate against ?sandbox=true reads."}}},"paths":{"/v1/ping":{"get":{"summary":"Health check","responses":{"200":{"description":"OK"}}}},"/v1/openapi.json":{"get":{"summary":"This OpenAPI 3.0 specification","security":[],"responses":{"200":{"description":"OpenAPI document"}}}},"/v1/auth/methods":{"get":{"summary":"List available authentication methods and how to obtain credentials (public — no key required)","security":[],"responses":{"200":{"description":"Auth methods description"}}}},"/v1/properties":{"get":{"summary":"List properties","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Paginated property list"}}},"post":{"summary":"Create property. Safe to retry with Idempotency-Key header.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","address","state"],"properties":{"name":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"unit_count":{"type":"integer"}}}}}}}},"/v1/properties/{id}":{"put":{"summary":"Update property. NOT idempotency-safe — retries may double-apply.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/properties/bulk":{"put":{"summary":"Update multiple properties (max 50). NOT idempotency-safe."}},"/v1/portfolio/summary":{"get":{"summary":"All properties with risk summaries in one call"}},"/v1/property/{id}/summary":{"get":{"summary":"Property risk summary with financials"}},"/v1/property/{id}/report":{"get":{"summary":"Full property report with component details"}},"/v1/property/{id}/forecast":{"get":{"summary":"30 / 60 / 90 day failure forecast"}},"/v1/property/{id}/reserve":{"get":{"summary":"Reserve fund recommendations — four fixed strategies (aggressive_12_month, standard_24_month, conservative_36_month, full_replacement_10yr). standard_24_month is the recommended default."}},"/v1/property/{id}/condition-score":{"get":{"summary":"Single 1–100 condition score for lender underwriting (NSPIRE-aligned). Returns null score/grade when components_assessed is 0 — partners must handle null; do NOT coerce to 0."}},"/v1/property/{id}/rcv":{"get":{"summary":"Replacement cost valuation for insurance carriers"}},"/v1/property/{id}/nspire":{"get":{"summary":"Predicted NSPIRE deficiency assessment (forward-looking estimate — NOT an official HUD inspection)"}},"/v1/property/{id}/compliance":{"get":{"summary":"Component-level compliance status against NSPIRE baselines and property-type compliance schedules"}},"/v1/components":{"get":{"summary":"List scored components. component_name field returns Knowledge Base slugs (e.g. hvac_filter, wh_gas_anode), not display prose.","parameters":[{"name":"property_id","in":"query","schema":{"type":"string"}},{"name":"tier","in":"query","schema":{"type":"string","enum":["CRITICAL","WARNING","GOOD"]}},{"name":"limit","in":"query","schema":{"type":"integer"}},{"name":"offset","in":"query","schema":{"type":"integer"}}]}},"/v1/components/{id}/history":{"get":{"summary":"Component risk score history — discrete score-change events"}},"/v1/components/{id}/trend":{"get":{"summary":"Time-series risk score trend for a component (daily snapshots for charting)"}},"/v1/alerts":{"get":{"summary":"Active CRITICAL and WARNING components across the portfolio"}},"/v1/events":{"get":{"summary":"Change event stream for incremental sync. Event types include component.critical, component.warning, component.recovered, service_event.created.","parameters":[{"name":"since","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}]}},"/v1/appliances":{"post":{"summary":"Create single appliance + auto-generate components. Safe to retry with Idempotency-Key header.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}]}},"/v1/appliances/bulk":{"post":{"summary":"Create multiple appliances. Safe to retry with Idempotency-Key header.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}]}},"/v1/appliances/{id}":{"put":{"summary":"Update appliance. NOT idempotency-safe.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]},"delete":{"summary":"Delete appliance. Not ledger-idempotent, but deletion is naturally idempotent by result (subsequent DELETE returns 404).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/appliances/{id}/photos":{"get":{"summary":"Get data plate photos for an appliance","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/v1/service-events/bulk":{"post":{"summary":"Create multiple service events (max 100). Safe to retry with Idempotency-Key header.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}]}},"/v1/property/{id}/claims":{"get":{"summary":"List insurance claims for a property"},"post":{"summary":"Import insurance claims (batch). Safe to retry with Idempotency-Key header.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}]}},"/v1/property/{id}/claims/impact":{"get":{"summary":"Show how each claim affects risk scoring with recency decay. Returns per-claim effective_likelihood_ratio; the underlying scoring parameters are trade-secret and not emitted."}},"/v1/meter-event":{"post":{"summary":"Submit IoT sensor event. Safe to retry with Idempotency-Key header.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}]}},"/v1/meter-event/smart":{"post":{"summary":"Smart meter event with auto-scoring and auto-creation of properties / appliances. NOT idempotency-safe — fires webhook side-effects on every call. Partners must dedupe client-side."}},"/v1/meter-event/smart/v2":{"post":{"summary":"Smart meter v2 with callbacks and webhooks. NOT idempotency-safe."}},"/oauth/token":{"post":{"summary":"Exchange client_id + client_secret for a bearer token (client_credentials grant). Bearer tokens expire in 1 hour. Public endpoint — no auth required to call.","security":[]}},"/oauth/token/info":{"get":{"summary":"Introspect a bearer token — returns the associated partner and expiry."}}}}