{"openapi":"3.1.0","info":{"title":"Civic Worth Public API","version":"1.0.0","summary":"Parcel-resolved civic-data synthesis — public API tier","description":"Public API for the Civic Worth platform. Phase 12 ships four endpoints —\n`/v1/resolve`, `/v1/parcels/{id}`, `/v1/zone-in/{parcelId}`, and\n`/v1/reports/lite` — under a stable v1 contract. v1 is supported for\n24 months from launch; deprecation policy is at\nhttps://api.civicworth.com/storage-rights.\n\n**Pricing tiers (brief Section 4):**\n\n  * Free:       10 req/day, no SLA, no overage (capped client-side)\n  * Developer:  $99/mo,    10,000 req/mo included + $0.01/call overage\n  * Pro:        $499/mo,   100,000 req/mo included + $0.01/call overage\n  * Enterprise: $2,500+/mo, custom (SLA + SSO + redistribution rights)\n\n**Storage rights:** responses are cacheable and redistributable EXCEPT\nfor any field tied to a connector whose license forbids commercial\nredistribution. Every response carries `metadata.licenseRestrictions[]`\nin the body and `X-Civic-License-Restrictions: true|false` in the\nheaders. See https://api.civicworth.com/storage-rights for the full\nstorage-rights doc.\n\n**Auth:** every request must carry `X-API-Key: cw_<prefix>_<secret>`.\nIssue keys at https://civicworth.com/account/api-keys.\n","contact":{"name":"Civic Worth","url":"https://civicworth.com"},"license":{"name":"Public API Terms (v1)","url":"https://api.civicworth.com/storage-rights"}},"servers":[{"url":"https://api.civicworth.com","description":"Production"},{"url":"http://localhost:3001","description":"Local dev (apps/api default port)"}],"tags":[{"name":"resolve","description":"Address → parcel + boundaries (the architectural unlock)"},{"name":"parcels","description":"Direct parcel-id lookup"},{"name":"zone-in","description":"Zone In Phase 1 / Phase 2 side-by-side"},{"name":"reports","description":"Lite-tier synthesis (no PDF)"}],"security":[{"ApiKeyAuth":[]}],"paths":{"/v1/resolve":{"post":{"tags":["resolve"],"summary":"Resolve a free-text address to a parcel + boundaries","operationId":"resolveAddress","description":"Takes a free-text address and returns the matched parcel plus\nevery overlapping jurisdictional boundary (council district,\narea commission, school district, etc.). Cached when possible;\nlive geocoder cascade (Geocodio → Census) otherwise.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveRequest"}}}},"responses":{"200":{"description":"Resolved (or `not_in_coverage` envelope when address geocoded but parcel was missing)","headers":{"X-Civic-License-Restrictions":{"$ref":"#/components/headers/XCivicLicenseRestrictions"},"X-Rate-Limit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Both geocoders failed"}}}},"/v1/parcels/{id}":{"get":{"tags":["parcels"],"summary":"Look up a parcel by its canonical UUID","operationId":"getParcel","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid","description":"Parcel UUID (the canonical SEO URL pattern)"}}],"responses":{"200":{"description":"Parcel resolved","headers":{"X-Civic-License-Restrictions":{"$ref":"#/components/headers/XCivicLicenseRestrictions"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveResponse"}}}},"400":{"description":"Parcel id is not a valid UUID"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Parcel not found"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/zone-in/{parcelId}":{"get":{"tags":["zone-in"],"summary":"Zone In Phase 1 / Phase 2 side-by-side data","operationId":"getZoneIn","parameters":[{"in":"path","name":"parcelId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Zone In data envelope","headers":{"X-Civic-License-Restrictions":{"$ref":"#/components/headers/XCivicLicenseRestrictions"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZoneInResponse"}}}},"400":{"description":"Parcel id is not a valid UUID"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Parcel not found"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/reports/lite":{"post":{"tags":["reports"],"summary":"Generate a Lite-tier civic report (JSON, no PDF)","operationId":"createLiteReport","description":"The Lite tier returns three structured sections (zoning, demographics,\ncivic-address overview) as JSON. The signed-PDF flow is reserved for\nthe consumer `/api/v1/reports` endpoint — partners who need PDFs can\nupgrade to the Pro tier and integrate with the consumer flow.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLiteReportRequest"}}}},"responses":{"200":{"description":"Report generated (or fetched from cache)","headers":{"X-Civic-License-Restrictions":{"$ref":"#/components/headers/XCivicLicenseRestrictions"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiteReportResponse"}}}},"400":{"$ref":"#/components/responses/InvalidRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Address resolved to a parcel outside coverage"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Worker renderer unavailable"}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Issue at https://civicworth.com/account/api-keys. Format: `cw_<8 chars>_<32 chars>`.\n"}},"headers":{"XCivicLicenseRestrictions":{"description":"`true` when the response body's `metadata.licenseRestrictions[]` is non-empty.\nQuick-check header for CDN/proxy layers; the structured detail is in the body.\n","schema":{"type":"string","enum":[true,false]}},"XRateLimitRemaining":{"description":"Approximate number of requests remaining in the most-binding window.","schema":{"type":"integer","minimum":0}}},"responses":{"InvalidRequest":{"description":"Validation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"RateLimited":{"description":"Over quota","headers":{"Retry-After":{"description":"Seconds until the next request will be allowed","schema":{"type":"integer"}},"X-Civic-Upgrade":{"description":"Upgrade URL when the quota was the monthly billing cap","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitErrorResponse"}}}}},"schemas":{"ResponseMetadata":{"type":"object","required":["apiVersion","dataAsOf","licenseRestrictions"],"properties":{"apiVersion":{"type":"string","enum":["v1"]},"dataAsOf":{"type":"string","format":"date-time","description":"ISO-8601 timestamp of the most-recent connector run touched by this response"},"licenseRestrictions":{"type":"array","description":"Per-connector license restrictions for any source whose data was\nomitted from this response. Empty when no restrictions hit. Populated\nwhen a connector's `commercial_redistribution_allowed=false`.\n","items":{"$ref":"#/components/schemas/LicenseRestriction"}}}},"LicenseRestriction":{"type":"object","required":["connectorId","restriction"],"properties":{"connectorId":{"type":"string","description":"Canonical connector slug (matches `connectors.name` in the source registry)"},"restriction":{"type":"string","enum":["non_commercial_only","attribution_required","license_agreement_required","other"]},"omittedFields":{"type":"array","items":{"type":"string"},"description":"Dotted-path field names omitted from the response body"},"note":{"type":"string"}}},"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"message":{"type":"string"},"issues":{"type":"array","items":{"type":"object"}}}},"RateLimitErrorResponse":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"reason":{"type":"string","enum":["over_minute_cap","over_day_cap","over_month_cap"]},"retry_after_seconds":{"type":"integer","minimum":0,"nullable":true}}}]},"ResolveRequest":{"type":"object","required":["address"],"properties":{"address":{"type":"string","minLength":3,"maxLength":500},"biasFips":{"type":"string","description":"Optional jurisdiction FIPS bias (Phase 2 defaults to \"39049\" — Franklin County)"}}},"ResolveResponse":{"type":"object","description":"See `apps/api/openapi.yaml` (the internal spec) for the full ResolveResponse\nschema. Phase 12 surfaces the same shape with an added `metadata` envelope.\n","additionalProperties":true,"required":["metadata"],"properties":{"metadata":{"$ref":"#/components/schemas/ResponseMetadata"}}},"ZoneInResponse":{"type":"object","required":["variant","envelope","metadata"],"properties":{"variant":{"type":"string","enum":["phase1","phase2_study_area","phase2_draft","legacy","not_in_zone_in"]},"envelope":{"$ref":"#/components/schemas/ResolveResponse"},"message":{"type":"string","description":"Optional partner-facing note (e.g., \"codified-text excerpts are available via /embed/[parcel_id]\")"},"metadata":{"$ref":"#/components/schemas/ResponseMetadata"}}},"CreateLiteReportRequest":{"type":"object","required":["address"],"properties":{"address":{"type":"string","minLength":3,"maxLength":500}}},"LiteReportResponse":{"type":"object","required":["reportId","tier","sections","generatedAt","metadata"],"properties":{"reportId":{"type":"string","format":"uuid"},"parcelId":{"type":"string","format":"uuid","nullable":true},"tier":{"type":"string","enum":["lite"]},"sections":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"RealizedSection[] — see internal spec for shape"},"skippedSections":{"type":"array","items":{"type":"object"}},"generatedAt":{"type":"string","format":"date-time"},"cached":{"type":"boolean"},"metadata":{"$ref":"#/components/schemas/ResponseMetadata"}}}}}}