{"openapi":"3.1.0","info":{"title":"@mailwoman/api","version":"7.8.1","description":"The native Mailwoman HTTP API — engine-agnostic /v1 surface (parse, geocode, batch, resolve, format) with health, metrics, and an emitted OpenAPI document.","license":{"name":"AGPL-3.0-only OR LicenseRef-Commercial","identifier":"AGPL-3.0-only"},"contact":{"name":"Sister Software","url":"https://mailwoman.sister.software"}},"servers":[{"url":"http://{host}:{port}","variables":{"host":{"default":"127.0.0.1"},"port":{"default":"3000"}}}],"tags":[{"name":"parsing","description":"Free-text address parsing."},{"name":"geocoding","description":"Address-to-coordinate resolution."},{"name":"resolving","description":"Gazetteer resolution over an already-decoded address tree."},{"name":"formatting","description":"Component-dict rendering — the inverse of parsing."},{"name":"meta","description":"Health, metrics, and deploy-time operations."}],"security":[],"components":{"schemas":{"ParseOutcome":{"type":"object","properties":{"input":{"type":"string"},"components":{"type":"array","items":{"$ref":"#/components/schemas/ParseComponent"}},"tree":{"type":"object","properties":{"roots":{"type":"array","items":{}}},"required":["roots"],"additionalProperties":{}},"debug":{"type":"string"}},"required":["input","components","tree"]},"ParseComponent":{"type":"object","properties":{"tag":{"type":"string"},"value":{"type":"string"}},"required":["tag","value"]},"APIError":{"type":"object","properties":{"error":{"type":"string"},"detail":{"type":"string"}},"required":["error"]},"ParseRequest":{"type":"object","properties":{"address":{"type":"string"},"debug":{"type":"boolean"}},"required":["address"]},"GeocodeOutcome":{"type":"object","properties":{"input":{"type":"string"},"lat":{"type":["number","null"]},"lon":{"type":["number","null"]},"resolution_tier":{"type":"string","enum":["address_point","interpolated","street","admin"]},"uncertainty_m":{"type":["number","null"]},"locality":{"type":["string","null"]},"region":{"type":["string","null"]},"postcode":{"type":["string","null"]},"house_number":{"type":["string","null"]},"street":{"type":["string","null"]},"countryCode":{"type":["string","null"]},"hierarchy":{"type":"array","items":{"$ref":"#/components/schemas/GeocodeHierarchyEntry"}},"candidates":{"type":"array","items":{"$ref":"#/components/schemas/GeocodeCandidate"}}},"required":["input","lat","lon","resolution_tier","uncertainty_m","locality","region","postcode","house_number","street","countryCode","hierarchy","candidates"],"additionalProperties":{}},"GeocodeHierarchyEntry":{"type":"object","properties":{"tag":{"type":"string"},"value":{"type":"string"},"name":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"placeID":{"type":"string"}},"required":["tag","value","name"]},"GeocodeCandidate":{"type":"object","properties":{"name":{"type":"string"},"tag":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"countryCode":{"type":["string","null"]},"placeID":{"type":"string"}},"required":["name","tag","lat","lon","countryCode"]},"GeocodeRequest":{"type":"object","properties":{"address":{"type":"string"}},"required":["address"]},"BatchResponse":{"type":"object","properties":{"results":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/GeocodeOutcome"},{"type":"object","properties":{"input":{"type":"string"},"error":{"type":"string"}},"required":["input","error"]}]}}},"required":["results"]},"BatchRequest":{"type":"object","properties":{"addresses":{"type":"array","items":{"type":"string"}}},"required":["addresses"]},"ResolveResponse":{"type":"object","properties":{"tree":{"type":"object","properties":{"roots":{"type":"array","items":{}}},"required":["roots"],"additionalProperties":{}}},"required":["tree"]},"ResolveRequest":{"type":"object","properties":{"tree":{"type":"object","properties":{"roots":{"type":"array","items":{}}},"required":["roots"],"additionalProperties":{}},"opts":{"type":"object","properties":{},"additionalProperties":{}}},"required":["tree"]},"FormatResponse":{"type":"object","properties":{"formatted":{"type":"string"},"canonicalKey":{"type":"string"}},"required":["formatted","canonicalKey"]},"FormatRequest":{"type":"object","properties":{"components":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}},"country":{"type":"string"},"options":{"type":"object","properties":{},"additionalProperties":{}}},"required":["components","country"]},"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"uptime_s":{"type":"number"}},"required":["status","uptime_s"],"additionalProperties":{}}},"parameters":{}},"paths":{"/v1/parse":{"get":{"operationId":"parseGet","summary":"Parse an address (query string)","tags":["parsing"],"parameters":[{"schema":{"type":"string","description":"The address to parse."},"required":false,"description":"The address to parse.","name":"address","in":"query"},{"schema":{"type":"string","description":"`\"true\"` to include a diagnostic report."},"required":false,"description":"`\"true\"` to include a diagnostic report.","name":"debug","in":"query"}],"responses":{"200":{"description":"The tokenized input span + ranked solutions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseOutcome"}}}},"400":{"description":"`address` is required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}},"501":{"description":"The backing engine method is not wired for this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}}}},"post":{"operationId":"parsePost","summary":"Parse an address (JSON body)","tags":["parsing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseRequest"}}}},"responses":{"200":{"description":"The tokenized input span + ranked solutions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseOutcome"}}}},"400":{"description":"`address` is required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}},"501":{"description":"The backing engine method is not wired for this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}}}}},"/v1/geocode":{"post":{"operationId":"geocode","summary":"Geocode an address to coordinates","tags":["geocoding"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeocodeRequest"}}}},"responses":{"200":{"description":"One geocode result (parse → resolve cascade), passed through from the engine verbatim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeocodeOutcome"}}}},"400":{"description":"`address` is required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}},"503":{"description":"The geocoding engine is not wired for this deployment (dependencies missing).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}}}}},"/v1/batch":{"post":{"operationId":"batch","summary":"Geocode a batch of addresses","tags":["geocoding"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"}}}},"responses":{"200":{"description":"One result per input address, in input order (per-row error isolation).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}},"400":{"description":"Body must be `{ addresses: string[] }`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}},"413":{"description":"`addresses.length` exceeds the configured batch cap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}},"503":{"description":"The geocoding engine is not wired for this deployment (dependencies missing).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}}}}},"/v1/resolve":{"post":{"operationId":"resolve","summary":"Resolve an already-decoded address tree against the gazetteer","tags":["resolving"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveRequest"}}}},"responses":{"200":{"description":"The same tree, decorated in place with gazetteer coordinates + attribution.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveResponse"}}}},"400":{"description":"Body must be `{ tree: AddressTree, opts? }`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}},"503":{"description":"The resolver is not wired for this deployment (dependencies missing).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}}}}},"/v1/reload":{"post":{"operationId":"reload","summary":"Reload versioned data shards (deploy-only; gate at ingress)","tags":["meta"],"responses":{"200":{"description":"Versioned data switchover result — the new per-shard version map.","content":{"application/json":{"schema":{"type":"object","properties":{"reloaded":{"type":"boolean"},"versions":{}},"required":["reloaded"],"additionalProperties":{}}}}},"503":{"description":"The geocoding engine is not wired for this deployment (dependencies missing).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}}}}},"/v1/format":{"post":{"operationId":"format","summary":"Render address components to a string + canonical match key","tags":["formatting"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormatRequest"}}}},"responses":{"200":{"description":"The rendered address string + the deterministic canonical match key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormatResponse"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}}}}}},"/health":{"get":{"operationId":"health","summary":"Liveness + engine health","tags":["meta"],"responses":{"200":{"description":"Liveness + engine health block. Answers 200 even when the engine is absent or broken.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/metrics":{"get":{"operationId":"metrics","summary":"In-process timing metrics snapshot","tags":["meta"],"responses":{"200":{"description":"The live in-process timing metrics snapshot (latency percentiles + per-tier counts).","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{}}}}}}}}},"webhooks":{}}
