{"openapi":"3.1.0","info":{"title":"Swath API","version":"1.0","description":"Verified severe-weather intelligence. Swath detects hail storms from live NEXRAD/MRMS radar and damaging-wind events from measured NWS storm reports, builds verified damage swath polygons, and joins them against parcel data to answer: which properties were hit, and how hard. Authentication: every /v1 endpoint takes `Authorization: Bearer sk_...`. Self-serve keys via POST /v1/signup (free tier: 100 credits/month, 60 requests/min). Credit costs per successful call: Swath Report (GET /v1/swaths/{id}/properties) = 1 credit per property returned plus 25 credits per record fetched fresh from the parcel vendor, 10-credit minimum; report quote (GET /v1/swaths/{id}/properties/quote) = 1 credit; property lookup (GET /v1/property) = 2 credits; billing endpoints = 0; everything else = 1 credit. Failed requests are never billed. Paid plans: developer (40,000 credits/mo) and scale (600,000 credits/mo) via POST /v1/billing/checkout."},"servers":[{"url":"https://swathapi.com"}],"security":[{"ApiKeyAuth":[]}],"paths":{"/v1/storms":{"get":{"operationId":"find_storms","summary":"List detected storms (one row per storm/swath) with optional time, area, and type filters","description":"Returns radar-detected storms, newest first. Each row pairs the storm with its damage swath summary. Costs 1 credit.","parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"string"},"description":"ISO 8601 timestamp; only storms last seen at or after this time (e.g. 2026-07-18 or 2026-07-18T00:00:00Z)"},{"name":"bbox","in":"query","required":false,"schema":{"type":"string"},"description":"Bounding box as \"west,south,east,north\" in WGS84 degrees, e.g. -103.0,25.8,-93.5,36.5 for Texas"},{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["hail","wind"]},"description":"Filter by storm type"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":500},"description":"Max rows (default 100, cap 500)"}],"responses":{"200":{"description":"Matching storms","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StormSwath"}}}}}}},"400":{"description":"Invalid since/type/bbox parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/swaths/{id}":{"get":{"operationId":"get_swath","summary":"Get one swath summary by swath id (swath_...) or storm id (sw_...)","description":"Costs 1 credit.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Swath id (swath_...) or its storm id (sw_...) — both work"}],"responses":{"200":{"description":"Swath summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwathDetail"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such swath","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/swaths/{id}/geometry":{"get":{"operationId":"get_swath_geometry","summary":"Get the swath damage footprint as a GeoJSON Feature (MultiPolygon)","description":"Costs 1 credit. Geometry can be large for big storms.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Swath id (swath_...) or storm id (sw_...)"}],"responses":{"200":{"description":"GeoJSON Feature with the swath polygon","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwathGeometryFeature"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such swath","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/swaths/{id}/properties":{"get":{"operationId":"get_swath_report","summary":"Swath Report: affected properties inside a swath with per-property hail size and exposure score (flagship, billed per record)","description":"Properties in a swath with exposure.hail_in and exposure.score (0-1), sorted by score then value. Bills 1 credit per property returned +25 per fresh-fetched record (10 min) — preview via the quote endpoint. Free plan is cached-only (coverage.vendor_fetch = \"skipped_free_plan\").","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Swath id (swath_...) or storm id (sw_...)"},{"name":"roof_age_min","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum estimated roof age in years"},{"name":"roof_material","in":"query","required":false,"schema":{"type":"string"},"description":"Exact roof material match, e.g. asphalt_shingle"},{"name":"owner_occupied","in":"query","required":false,"schema":{"type":"boolean"},"description":"true = owner-occupied only, false = non-owner-occupied only"},{"name":"year_built_before","in":"query","required":false,"schema":{"type":"number"},"description":"Built strictly before this year"},{"name":"year_built_after","in":"query","required":false,"schema":{"type":"number"},"description":"Built strictly after this year"},{"name":"value_min","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum assessed value (USD)"},{"name":"value_max","in":"query","required":false,"schema":{"type":"number"},"description":"Maximum assessed value (USD)"},{"name":"min_exposure_score","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum exposure score, 0 to 1"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":1000},"description":"Page size (default 100, cap 1000)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0},"description":"Pagination offset"}],"responses":{"200":{"description":"Report with matched properties","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwathReport"}}}},"400":{"description":"A filter parameter is not a number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such swath","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/swaths/{id}/properties/quote":{"get":{"operationId":"get_report_quote","summary":"Cost preview for the Swath Report: filtered cached-property count and fresh-fetch estimate, no rows returned (1 credit)","description":"Runs the report's filtered COUNT over cached parcels (1 credit, no property rows). Returns total_cached, whether fresh vendor fetches are possible, and a clearly-labeled fresh_estimate (null when no basis). Billing of the real report always uses actual counts.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Swath id (swath_...) or storm id (sw_...)"},{"name":"roof_age_min","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum estimated roof age in years"},{"name":"roof_material","in":"query","required":false,"schema":{"type":"string"},"description":"Exact roof material match, e.g. asphalt_shingle"},{"name":"owner_occupied","in":"query","required":false,"schema":{"type":"boolean"},"description":"true = owner-occupied only, false = non-owner-occupied only"},{"name":"year_built_before","in":"query","required":false,"schema":{"type":"number"},"description":"Built strictly before this year"},{"name":"year_built_after","in":"query","required":false,"schema":{"type":"number"},"description":"Built strictly after this year"},{"name":"value_min","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum assessed value (USD)"},{"name":"value_max","in":"query","required":false,"schema":{"type":"number"},"description":"Maximum assessed value (USD)"},{"name":"min_exposure_score","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum exposure score, 0 to 1"}],"responses":{"200":{"description":"Cost preview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportQuote"}}}},"400":{"description":"A filter parameter is not a number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such swath","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/swaths/{id}/email-report":{"post":{"operationId":"email_report","summary":"Email a swath report summary to your account email (summary 1 credit; with limit, billed like the report)","description":"Sends a branded report summary (max hail/wind, area, county, property count, storm link) to the account email your key was signed up with — no other recipients. Pass limit to include the top N properties, billed like the report endpoint. Keys without an account email get a 400.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Swath id (swath_...) or storm id (sw_...)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50},"description":"Include the top N properties by exposure (billed like the report, 10-credit minimum); omit for a 1-credit summary"},{"name":"roof_age_min","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum estimated roof age in years"},{"name":"roof_material","in":"query","required":false,"schema":{"type":"string"},"description":"Exact roof material match, e.g. asphalt_shingle"},{"name":"owner_occupied","in":"query","required":false,"schema":{"type":"boolean"},"description":"true = owner-occupied only, false = non-owner-occupied only"},{"name":"year_built_before","in":"query","required":false,"schema":{"type":"number"},"description":"Built strictly before this year"},{"name":"year_built_after","in":"query","required":false,"schema":{"type":"number"},"description":"Built strictly after this year"},{"name":"value_min","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum assessed value (USD)"},{"name":"value_max","in":"query","required":false,"schema":{"type":"number"},"description":"Maximum assessed value (USD)"},{"name":"min_exposure_score","in":"query","required":false,"schema":{"type":"number"},"description":"Minimum exposure score, 0 to 1"}],"responses":{"200":{"description":"Report emailed to the account address"},"400":{"description":"No account email on file, or a filter parameter is not a number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such swath","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Email send failed — nothing billed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Email sending not configured on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/property":{"get":{"operationId":"lookup_property","summary":"Look up one property record by address, lat+lng, or parcel_id (2 credits)","description":"Provide exactly one of: address, lat AND lng, or parcel_id. Costs 2 credits. On the free plan only cached records are returned (a cache miss is a 404 explaining that live vendor coverage requires a paid plan).","parameters":[{"name":"address","in":"query","required":false,"schema":{"type":"string"},"description":"Street address, e.g. \"1801 N Pearl St, Dallas, TX\""},{"name":"lat","in":"query","required":false,"schema":{"type":"number"},"description":"Latitude (use with lng)"},{"name":"lng","in":"query","required":false,"schema":{"type":"number"},"description":"Longitude (use with lat)"},{"name":"parcel_id","in":"query","required":false,"schema":{"type":"string"},"description":"County parcel id"}],"responses":{"200":{"description":"Property record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Property"}}}},"400":{"description":"Missing/invalid lookup parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No property record found (on free plan: no cached record)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/monitors":{"get":{"operationId":"list_monitors","summary":"List active storm monitors for this account","description":"Costs 1 credit.","responses":{"200":{"description":"Monitors","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Monitor"}}}}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"create_monitor","summary":"Create a storm monitor: email and/or webhook alerts when a qualifying storm enters an area","description":"Provide an area (GeoJSON geometry or bbox) plus at least one alert channel: webhook_url and/or email_alerts: true (emails go only to the account owner's address). hail_min_in defaults to 1.00. With webhook_url, the response includes webhook_secret exactly once. Costs 1 credit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Human label for the monitor"},"geometry":{"type":"object","description":"GeoJSON Polygon or MultiPolygon in WGS84 (alternative to bbox)"},"bbox":{"type":"array","items":{"type":"number"},"minItems":4,"maxItems":4,"description":"[west, south, east, north] in WGS84 degrees (alternative to geometry)"},"hail_min_in":{"type":"number","description":"Minimum hail size in inches to trigger (default 1.00)"},"wind_min_mph":{"type":"number","description":"Minimum wind speed in mph to trigger"},"webhook_url":{"type":"string","description":"http(s) URL that receives signed alert POSTs (optional if email_alerts is true)"},"email_alerts":{"type":"boolean","description":"Email storm alerts to the account owner's address — no other recipient is possible (default false)"}}}}}},"responses":{"201":{"description":"Monitor created (webhook_secret shown only in this response)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorWithSecret"}}}},"400":{"description":"Missing area, or neither webhook_url nor email_alerts provided","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/usage":{"get":{"operationId":"get_usage","summary":"Current plan, credits used this month, and limits for the calling API key","description":"Costs 1 credit.","responses":{"200":{"description":"Usage summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/billing/checkout":{"post":{"operationId":"create_checkout","summary":"Create a Stripe checkout link to upgrade to a paid plan (0 credits, works even when over limit)","description":"Returns a Stripe-hosted checkout URL for the chosen plan. Billing calls cost 0 credits so an over-limit account can always upgrade. The user opens the returned url in a browser to pay.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["plan"],"properties":{"plan":{"type":"string","enum":["developer","scale"],"description":"developer = 40,000 credits/mo; scale = 600,000 credits/mo"}}}}}},"responses":{"200":{"description":"Checkout session created","content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"Stripe-hosted checkout page URL — open in a browser"}}}}}},"400":{"description":"plan must be 'developer' or 'scale'","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, or revoked API key. Fix: pass Authorization: Bearer sk_... (get a key at /v1/signup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly credit limit reached (code credits_exhausted). Fix: upgrade via POST /v1/billing/checkout or wait for the monthly reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-minute rate limit for the plan exceeded (code rate_limited). Retry after a short wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Billing not configured on this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/signup":{"post":{"operationId":"create_account","summary":"Self-serve signup: email in, live free-tier API key out (no auth required)","description":"Creates a free-tier account (100 credits/month) and returns a live API key. The api_key value is shown exactly once — only its hash is stored, so it must be saved immediately. Rate limited to 3 signups per IP per 24h.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","description":"Account email (one account per email)"},"name":{"type":"string","description":"Optional account name"}}}}}},"responses":{"201":{"description":"Account created — api_key appears only in this response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupResult"}}}},"400":{"description":"A valid email is required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"An account with this email already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Signup rate limit (3 per IP per 24h)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","description":"Swath API key, e.g. Authorization: Bearer sk_test_... Get one at POST /v1/signup or https://swathapi.com/signup"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code, e.g. invalid_request, unauthorized, credits_exhausted, rate_limited, not_found, account_exists, billing_not_configured"},"message":{"type":"string"},"used":{"type":"number","description":"Credits used this month (present on credits_exhausted)"},"plan":{"type":"string","description":"Current plan (present on credits_exhausted)"}}}}},"StormSwath":{"type":"object","description":"One storm with its damage-swath summary","properties":{"storm_id":{"type":"string","description":"Storm id (sw_...)"},"type":{"type":"string","enum":["hail","wind"]},"started_at":{"type":"string","format":"date-time"},"last_seen_at":{"type":"string","format":"date-time"},"verified_at":{"type":["string","null"],"format":"date-time","description":"When radar verification confirmed the swath"},"status":{"type":"string","description":"e.g. active"},"swath_id":{"type":"string","description":"Swath id (swath_...)"},"mesh_max_in":{"type":["number","null"],"description":"Max radar-estimated hail size in the swath, inches"},"mesh_mean_in":{"type":["number","null"],"description":"Mean radar-estimated hail size, inches"},"wind_max_mph":{"type":["number","null"],"description":"Max wind speed, mph (wind storms)"},"area_km2":{"type":["number","null"],"description":"Swath footprint area, square km"}}},"SwathDetail":{"type":"object","properties":{"id":{"type":"string","description":"Swath id (swath_...)"},"storm_id":{"type":"string"},"type":{"type":"string","enum":["hail","wind"]},"started_at":{"type":"string","format":"date-time"},"last_seen_at":{"type":"string","format":"date-time"},"verified_at":{"type":["string","null"],"format":"date-time"},"status":{"type":"string"},"mesh_max_in":{"type":["number","null"]},"mesh_mean_in":{"type":["number","null"]},"wind_max_mph":{"type":["number","null"]},"area_km2":{"type":["number","null"]},"updated_at":{"type":"string","format":"date-time"}}},"SwathGeometryFeature":{"type":"object","description":"GeoJSON Feature carrying the swath footprint","properties":{"type":{"type":"string","enum":["Feature"]},"properties":{"type":"object","properties":{"swath_id":{"type":"string"},"storm_id":{"type":"string"},"mesh_max_in":{"type":["number","null"]}}},"geometry":{"type":"object","description":"GeoJSON MultiPolygon in WGS84"}}},"Exposure":{"type":"object","properties":{"hail_in":{"type":["number","null"],"description":"Max radar hail size at this parcel, inches"},"score":{"type":["number","null"],"description":"0 at the 1.00\" detection threshold up to 1.0 at >= 3.00\" hail"}}},"Coverage":{"type":"object","description":"Parcel-data coverage metadata for this report run","properties":{"tiles_fetched":{"type":"integer","description":"Vendor tiles fetched live during this call"},"parcels_fetched":{"type":"integer","description":"Parcels fetched live during this call"},"vendor":{"type":"string","description":"Parcel data vendor, e.g. regrid"},"vendor_fetch":{"type":"string","enum":["live","skipped_free_plan"],"description":"Whether live vendor fetches ran (free plan is cached-only)"}}},"ReportProperty":{"type":"object","properties":{"address":{"type":"string"},"parcel_id":{"type":["string","null"]},"year_built":{"type":["integer","null"]},"sqft":{"type":["number","null"]},"roof_material":{"type":["string","null"]},"roof_age_est":{"type":["number","null"],"description":"Estimated roof age in years"},"roof_age_source":{"type":["string","null"],"description":"e.g. year_built or permit"},"last_roof_permit":{"type":["string","null"],"description":"YYYY-MM-DD of the last roofing permit, if known"},"owner_occupied":{"type":["boolean","null"]},"assessed_value":{"type":["number","null"],"description":"Assessed value, USD"},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"exposure":{"$ref":"#/components/schemas/Exposure"}}},"SwathReport":{"type":"object","description":"Swath Report: properties inside the swath, highest exposure first","properties":{"storm_id":{"type":"string"},"swath_id":{"type":"string"},"type":{"type":"string","enum":["hail","wind"]},"verified_at":{"type":["string","null"],"format":"date-time"},"mesh_max_in":{"type":["number","null"]},"total":{"type":"integer","description":"Total matching properties across all pages"},"count":{"type":"integer","description":"Properties in this page"},"offset":{"type":"integer"},"coverage":{"$ref":"#/components/schemas/Coverage"},"billing":{"$ref":"#/components/schemas/ReportBilling"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ReportProperty"}}}},"ReportBilling":{"type":"object","description":"Actual credits billed for this report call (per-record pricing)","properties":{"credits":{"type":"integer","description":"Credits billed: properties_returned × 1 + fresh_fetched × fresh_surcharge_per_record, minimum applied"},"properties_returned":{"type":"integer","description":"Properties in this page — 1 credit each"},"fresh_fetched":{"type":"integer","description":"Records fetched fresh from the parcel vendor during this call"},"fresh_surcharge_per_record":{"type":"integer","description":"Credits per fresh-fetched record (25 at current rates)"},"minimum":{"type":"integer","description":"Per-request billing floor (10 credits)"}}},"ReportQuote":{"type":"object","description":"Cost preview for a Swath Report — counts only, no property rows","properties":{"storm_id":{"type":"string"},"swath_id":{"type":"string"},"total_cached":{"type":"integer","description":"Cached properties matching the filters (1 credit each when returned)"},"fresh_possible":{"type":"boolean","description":"Whether a paid-plan report could trigger live vendor fetches (uncovered tiles exist)"},"fresh_estimate":{"type":["integer","null"],"description":"ESTIMATE of fresh-fetchable records (uncovered tiles × observed parcels-per-tile average); null when no basis. Billing uses actuals only."},"quote":{"type":"object","properties":{"cached_credits":{"type":"integer","description":"total_cached × 1 credit"},"fresh_surcharge_per_record":{"type":"integer","description":"Credits added per fresh-fetched record (25 at current rates)"},"minimum":{"type":"integer","description":"Per-request billing floor (10 credits)"},"note":{"type":"string"}}}}},"Property":{"type":"object","properties":{"id":{"type":"string","description":"Property id (prop_...)"},"parcel_id":{"type":["string","null"]},"address":{"type":"string"},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"zip":{"type":["string","null"]},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"year_built":{"type":["integer","null"]},"sqft":{"type":["number","null"]},"roof_material":{"type":["string","null"]},"last_roof_permit":{"type":["string","null"]},"roof_age_est":{"type":["number","null"]},"roof_age_source":{"type":["string","null"]},"owner_occupied":{"type":["boolean","null"]},"assessed_value":{"type":["number","null"]},"vendor":{"type":"string","description":"Data vendor, e.g. regrid"},"fetched_at":{"type":"string","format":"date-time"},"cache":{"type":"string","enum":["hit","miss"],"description":"Whether this record came from cache or a live vendor fetch"}}},"Monitor":{"type":"object","properties":{"id":{"type":"string","description":"Monitor id (mon_...)"},"name":{"type":["string","null"]},"hail_min_in":{"type":["number","null"],"description":"Trigger threshold, inches"},"wind_min_mph":{"type":["number","null"],"description":"Trigger threshold, mph"},"webhook_url":{"type":["string","null"]},"email_alerts":{"type":"boolean","description":"Email the account owner on each verified storm"},"created_at":{"type":"string","format":"date-time"},"geometry":{"type":"object","description":"GeoJSON MultiPolygon of the monitored area"}}},"MonitorWithSecret":{"allOf":[{"$ref":"#/components/schemas/Monitor"},{"type":"object","properties":{"webhook_secret":{"type":"string","description":"HMAC secret (whsec_...) for verifying webhook payloads — returned only at creation"}}}]},"Usage":{"type":"object","properties":{"plan":{"type":"string","description":"free, developer, or scale"},"month_credits_used":{"type":"number"},"month_calls":{"type":"number"},"monthly_credit_limit":{"type":"number","description":"free 100, developer 40,000, scale 600,000"},"rate_limit_per_minute":{"type":"number"}}},"SignupResult":{"type":"object","properties":{"account_id":{"type":"string"},"plan":{"type":"string","enum":["free"]},"monthly_credits":{"type":"number"},"api_key":{"type":"string","description":"Live API key (sk_test_...) — shown exactly once, save it immediately"},"docs":{"type":"string"},"quickstart":{"type":"string","description":"Ready-to-run curl command using the new key"}}}}}}