{"openapi":"3.1.0","info":{"title":"ThinAir Geo API","version":"1.0.0","description":"## Overview\n\nGeocoding, autocomplete, structured search, place IDs, intersections, routing, distance matrix, isochrones, map matching, traffic, weather, and POI search — one API. REST and MCP consume the same ThinAir Geo service layer; this spec describes the REST surface.\n\n## Base URL & authentication\n\nAll endpoints live under `https://geo.thinair.co`. Authenticate with a bearer API key:\n\n```bash\ncurl \"https://geo.thinair.co/v1/geocode?text=Pike Place Market Seattle\" \\\n  -H \"Authorization: Bearer ta_geo_…\"\n```\n\n## Observability\n\nEvery REST response carries a correlation pair (`X-Request-Id` / `X-Trace-Id`), a cache status (`X-Cache: HIT | MISS | BYPASS`), and a per-stage timing family (`X-Auth-Ms`, `X-Quota-Ms`, `X-Upstream-Ms`, `X-Worker-Ms`, `X-Edge-Ms`, `X-Total-Ms`) — see components.headers for the full surface. Edge-cached lookups answer in single-digit milliseconds at the edge (verifiable via `X-Edge-Ms`); cold geocoding runs through the regional engine.\n\n## Quota\n\nThe daily-quota debit is write-behind on all API-key plans, so `X-Quota-Used` / `X-Quota-Remaining` are eventually consistent (typically within ~30 seconds); `GET /v1/quota` is the authoritative synchronous count.\n\n## Enterprise access\n\n`https://geo.thinair.co` is the canonical public API and MCP surface. Azure API Management (APIM) is available as an enterprise access layer — private deployments, custom quota/SLA, VNET/private-link connectivity, subscription keys, and customer-side governance. APIM is an enterprise access layer. It does not own product behavior. All product behavior lives in the shared ThinAir Geo service layer consumed by REST, MCP, and APIM. APIM calls the same service layer (no separate business logic), preserves request/trace ids and ThinAir response shapes, and may add enterprise auth, quota, IP policy, logging, and private networking. Public documentation keeps `https://geo.thinair.co` as the canonical base URL; enterprise base URLs are issued per customer."},"servers":[{"url":"https://geo.thinair.co"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"UpgradePlan":{"type":"object","properties":{"name":{"type":"string"},"price":{"type":"string","description":"Display price, e.g. \"$29/mo\" or \"custom\"."},"quota":{"type":"string","description":"Display allowance, e.g. \"10,000/day\"."},"note":{"type":"string"},"upgrade_url":{"type":"string","description":"Checkout link for this specific plan. Carries the same plan preselect, in-place upgrade token and src attribution as the top-level upgrade_url."}},"required":["name","price","quota","upgrade_url"],"description":"One self-serve plan offered on a 429."},"ProblemDetails":{"type":"object","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"code":{"type":"string","description":"Stable internal_code; see /docs/errors"},"request_id":{"type":"string"},"instance":{"type":"string"},"retry_after_ms":{"type":"integer"},"upgrade_url":{"type":"string","description":"Present on 429 (quota exceeded) and on 401 TRIAL_EXPIRED. Preselects the recommended plan and, when the caller's tenant is known, carries a short-lived token that upgrades the API key you are already using — no key swap and no config change after payment."},"suggested_upgrade":{"type":["string","null"],"description":"Recommended next plan, or null on the top self-serve tier. 429 only."},"upgrade_cta":{"type":"string","description":"One-line upgrade prompt. 429 only."},"upgrade_plans":{"type":"array","items":{"$ref":"#/components/schemas/UpgradePlan"},"description":"Full self-serve plan menu. 429 only."},"next_step":{"type":"string","description":"Prose instruction naming the same URL as upgrade_url. 429 only."},"usage":{"type":"object","properties":{"plan":{"type":"string"},"used":{"type":"number"},"limit":{"type":"number"},"period":{"type":"string"},"reset":{"type":"string","description":"ISO 8601 instant the counter rolls over."}},"required":["plan"],"description":"Where the cap stands. 429 only; counters are omitted when the denial has none."},"expired_at":{"type":"string","description":"ISO 8601 instant the key stopped authenticating. TRIAL_EXPIRED only."},"plan":{"type":"string","description":"Plan the expired key carried. TRIAL_EXPIRED only."},"quota":{"type":"object","properties":{"daily":{"type":"number"},"period":{"type":"string"}},"required":["daily","period"],"description":"Daily allowance the expired key had; -1 = unlimited. TRIAL_EXPIRED only."}},"required":["type","title","status","detail","code","request_id","instance"],"description":"RFC 7807 problem-details with ThinAir extensions (code, request_id, retry_after_ms). A 429 additionally carries upgrade_url, suggested_upgrade, upgrade_cta, upgrade_plans, next_step and usage; a 401 TRIAL_EXPIRED carries upgrade_url, expired_at, plan and quota."},"WeatherResponse":{"type":"object","properties":{"location":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"},"label":{"type":"string"}},"required":["lat","lon"]},"units":{"type":"string","enum":["imperial","metric"]},"current":{"type":"object","additionalProperties":{}},"forecast":{"type":"array","items":{"type":"object","additionalProperties":{}}},"forecast_days":{"type":"number"},"alerts":{"type":"array","items":{"type":"object","additionalProperties":{}}},"alerts_error":{"type":"string"},"minutely_precipitation":{"type":"array","items":{"type":"object","additionalProperties":{}}},"minutely_error":{"type":"string"},"source":{"type":"string"},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"}},"required":["upstream_ms","feature_count"]},"upstream_error":{"type":["string","null"]}},"required":["location","units","current","source","meta"],"description":"Pass-through of the typed WeatherResult. Field set varies with include_* flags."},"TrafficResponse":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"},"coverage":{"type":"boolean"},"coverage_reason":{"type":"string","enum":["disabled","unavailable"]},"current_speed":{"type":["number","null"]},"free_flow_speed":{"type":["number","null"]},"ratio_pct":{"type":["number","null"]},"units":{"type":"string"},"segments":{"type":"array","items":{"type":"object","additionalProperties":{}}},"segment_count":{"type":"number"},"closures":{"type":"number"},"congestion":{"type":"string"},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"}},"required":["upstream_ms","feature_count"]},"upstream_error":{"type":["string","null"]}},"required":["lat","lon","current_speed","free_flow_speed","ratio_pct","units","segments","segment_count","closures","congestion","meta"],"description":"Pass-through of the typed TrafficResult. `coverage: false` means no live data was returned; `coverage_reason` names the cause when it is known to be something other than the location (`disabled`, `unavailable`) and is absent for a genuine coverage gap."},"ExploreItem":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"layer":{"type":"string","description":"The place's own layer — one of locality, neighbourhood, borough, county, region. This is the classification; there is no other tier field.","example":"locality"},"population":{"type":"number","description":"Population, present ONLY where the source carries one (coverage is partial and varies by layer). Absent means unknown — never zero-filled.","example":2314157},"distance_km":{"type":"number","description":"Distance from the center point in kilometers (the canonical unit).","example":12.4},"distance":{"type":"number","description":"The same distance in the request's display unit (`meta.units`), 1 decimal.","example":7.7},"distance_unit":{"type":"string","enum":["miles","kilometers"],"description":"Display unit of `distance` — always equal to `meta.units`.","example":"miles"},"bearing":{"type":"integer","minimum":0,"maximum":359,"description":"Initial bearing from the center point to this place, integer degrees 0-359 clockwise from true north.","example":315},"coordinates":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in decimal degrees, WGS84.","example":29.7604},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in decimal degrees, WGS84.","example":-95.3698}},"required":["lat","lon"]},"region":{"type":"string"},"country":{"type":"string"}}},"StructuredError":{"type":"object","properties":{"code":{"type":"string","description":"Stable, snake_case identifier callers can switch on.","example":"radius_exceeds_max"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["code","message"]},"ExploreResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ExploreItem"}},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"},"query":{"type":"string"},"center_lat":{"type":"number"},"center_lon":{"type":"number"},"center_label":{"type":"string"},"radius_km":{"type":"number","description":"Search radius actually applied, in kilometers (the canonical unit) — 150 on the default path. Present on every response except the legacy unparseable-radius fall-through, where the engine decided.","example":150},"radius":{"type":"number","description":"The applied radius expressed in `units`, rounded to 3 decimals — 93.206 (miles) on the default path, 150 with `units=kilometers`.","example":93.206},"units":{"type":"string","enum":["miles","kilometers"],"description":"Display unit of `radius`: the request's `units` if sent; else the unit of a suffixed `radius` string ('30mi' → miles, '30km' / '800m' → kilometers); else miles — the platform default, which also covers a bare numeric `radius` and the no-input path. `units` alone does not supply a radius; it only selects this.","example":"miles"},"layers":{"type":"array","items":{"type":"string","enum":["locality","neighbourhood","borough","county","region"]},"description":"The layers actually served, in the order requested. A layer that received no share of `size` is absent here and `truncated` is true.","example":["locality"]},"ranking":{"type":"string","description":"Plain-language statement of the ranking basis actually applied.","example":"population desc, distance asc"},"truncated":{"type":"boolean","description":"True when a layer produced more candidates than its share of `size`. False means no truncation was observed — with no upstream total, it is not a claim that nothing more exists.","example":false},"total_candidates":{"type":"integer","description":"Count of matching candidates upstream, before this API's `min_population` filter and duplicate-place removal are applied — NOT the number of items in this response's `items` array, and not how many more results you could page through. Present ONLY when the backend reports a true, exact total for every layer served; omitted when it cannot (including whenever `min_population` is greater than zero; an explicit `min_population: 0` means no filter and keeps the exact total), and never estimated.","example":412}},"required":["upstream_ms","feature_count","query"]},"upstream_error":{"type":["string","null"]},"structured_error":{"$ref":"#/components/schemas/StructuredError"}},"required":["items","meta"]},"PlacesItem":{"type":"object","properties":{"id":{"type":"string","description":"Stable public ThinAir id (`ta_place_<key>`). Resolvable via `GET /v1/place`.","example":"ta_place_8f3a2c9e61c04d58"},"name":{"type":"string"},"label":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"layer":{"type":"string"},"distance_km":{"type":"number"},"website":{"type":"string"},"phone":{"type":"string"},"hours":{"type":"string"},"cuisine":{"type":"string"},"brand":{"type":"string"}}},"PlacesResponse":{"type":"object","properties":{"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","example":"category_text_mismatch"},"severity":{"type":"string","enum":["advisory"]},"message":{"type":"string"}},"required":["code","severity","message"]}},"query":{"type":"string"},"radius":{"type":"string","description":"Legacy echo — the applied radius as '<n>m'. Present only when the request supplied a radius (any form); absent on the default path. Prefer `radius_km` + `units`.","example":"48280m"},"radius_km":{"type":"number","description":"Search radius actually applied, in kilometers (the canonical unit) — 50 on the default path. Present on every center-based search, cache hit or miss; absent on the text-only path (no center), where no radius applies.","example":50},"units":{"type":"string","enum":["miles","kilometers"],"description":"Display unit for the radius: the request's `units` if sent; else the unit of a suffixed `radius` string; else kilometers for `radius_m`; else miles — the platform default, which also covers a bare numeric `radius` and the no-input path. Same presence rules as `radius_km`.","example":"miles"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PlacesItem"}},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"}},"required":["upstream_ms","feature_count"]},"upstream_error":{"type":["string","null"]},"raw_upstream_payload":{}},"required":["query","items","meta"]},"IsochroneFeature":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string"},"coordinates":{}},"required":["type"]},"properties":{"type":"object","additionalProperties":{}}},"required":["type","geometry","properties"]},"IsochroneResponse":{"type":"object","properties":{"origin":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in decimal degrees, WGS84.","example":29.7604},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in decimal degrees, WGS84.","example":-95.3698},"label":{"type":"string"}},"required":["lat","lon"]},"costing":{"type":"string"},"preset":{"type":"string"},"preset_note":{"type":"string"},"costing_options":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{}}},"contour_values":{"type":"array","items":{"type":"number"}},"metric":{"type":"string"},"geojson":{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"$ref":"#/components/schemas/IsochroneFeature"}}},"required":["type","features"]},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"}},"required":["upstream_ms","feature_count"]},"upstream_error":{"type":["string","null"]},"structured_error":{"$ref":"#/components/schemas/StructuredError"}},"required":["origin","costing","contour_values","metric","geojson","meta"]},"GeocodedFeature":{"type":"object","properties":{"id":{"type":"string","description":"Stable public ThinAir id (`ta_place_<key>` / `ta_intersection_<key>`). Deterministic across re-serves; resolvable via `GET /v1/place`. Absent on synthetic curated features.","example":"ta_place_8f3a2c9e61c04d58"},"name":{"type":"string"},"label":{"type":"string"},"confidence":{"type":"number"},"layer":{"type":"string"},"intersection_type":{"type":"string","enum":["corner","interchange"]},"intersection_precision":{"type":"string","enum":["geometric_crossing","tangent_intersection","centroid_cluster","approximate"]},"street_kind":{"type":"string","enum":["street","route"]},"cross_street_kind":{"type":"string","enum":["street","route"]},"street_route":{"type":"string","minLength":1,"maxLength":128},"cross_street_route":{"type":"string","minLength":1,"maxLength":128},"place_type":{"type":"string"},"confidence_scope":{"type":"string","enum":["query_to_candidate"],"description":"What `confidence` is scoped to. `query_to_candidate` means it scores how well this candidate matches the submitted query — it is NOT a probability that the place is real, that the address is correct, or that mail would arrive. Fixed value today; treat it as a closed set that may gain members."},"address_evidence":{"type":"object","properties":{"house_number":{"type":"string","enum":["matched","partial","absent"]},"street":{"type":"string","enum":["matched","partial","absent"]},"locality":{"type":"string","enum":["matched","partial","absent"]},"region":{"type":"string","enum":["matched","partial","absent"]},"postal_code":{"type":"string","enum":["matched","partial","absent"],"description":"Whether the candidate carries a postal code. Presence only — this states what the result has, and makes no claim about its correctness."}},"required":["house_number","street","locality","region","postal_code"],"description":"What THIS CANDIDATE carries, component by component — `matched` (a real value is present), `partial` (only a coarser or derived stand-in, e.g. an interpolated house number or a locality falling back to its administrative parent), `absent` (nothing). This describes the result, NOT a comparison against your query: the engine returns no per-component match data, and re-parsing your text here would mean two parsers disagreeing about the same string. Derived from the feature, never from `confidence`."},"postal_code":{"type":"string","pattern":"^[0-9]{5}$","description":"An eligible US ZIP5 read from the selected result's own postal-code field. While origin enforcement is active this field is emitted ONLY when the engine stamped the value as coming from the address record itself (`source_record` provenance); a value inherited from a parent area, or a result cached before that evidence existed, is omitted rather than emitted without provenance — so an absent ZIP under enforcement means \"not proven for this record\", not \"no such ZIP\". Provenance describes only WHERE the value came from. It is not a postal operator check of any kind — no certification, no address-quality rating, and no statement about whether mail would arrive — so a syntactically correct, correctly-sourced value can still be wrong for the address. Also omitted when unavailable or unsuitable for five-digit form. US address results only. Distinct from `address_evidence.postal_code`, which is a presence STATUS rather than a value. On reverse geocoding this describes the RETURNED ADDRESS — it is not a postal assignment to the coordinate you supplied."},"postal_input_match":{"type":"string","enum":["match","conflict","unavailable"],"description":"STRUCTURED GEOCODING ONLY (`geocode_structured` / `POST /v1/search/structured`); absent from free-text geocoding. Whether the postcode YOU SUBMITTED agrees with this candidate's own postcode — `match` (same five-digit value), `conflict` (they differ), `unavailable` (no comparison was possible). This compares two values already in hand: no lookup, no external reference, and your query text is never parsed to find one. `conflict` does NOT say which side is wrong — your input may name a neighbouring area, the candidate may be a near-miss, or the record's own value may be stale; treat it as a prompt to ask a human, never as a correction to apply automatically. The candidate side counts only when its postcode came from the address record itself; one inherited from a parent area yields `unavailable` rather than a misleading verdict. `unavailable` also covers: nothing submitted, a submitted value not readable as a US five-digit code, and results cached before this field existed."},"address_line1":{"type":"string","description":"House number and street for an eligible US address result, composed from the result's own components — never parsed from the display label. Absent when the result is not a US street address."},"address_line2":{"type":"string","description":"Unit or suite, only when the source record carries one. A unit supplied in your request is never echoed back here."},"formatted_address":{"type":"string","description":"Single-line US display form, e.g. `1600 Pennsylvania Avenue NW, Washington, DC 20500, USA`. The postal segment is present only when an eligible ZIP is; its absence never suppresses the street address."},"address_components":{"type":"object","properties":{"house_number":{"type":"string"},"street":{"type":"string"},"unit":{"type":"string"},"locality":{"type":"string"},"region_code":{"type":"string"},"region_name":{"type":"string"},"postal_code":{"type":"string"},"country_code":{"type":"string"}},"description":"The same US components the formatted string is built from. Every field is source-established or absent; nothing is inferred or back-filled."},"method":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"}},"required":["place_type","confidence_scope","address_evidence","method","lat","lon"]},"GeocodeMeta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"},"bbox":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]},"query":{"type":"string"},"mode":{"type":"string","enum":["geocode","reverse"]}},"required":["upstream_ms","feature_count","query","mode"]},"GeocodeFeatureCollection":{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string","enum":["Point"]},"coordinates":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}]}},"required":["type","coordinates"]},"properties":{"type":"object","additionalProperties":{}}},"required":["type","geometry","properties"]}}},"required":["type","features"]},"PrecisionBlock":{"type":"object","properties":{"class":{"type":"string","example":"intersection"},"risk_flags":{"type":"array","items":{"type":"string"},"example":["ambiguous_without_context"]},"required_context":{"type":"array","items":{"type":"string"},"example":["borough"]},"learning_overlay":{"type":"object","properties":{"applied":{"type":"boolean"},"rule_ids":{"type":"array","items":{"type":"string"}},"actions":{"type":"array","items":{"type":"string"}}},"required":["applied"]}},"required":["class","risk_flags","required_context","learning_overlay"],"description":"Learning-overlay precision adjudication. Present only when the overlay evaluated this query (intersection-class). Omitted otherwise — legacy consumers see no shape change."},"GeocodeResponse":{"type":"object","properties":{"features":{"type":"array","items":{"$ref":"#/components/schemas/GeocodedFeature"}},"meta":{"$ref":"#/components/schemas/GeocodeMeta"},"geojson":{"$ref":"#/components/schemas/GeocodeFeatureCollection"},"warnings":{"type":"array","items":{"type":"string"},"description":"Geocoder warnings (`geocoding.warnings`) — present only when the suppress middleware emitted signals on this response. Treat as opaque strings.","example":["intersection_unresolved"]},"precision":{"$ref":"#/components/schemas/PrecisionBlock"},"upstream_error":{"type":["string","null"]}},"required":["features","meta","geojson"]},"PlaceComponents":{"type":"object","properties":{"country_code":{"type":"string"},"country":{"type":"string"},"region":{"type":"string"},"locality":{"type":"string"}}},"PlaceResponse":{"type":"object","properties":{"id":{"type":"string","example":"ta_place_8f3a2c9e61c04d58"},"type":{"type":"string","enum":["place","intersection"]},"name":{"type":"string"},"label":{"type":"string"},"layer":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"components":{"$ref":"#/components/schemas/PlaceComponents"},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"cacheHit":{"type":"boolean"},"id_scheme_version":{"type":"number"}},"required":["upstream_ms","cacheHit","id_scheme_version"]}},"required":["id","type","lat","lon","meta"],"description":"The identified place. Confidence is intentionally absent — an id lookup is an identity resolution, not a match."},"AutocompleteItem":{"type":"object","properties":{"id":{"type":"string","description":"Stable public ThinAir id (`ta_place_<key>` / `ta_intersection_<key>`). Resolvable via `GET /v1/place`.","example":"ta_place_8f3a2c9e61c04d58"},"name":{"type":"string"},"label":{"type":"string"},"layer":{"type":"string"},"confidence":{"type":"number"},"lat":{"type":"number"},"lon":{"type":"number"},"country_code":{"type":"string"},"locality":{"type":"string"},"region":{"type":"string"},"distance_km":{"type":"number"}},"required":["lat","lon"]},"AutocompleteResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AutocompleteItem"}},"meta":{"type":"object","properties":{"query":{"type":"string"},"upstream_ms":{"type":"number"},"item_count":{"type":"number"},"cacheHit":{"type":"boolean"},"cacheHitKind":{"type":"string","enum":["v3_hit","legacy_dual_read"],"description":"AC_L1_V3 only: `v3_hit` when the coarse `ac:v3` key served the answer; `legacy_dual_read` when a warm `ac:v2` entry was served via the dual-read fallback. Omitted when the flag is OFF or the answer was not a positive-cache HIT.","example":"v3_hit"},"source":{"type":"string","description":"Where the answer came from: `engine` (live upstream), `cache` (edge prefix cache), `prefilled_cache` (curated/prewarmed entry), or `edge_hotset` (the L(-1) in-isolate runtime hot-set for 1-2 char input). Absent on the degraded/empty paths: below-floor short inputs, negative-cache hits, and timeout-degraded responses.","example":"engine"},"cacheTier":{"type":"string","enum":["l0","kv","edge"],"description":"Present only on a hit from the L(-1) runtime hot-set (`edge`) — drives `X-Cache: HIT-EDGE`. Omitted on every other response.","example":"edge"}},"required":["query","upstream_ms","item_count"]},"warnings":{"type":"array","items":{"type":"string"},"description":"Soft signals: `min_length` (input below the engine floor — 3 chars global, 2 chars when scoped by focus/bounds/country; engine skipped), `focus_distance_filtered` (far results dropped), `no_results_near_focus` (nothing within ~500 km of focus; honest unfiltered set returned), `country_filtered` (a result outside `boundary_country` was dropped by the hard-filter backstop), `bounds_filtered` (a result outside `bounds` was dropped by the rect backstop), `no_focus_ambiguous` (no focus/bounds/country given and the top result's country differs from the caller's geo-IP country), `short_final_token_dropped` (a 1-char final token was stripped from the engine query; the full text is still echoed in `meta.query`), `negative_cached` (served from the short-TTL timeout/empty negative cache), `upstream_degraded` (that negative entry recorded a recent engine timeout), `autocomplete_more_context_required` (2-char input without focus/bounds/country — add one to enable suggestions at this length), `autocomplete_cache_only` (1-char input — served from the prefilled cache only, never the engine), `autocomplete_timeout` (engine exceeded the per-call ceiling; degraded to an empty 200).","example":["no_focus_ambiguous"]}},"required":["items","meta"]},"ReversedFeature":{"type":"object","properties":{"id":{"type":"string","description":"Stable public ThinAir id (`ta_place_<key>` / `ta_intersection_<key>`). Resolvable via `GET /v1/place`.","example":"ta_place_8f3a2c9e61c04d58"},"name":{"type":"string"},"label":{"type":"string"},"confidence":{"type":"number"},"layer":{"type":"string"},"intersection_type":{"type":"string","enum":["corner","interchange"]},"intersection_precision":{"type":"string","enum":["geometric_crossing","tangent_intersection","centroid_cluster","approximate"]},"street_kind":{"type":"string","enum":["street","route"]},"cross_street_kind":{"type":"string","enum":["street","route"]},"street_route":{"type":"string","minLength":1,"maxLength":128},"cross_street_route":{"type":"string","minLength":1,"maxLength":128},"place_type":{"type":"string"},"confidence_scope":{"type":"string","enum":["query_to_candidate"],"description":"What `confidence` is scoped to. `query_to_candidate` means it scores how well this candidate matches the submitted query — it is NOT a probability that the place is real, that the address is correct, or that mail would arrive. Fixed value today; treat it as a closed set that may gain members."},"address_evidence":{"type":"object","properties":{"house_number":{"type":"string","enum":["matched","partial","absent"]},"street":{"type":"string","enum":["matched","partial","absent"]},"locality":{"type":"string","enum":["matched","partial","absent"]},"region":{"type":"string","enum":["matched","partial","absent"]},"postal_code":{"type":"string","enum":["matched","partial","absent"],"description":"Whether the candidate carries a postal code. Presence only — this states what the result has, and makes no claim about its correctness."}},"required":["house_number","street","locality","region","postal_code"],"description":"What THIS CANDIDATE carries, component by component — `matched` (a real value is present), `partial` (only a coarser or derived stand-in, e.g. an interpolated house number or a locality falling back to its administrative parent), `absent` (nothing). This describes the result, NOT a comparison against your query: the engine returns no per-component match data, and re-parsing your text here would mean two parsers disagreeing about the same string. Derived from the feature, never from `confidence`."},"postal_code":{"type":"string","pattern":"^[0-9]{5}$","description":"An eligible US ZIP5 read from the selected result's own postal-code field. While origin enforcement is active this field is emitted ONLY when the engine stamped the value as coming from the address record itself (`source_record` provenance); a value inherited from a parent area, or a result cached before that evidence existed, is omitted rather than emitted without provenance — so an absent ZIP under enforcement means \"not proven for this record\", not \"no such ZIP\". Provenance describes only WHERE the value came from. It is not a postal operator check of any kind — no certification, no address-quality rating, and no statement about whether mail would arrive — so a syntactically correct, correctly-sourced value can still be wrong for the address. Also omitted when unavailable or unsuitable for five-digit form. US address results only. Distinct from `address_evidence.postal_code`, which is a presence STATUS rather than a value. On reverse geocoding this describes the RETURNED ADDRESS — it is not a postal assignment to the coordinate you supplied."},"address_line1":{"type":"string","description":"House number and street for an eligible US address result, composed from the result's own components — never parsed from the display label. Absent when the result is not a US street address."},"address_line2":{"type":"string","description":"Unit or suite, only when the source record carries one. A unit supplied in your request is never echoed back here."},"formatted_address":{"type":"string","description":"Single-line US display form, e.g. `1600 Pennsylvania Avenue NW, Washington, DC 20500, USA`. The postal segment is present only when an eligible ZIP is; its absence never suppresses the street address."},"address_components":{"type":"object","properties":{"house_number":{"type":"string"},"street":{"type":"string"},"unit":{"type":"string"},"locality":{"type":"string"},"region_code":{"type":"string"},"region_name":{"type":"string"},"postal_code":{"type":"string"},"country_code":{"type":"string"}},"description":"The same US components the formatted string is built from. Every field is source-established or absent; nothing is inferred or back-filled."},"method":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"}},"required":["place_type","confidence_scope","address_evidence","method","lat","lon"]},"ReverseMeta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"},"bbox":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]},"query":{"type":"string"},"mode":{"type":"string","enum":["geocode","reverse"]}},"required":["upstream_ms","feature_count","query","mode"]},"ReverseFeatureCollection":{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string","enum":["Point"]},"coordinates":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}]}},"required":["type","coordinates"]},"properties":{"type":"object","additionalProperties":{}}},"required":["type","geometry","properties"]}}},"required":["type","features"]},"ReverseResponse":{"type":"object","properties":{"features":{"type":"array","items":{"$ref":"#/components/schemas/ReversedFeature"}},"meta":{"$ref":"#/components/schemas/ReverseMeta"},"geojson":{"$ref":"#/components/schemas/ReverseFeatureCollection"},"warnings":{"type":"array","items":{"type":"string"},"description":"Geocoder warnings (`geocoding.warnings`) — present only when the suppress middleware emitted signals on this response.","example":["admin_fallback_suppressed"]},"upstream_error":{"type":["string","null"]}},"required":["features","meta","geojson"]},"DirectionsResponse":{"type":"object","properties":{"distance":{"type":"number"},"distance_units":{"type":"string","enum":["miles","kilometers"]},"duration_seconds":{"type":"number"},"base_duration_seconds":{"type":"number","description":"The routing engine's duration with no traffic applied. Equal to duration_seconds."},"effective_duration_seconds":{"type":"number","description":"What to plan against: base_duration_seconds adjusted by any computed traffic delay, else identical to base_duration_seconds."},"traffic_delay_seconds":{"type":"number","description":"effective_duration_seconds minus base_duration_seconds. Positive means traffic made the ETA slower, negative means it made the ETA faster, and zero means the duration was unchanged. For truck costing, general traffic evidence is slowdown-only, so this value is never negative."},"traffic_adjusted_seconds":{"type":"number","description":"Computed traffic-adjusted duration in seconds when traffic produced an adjustment. Use effective_duration_seconds as the authoritative duration for this response."},"base_arrival_utc":{"type":"string","description":"Arrival time assuming base_duration_seconds, in UTC."},"base_arrival_local":{"type":["string","null"],"description":"Same instant as base_arrival_utc, in the destination's local time. Null when no destination timezone was resolved."},"effective_arrival_utc":{"type":"string","description":"Arrival time assuming effective_duration_seconds, in UTC."},"effective_arrival_local":{"type":["string","null"],"description":"Same instant as effective_arrival_utc, in the destination's local time. Null when no destination timezone was resolved."},"has_toll":{"type":"boolean"},"has_highway":{"type":"boolean"},"has_ferry":{"type":"boolean"},"costing":{"type":"string"},"legs":{"type":"array","items":{"type":"object","additionalProperties":{}}},"maneuvers":{"type":"array","items":{"type":"object","additionalProperties":{}}},"maneuver_count":{"type":"number"},"timing":{"type":"object","properties":{"departure_utc":{"type":"string","description":"Departure instant, ISO-8601 UTC. Empty string when no route was produced."},"departure_local":{"type":["string","null"],"description":"Departure in the ORIGIN's local time. Null when the origin timezone was not resolved — never a UTC instant relabelled as local."},"arrival_utc":{"type":"string","description":"Arrival instant, ISO-8601 UTC. Empty string when no route was produced."},"arrival_local":{"type":["string","null"],"description":"Arrival in the DESTINATION's local time. Null when the destination timezone was not resolved."},"origin_timezone":{"type":["string","null"],"description":"IANA timezone at the origin, or null when unresolved. See origin_timezone_status."},"destination_timezone":{"type":["string","null"],"description":"IANA timezone at the destination, or null when unresolved. See destination_timezone_status."},"origin_timezone_status":{"type":"string","enum":["resolved","boundary_ambiguous","source_conflict","not_covered","unavailable"],"description":"Why origin_timezone is what it is. Only `resolved` carries a name; the rest explain the null rather than hiding it."},"destination_timezone_status":{"type":"string","enum":["resolved","boundary_ambiguous","source_conflict","not_covered","unavailable"],"description":"Why destination_timezone is what it is."},"timezone_artifact_version":{"type":["string","null"],"description":"Boundary artifact both timezones came from. Null when none is active."},"duration_seconds":{"type":"number","description":"Routed duration in seconds. 0 when no route was produced."},"timezones_crossed":{"type":"array","items":{"type":"string"},"description":"Distinct IANA zones sampled along the route, present ONLY when the route crosses more than one. An unresolved sample is omitted rather than contributing a fabricated crossing."}},"required":["departure_utc","departure_local","arrival_utc","arrival_local","origin_timezone","destination_timezone","origin_timezone_status","destination_timezone_status","timezone_artifact_version","duration_seconds"]},"origin":{"type":"object","additionalProperties":{}},"destination":{"type":"object","additionalProperties":{}},"geojson":{"type":"object","additionalProperties":{}},"alternates":{"type":"array","items":{"type":"object","properties":{"distance":{"type":"number"},"distance_units":{"type":"string","enum":["miles","kilometers"]},"duration_seconds":{"type":"number"},"legs":{"type":"array","items":{"type":"object","properties":{"leg":{"type":"number"},"distance":{"type":"number"},"duration_seconds":{"type":"number"},"maneuvers":{"type":"number"}},"required":["leg","distance","duration_seconds","maneuvers"]}},"maneuvers":{"type":"array","items":{"type":"object","properties":{"instruction":{"type":"string"},"distance":{"anyOf":[{"type":"number"},{"type":"string"}]},"time":{"type":"number"},"type":{"type":"number"},"begin_shape_index":{"type":"number"}}}},"geojson":{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"id":{"anyOf":[{"type":"string"},{"type":"number"}]},"geometry":{"type":["object","null"],"properties":{"type":{"anyOf":[{"type":"string","enum":["Point","LineString","Polygon","MultiPoint","MultiLineString","MultiPolygon","GeometryCollection"]},{"type":"string"}]},"coordinates":{},"geometries":{"type":"array","items":{}}},"required":["type"]},"properties":{"type":"object","additionalProperties":{}}},"required":["type","geometry"]}}},"required":["type","features"]},"geometry":{"type":"object","additionalProperties":{}}},"required":["distance","distance_units","duration_seconds","legs","maneuvers"]}},"traffic":{"type":"object","properties":{"sample_count":{"type":"integer","minimum":0},"sample_spacing_mi":{"type":"number","minimum":0,"description":"Nominal spacing between traffic observations, in miles, regardless of the route's display units."},"samples":{"type":"array","items":{"type":"object","properties":{"mile":{"type":"number","description":"Approximate mile marker along the route for this observation."},"lat":{"type":"number"},"lon":{"type":"number"},"current_speed":{"type":"number"},"free_flow_speed":{"type":"number"},"congestion":{"type":"string"},"units":{"type":"string"},"measured_mi":{"type":"number","description":"Length of road actually measured by this observation, in miles. Omitted when the provider reported no measurable span."},"measured_start_mi":{"type":"number","description":"Start of the measured span as distance along the route, in miles."},"measured_end_mi":{"type":"number","description":"End of the measured span as distance along the route, in miles."}},"required":["mile","lat","lon"]}},"adjusted_time_factor":{"type":"number"},"adjusted_eta_seconds":{"type":"number"},"error":{"type":"string"}},"required":["sample_count","sample_spacing_mi","samples"],"description":"Traffic observations consulted along this route. Sample span distances are always expressed in miles; provider-specific legacy extensions may also be present."},"timezone_start":{"type":["string","null"],"description":"IANA timezone at the route origin, or null when unresolved. Contains information from timezone-boundary-builder, which is made available under the Open Database License (ODbL) v1.0. Timezone boundaries © OpenStreetMap contributors. US legal-zone cross-check: U.S. Department of Transportation, Bureau of Transportation Statistics (public domain)."},"timezone_end":{"type":["string","null"],"description":"IANA timezone at the route destination, or null when unresolved. Contains information from timezone-boundary-builder, which is made available under the Open Database License (ODbL) v1.0. Timezone boundaries © OpenStreetMap contributors. US legal-zone cross-check: U.S. Department of Transportation, Bureau of Transportation Statistics (public domain)."},"timezone_start_status":{"type":"string","enum":["resolved","boundary_ambiguous","source_conflict","not_covered","unavailable"]},"timezone_end_status":{"type":"string","enum":["resolved","boundary_ambiguous","source_conflict","not_covered","unavailable"]},"timezone_artifact_version":{"type":["string","null"]},"elevation_gain_m":{"type":["number","null"]},"elevation_loss_m":{"type":["number","null"]},"elevation_gain_ft":{"type":["number","null"]},"elevation_loss_ft":{"type":["number","null"]},"elevation_start_m":{"type":["number","null"]},"elevation_end_m":{"type":["number","null"]},"elevation_start_ft":{"type":["number","null"]},"elevation_end_ft":{"type":["number","null"]},"traffic_provenance":{"type":"object","properties":{"mode":{"type":"string","enum":["none","eta_enrichment","candidate_ranking","native_live_routing","native_predicted_routing"],"description":"Which traffic-processing rung this request attempted. `none`: no traffic data was consulted. `eta_enrichment`: after the route was chosen, traffic evidence was sought only to adjust its estimated duration; the roads returned are unchanged. The attempt may fail or produce no adjustment, so inspect `outcome` and `duration_influenced` for what reached this answer. `candidate_ranking`: traffic re-ranked several candidate routes against each other. `native_live_routing`: live speeds were used to choose which roads to take. `native_predicted_routing`: as above, plus time-of-day predicted speeds for a future departure."},"outcome":{"type":"string","enum":["TRAFFIC_DISABLED","PROVIDER_DISABLED","QUOTA_EXHAUSTED","NO_SEGMENT_MATCH","INSUFFICIENT_COVERAGE","STALE_EVIDENCE","UPSTREAM_ERROR","APPLIED"],"description":"Neutral, stable reason code for what happened to traffic evidence on this request. `TRAFFIC_DISABLED`: no traffic evidence was sought. `PROVIDER_DISABLED`: evidence was sought but the traffic backend was not configured. `QUOTA_EXHAUSTED`: the traffic backend's spend allotment was exhausted. `NO_SEGMENT_MATCH`: the backend answered but no sample matched a live road segment. `INSUFFICIENT_COVERAGE`: the backend reported no sensor coverage for the sampled area. `STALE_EVIDENCE`: an adjustment was computed, but the evidence behind it was fetched more than 30 minutes ago because the provider could not refresh it. `UPSTREAM_ERROR`: a configured backend or network failure prevented reading all requested traffic evidence. `APPLIED`: an adjustment was computed and reached this response; inspect `retrieved_at` / `retrieval_age_seconds` to see how old the evidence behind it is (acquisition age — this traffic source reports no observation time, see those fields' own descriptions). Material failure codes take precedence over `APPLIED` when only partial evidence adjusted the duration; `duration_influenced` remains true in that case."},"path_influenced":{"type":"boolean","description":"Whether traffic could have changed WHICH roads were returned. False today: the route is selected from the road network's own speeds, so traffic never changes the path — only the estimated duration."},"duration_influenced":{"type":"boolean","description":"Whether traffic actually changed the duration returned by THIS request. False when no traffic adjustment was applied, even if traffic was requested and fetched successfully."},"source_as_of":{"type":["string","null"],"description":"DEPRECATED since 2026-09-24, always null. The traffic source provides no observation timestamp, so this field can never be filled honestly. See `retrieved_at` for when ThinAir fetched the traffic evidence (not when it was observed). Will be removed in /v2.","deprecated":true},"route_coverage_pct":{"type":["number","null"],"description":"Percentage of the route's distance backed by a live traffic observation. This is a MEASURED figure, not an attributed one: it sums the length of road each traffic observation actually matched and divides by the route distance. Coverage can range from sparse observations to most of a corridor, depending on the evidence available for this request; use the returned value rather than assuming a sampling pattern. A low percentage does not mean the ETA is unreliable; it means most of the route's duration comes from the road network's own speeds rather than from live observation. Null when no live traffic observation carried a measurable length."},"freshness_seconds":{"type":["number","null"],"description":"DEPRECATED since 2026-09-24, always null — same reason as `source_as_of`. See `retrieval_age_seconds` for the acquisition age ThinAir CAN report honestly. Will be removed in /v2.","deprecated":true},"retrieved_at":{"type":["string","null"],"description":"ISO 8601 time when ThinAir fetched the traffic evidence behind this response (point or tile). This is acquisition metadata, not the time the traffic was observed — the traffic source reports no observation time (see `source_as_of`). Null when no consulted sample's acquisition time was determinable."},"retrieval_age_seconds":{"type":["number","null"],"description":"Age, in seconds, of `retrieved_at` — how long ago ThinAir fetched the traffic evidence behind this response (point or tile). This is acquisition/cache age, not traffic-observation freshness. Null under the same condition as `retrieved_at`."},"routing_release_id":{"type":["string","null"],"description":"Identifier of the road-network build that produced this route. Null today: the routing engine does not report one."},"overlay_epoch":{"type":["string","null"],"description":"Identifier of the live-speed overlay applied to the road network. Null today: no live-speed overlay is in use."}},"required":["mode","outcome","path_influenced","duration_influenced","source_as_of","route_coverage_pct","freshness_seconds","retrieved_at","retrieval_age_seconds","routing_release_id","overlay_epoch"],"additionalProperties":false,"description":"What traffic actually did to this response: whether it changed the roads chosen, whether it changed the duration, and how fresh the underlying observations were. Always present — when no traffic data was consulted it reports mode `none` rather than being omitted."},"meta":{"type":"object","additionalProperties":{}}},"required":["distance","distance_units","duration_seconds","base_duration_seconds","effective_duration_seconds","traffic_delay_seconds","base_arrival_utc","base_arrival_local","effective_arrival_utc","effective_arrival_local","has_toll","has_highway","has_ferry","costing","legs","maneuvers","maneuver_count","timing","origin","destination","timezone_start","timezone_end","timezone_start_status","timezone_end_status","timezone_artifact_version","elevation_gain_m","elevation_loss_m","elevation_gain_ft","elevation_loss_ft","elevation_start_m","elevation_end_m","elevation_start_ft","elevation_end_ft","traffic_provenance","meta"],"description":"Routing result. `geojson` (FeatureCollection) present when the upstream returned shape geometry. Upstream-emitted fields (`confidence`, `method`, `layer`, `source` on origin/destination resolution) are passed through unchanged. `traffic_provenance` states what traffic did to this specific result."},"DirectionsRequest":{"type":"object","properties":{"origin":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"example":"Houston, TX"},{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lon"],"example":{"lat":29.7604,"lon":-95.3698}}],"description":"Free-text address ('Houston, TX', '4131 Center St, Houston, TX 77007') OR a coordinate pair { lat, lon }."},"destination":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"example":"Houston, TX"},{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lon"],"example":{"lat":29.7604,"lon":-95.3698}}],"description":"Free-text address ('Houston, TX', '4131 Center St, Houston, TX 77007') OR a coordinate pair { lat, lon }."},"via":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"example":"Houston, TX"},{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lon"],"example":{"lat":29.7604,"lon":-95.3698}}],"description":"Free-text address ('Houston, TX', '4131 Center St, Houston, TX 77007') OR a coordinate pair { lat, lon }."},"maxItems":48,"description":"Intermediate stops in order. Same shape as origin/destination. Up to 48 waypoints (max 50 total locations)."},"alternates":{"type":"integer","minimum":0,"maximum":2,"description":"Number of genuine motorized alternate routes to request (0, 1, or 2). Positive values require no via waypoints."},"costing":{"type":"string","enum":["auto","truck","bicycle","pedestrian","motorcycle"],"description":"Transport mode. Default: auto.","example":"auto"},"preset":{"type":"string","maxLength":64,"description":"Routing preset — one of: car_default, truck_53, truck_box26, truck_tanker, truck_flatbed, truck_step_deck, truck_auto_carrier, truck_sprinter, truck_double, truck_oversize, motorcycle, scooter, bike, walk, transit. Sets costing AND dimensions together. NOTE: these are NOT the `vehicle_profile` names — passing a vehicle_profile id here is not recognised and currently falls back to a CAR route.","example":"truck_53"},"vehicle_profile":{"type":"string","maxLength":64,"description":"Truck dimension profile (10): DRY_VAN_53, FLATBED_48, FLATBED_40, STEP_DECK, TANKER, BOX_TRUCK_26, AUTO_CARRIER, SPRINTER_VAN, DOUBLE_TRAILER, OVERSIZE. Sets height/weight/length/axles. Distinct from `preset`, which also sets costing.","example":"DRY_VAN_53"},"truck_height":{"type":"number","exclusiveMinimum":0,"maximum":30,"description":"Truck height (m)","example":4.11},"truck_weight":{"type":"number","exclusiveMinimum":0,"maximum":200,"description":"Truck weight (metric tons)","example":36.3},"truck_width":{"type":"number","exclusiveMinimum":0,"maximum":10},"truck_length":{"type":"number","exclusiveMinimum":0,"maximum":60},"hazmat":{"type":"boolean","description":"Routing avoids hazmat-restricted roads when true."},"axle_load":{"type":"number","exclusiveMinimum":0,"maximum":100},"axle_count":{"type":"integer","exclusiveMinimum":0,"maximum":20},"top_speed":{"type":"number","exclusiveMinimum":0,"maximum":300},"use_highways":{"type":"number","minimum":0,"maximum":1},"use_ferry":{"type":"number","minimum":0,"maximum":1},"avoid_tolls":{"type":"boolean"},"include_traffic":{"type":"boolean","description":"Apply post-route live-traffic ETA enrichment. Default: true. Traffic does not change the returned path (path_influenced=false); truck adjustments are slowdown-only."},"include_weather":{"type":"boolean"},"include_elevation":{"type":"boolean","description":"When true, include available route elevation fields. Point elevation may be returned for the origin and destination. Cumulative gain/loss is returned only when route-profile elevation is available. Default: false (an extra upstream lookup, opt-in only)."},"units":{"type":"string","enum":["miles","kilometers"],"description":"Distance units. Default: miles. Accepts 'km'/'mi' as aliases.","example":"miles"},"depart_at":{"type":"string","format":"date-time","description":"Departure time (ISO 8601). Default: now."},"include_geometry":{"type":"string","enum":["none","encoded","full"],"description":"Route geometry shape control. \"none\" returns origin/destination Points only. \"encoded\" returns an encoded polyline6 string in a top-level `geometry` field plus origin/destination Points (≈95% smaller than raw GeoJSON; decode with @mapbox/polyline at precision 6). \"full\" emits decoded coordinates as a LineString in `geojson.features[0]`. When omitted (default), small routes use \"full\" and long routes auto-downgrade to \"encoded\" with a `geometry_truncated` warning."}},"required":["origin","destination"],"description":"Routing request — origin + destination (free text or coordinate pair), optional waypoints, costing model, and per-mode parameters. Traffic is post-route ETA enrichment only; it does not change the selected path."},"LocateRoad":{"type":["object","null"],"properties":{"name":{"type":["string","null"]},"road_class":{"type":["string","null"]},"speed_limit_kph":{"type":["number","null"]},"speed_limit_mph":{"type":["number","null"]},"bearing":{"type":["number","null"]},"distance_m":{"type":["number","null"]},"distance_ft":{"type":["number","null"]},"side_of_street":{"type":"string","enum":["left","right","unknown"]},"truck_legal":{"type":["boolean","null"]},"has_toll":{"type":"boolean"},"has_ferry":{"type":"boolean"},"has_highway":{"type":"boolean"},"bridge":{"type":"boolean"},"tunnel":{"type":"boolean"}},"required":["name","road_class","speed_limit_kph","speed_limit_mph","bearing","distance_m","distance_ft","side_of_street","truck_legal","has_toll","has_ferry","has_highway","bridge","tunnel"]},"LocateGeoJson":{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string","enum":["Point"]},"coordinates":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}]}},"required":["type","coordinates"]},"properties":{"type":"object","additionalProperties":{}}},"required":["type","geometry","properties"]}}},"required":["type","features"]},"LocateMeta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean","description":"True when the result was served from the edge grid cache (no upstream call)."},"units":{"type":"string","enum":["imperial","metric"]},"include_elevation":{"type":"boolean"},"degraded":{"type":"boolean","description":"Present and `true` only when `road_status` is `unavailable`: the road lookup could not be answered, so the result carries the timezone (and elevation, when requested and available) but no road information."},"elevation_source":{"type":["string","null"],"enum":["terrain"],"description":"Provenance of `elevation_m` — `terrain` when sampled from terrain/DEM data, or `null` when unavailable.","example":null}},"required":["upstream_ms","feature_count","units","include_elevation","elevation_source"]},"LocateResponse":{"type":"object","properties":{"summary":{"type":"string","description":"Human/LLM-friendly summary of the road match."},"matched":{"type":"boolean"},"road_status":{"type":"string","enum":["matched","no_road_nearby","unavailable"],"description":"Why `road` is what it is. `matched` - a routable road was found. `no_road_nearby` - the road lookup answered and found no routable road for this travel profile (a real answer; may be cached). `unavailable` - the road lookup could not be answered right now; this says nothing about the point, `matched` is `false`, `road` is `null`, `retryable` is `true`, and the result is never cached. The timezone and elevation fields are still resolved.","example":"matched"},"retryable":{"type":"boolean","description":"Present and `true` only when `road_status` is `unavailable`: the same request may succeed shortly."},"location":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"}},"required":["lat","lon"]},"road":{"$ref":"#/components/schemas/LocateRoad"},"timezone":{"type":["string","null"],"description":"IANA timezone name for the point (e.g. `America/Los_Angeles`), resolved by point-in-polygon against published timezone boundaries. Null unless `timezone_status` is `resolved` — an absent timezone is reported rather than an estimated one. Contains information from timezone-boundary-builder, which is made available under the Open Database License (ODbL) v1.0. Timezone boundaries © OpenStreetMap contributors. US legal-zone cross-check: U.S. Department of Transportation, Bureau of Transportation Statistics (public domain).","example":"America/Los_Angeles"},"timezone_status":{"type":"string","enum":["resolved","boundary_ambiguous","source_conflict","not_covered","unavailable"],"description":"Why `timezone` is what it is. `resolved` — exactly one zone contains the point, clear of every boundary. `boundary_ambiguous` — on or within the documented boundary tolerance of an edge, or claimed by two zones. `source_conflict` — the boundary data and the US legal-zone layer disagree here. `not_covered` — no zone polygon contains the point. `unavailable` — no verified boundary artifact is active. Only `resolved` carries a name.","example":"resolved"},"timezone_artifact_version":{"type":["string","null"],"description":"Version of the timezone boundary artifact that produced this answer. Changes when the data is promoted, and participates in this endpoint's cache identity so an answer cannot outlive the data it came from.","example":"2026c-g2-d5dbf146f5c54527"},"elevation_m":{"type":["number","null"],"description":"Ground elevation in metres. Non-null only when `include_elevation=true` AND terrain data is available; `null` otherwise.","example":null},"elevation_ft":{"type":["number","null"],"example":null},"grade_pct":{"type":["number","null"],"description":"Road grade at the point, percent. Reserved — `null` until an elevation-enhanced road network or verified post-processing are live.","example":null},"geojson":{"$ref":"#/components/schemas/LocateGeoJson"},"warnings":{"type":"array","items":{"type":"string"}},"meta":{"$ref":"#/components/schemas/LocateMeta"},"upstream_error":{"type":["string","null"]}},"required":["summary","matched","road_status","location","road","timezone","timezone_status","timezone_artifact_version","elevation_m","elevation_ft","grade_pct","geojson","warnings","meta"]},"QuotaResponse":{"type":"object","properties":{"schema_version":{"type":"string","enum":["v1"]},"mcp":{"type":"object","properties":{"plan":{"type":"string","example":"free"},"used_today":{"type":"integer","minimum":0},"daily_limit":{"anyOf":[{"type":"integer","minimum":0},{"type":"string","enum":["unlimited"]}]},"remaining_today":{"anyOf":[{"type":"integer","minimum":0},{"type":"string","enum":["unlimited"]}]},"monthly_limit":{"anyOf":[{"type":"integer","minimum":0},{"type":"string","enum":["unlimited"]}]},"price_per_month":{"type":"string","example":"$0"},"suggested_upgrade":{"type":["string","null"]},"upgrade_url":{"type":["string","null"]},"reset_at":{"type":"string","example":"00:00 UTC daily"}},"required":["plan","used_today","daily_limit","remaining_today","monthly_limit","price_per_month","suggested_upgrade","upgrade_url","reset_at"]}},"required":["schema_version","mcp"],"description":"Caller's current quota — plan, daily usage, remaining, upgrade options. Mirrors the MCP `quota` tool response shape so REST and MCP stay symmetric."},"IntersectionResolvedFeature":{"type":["object","null"],"properties":{"id":{"type":"string","description":"Stable public ThinAir id (`ta_intersection_<key>` when the resolved doc carries the derivation inputs, else `ta_place_<key>`). Resolvable via `GET /v1/place`.","example":"ta_intersection_b3a9f27e61c04d58"},"name":{"type":"string"},"label":{"type":"string"},"confidence":{"type":"number"},"layer":{"type":"string"},"intersection_type":{"type":"string","enum":["corner","interchange"]},"intersection_precision":{"type":"string","enum":["geometric_crossing","tangent_intersection","centroid_cluster","approximate"]},"street_kind":{"type":"string","enum":["street","route"]},"cross_street_kind":{"type":"string","enum":["street","route"]},"street_route":{"type":"string","minLength":1,"maxLength":128},"cross_street_route":{"type":"string","minLength":1,"maxLength":128},"place_type":{"type":"string"},"method":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"}},"required":["place_type","method","lat","lon"],"description":"Best resolved intersection feature, or null when nothing resolved."},"IntersectionResolveResponse":{"type":"object","properties":{"feature":{"$ref":"#/components/schemas/IntersectionResolvedFeature"},"confidence":{"type":"number","description":"Resolved feature's confidence (0–1); 0 when unresolved."},"fallback_used":{"type":"boolean","description":"True when the geocoder fallback produced the answer (CSV missed or was below threshold)."},"admin":{"type":"object","properties":{"locality":{"type":"string"},"region":{"type":"string"},"country":{"type":"string"}},"description":"Admin context echoed back."},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"method":{"type":"string"},"query":{"type":"string"}},"required":["upstream_ms","method","query"],"description":"Resolution metadata; `method` is the rung that resolved (csv_intersection | intersection | none)."},"warnings":{"type":"array","items":{"type":"string"},"description":"Advisory strings — present on a miss (coverage hint) or geocoder suppress signals."},"upstream_error":{"type":["string","null"]},"precision":{"allOf":[{"$ref":"#/components/schemas/PrecisionBlock"},{"description":"Learning-overlay precision adjudication. Present only when the flag-gated overlay applied to this resolution. Omitted otherwise — legacy consumers see no shape change."}]}},"required":["feature","confidence","fallback_used","admin","meta"]},"IntersectionResolveRequest":{"type":"object","properties":{"street1":{"type":"string","minLength":1,"maxLength":2000,"description":"First street of the cross-street pair.","example":"Bellaire Blvd"},"street2":{"type":"string","minLength":1,"maxLength":2000,"description":"Second (cross) street of the pair.","example":"Chimney Rock Rd"},"locality":{"type":"string","maxLength":2000,"description":"City / town that scopes the lookup.","example":"Houston"},"region":{"type":"string","maxLength":2000,"description":"State / province (full name or abbreviation, e.g. 'TX').","example":"TX"},"country":{"type":"string","minLength":2,"maxLength":3,"description":"ISO 3166-1 alpha-2/alpha-3 country code to scope results.","example":"US"},"confidence_threshold":{"type":"number","minimum":0,"maximum":1,"description":"Confidence floor (0–1) below which a CSV/geocoder hit falls through to the next resolution rung. Defaults to 0.5.","example":0.5}},"required":["street1","street2"],"description":"Resolve a named cross-street to coordinates. The high-confidence tier (CSV) is US highways only; named non-highway streets resolve via the geocoder fallback (global, lower-confidence). For free-form streets use /v1/geocode."},"IntersectionSearchFeature":{"type":"object","properties":{"id":{"type":"string","description":"Stable public ThinAir id (`ta_intersection_<key>` / `ta_place_<key>`). Resolvable via `GET /v1/place`.","example":"ta_intersection_b3a9f27e61c04d58"},"name":{"type":"string"},"label":{"type":"string"},"confidence":{"type":"number"},"layer":{"type":"string"},"intersection_type":{"type":"string","enum":["corner","interchange"]},"intersection_precision":{"type":"string","enum":["geometric_crossing","tangent_intersection","centroid_cluster","approximate"]},"street_kind":{"type":"string","enum":["street","route"]},"cross_street_kind":{"type":"string","enum":["street","route"]},"street_route":{"type":"string","minLength":1,"maxLength":128},"cross_street_route":{"type":"string","minLength":1,"maxLength":128},"place_type":{"type":"string"},"method":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"}},"required":["place_type","method","lat","lon"]},"IntersectionSearchMeta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"cacheHit":{"type":"boolean"},"bbox":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}]},"query":{"type":"string"},"mode":{"type":"string","enum":["geocode","reverse"]}},"required":["upstream_ms","feature_count","query","mode"]},"IntersectionSearchFeatureCollection":{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string","enum":["Point"]},"coordinates":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}]}},"required":["type","coordinates"]},"properties":{"type":"object","additionalProperties":{}}},"required":["type","geometry","properties"]}}},"required":["type","features"]},"IntersectionSearchResponse":{"type":"object","properties":{"features":{"type":"array","items":{"$ref":"#/components/schemas/IntersectionSearchFeature"}},"meta":{"$ref":"#/components/schemas/IntersectionSearchMeta"},"geojson":{"$ref":"#/components/schemas/IntersectionSearchFeatureCollection"},"warnings":{"type":"array","items":{"type":"string"}},"upstream_error":{"type":["string","null"]}},"required":["features","meta","geojson"]},"IntersectionSearchRequest":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":2000,"description":"Free-text intersection query (e.g. 'Broadway and 42nd Street').","example":"Bellaire Blvd and Chimney Rock Rd"},"locality":{"type":"string","maxLength":2000,"description":"City / town hint.","example":"Houston"},"region":{"type":"string","maxLength":2000,"description":"State / province hint.","example":"TX"},"country":{"type":"string","minLength":2,"maxLength":3,"description":"ISO 3166-1 alpha-2/alpha-3 country code to scope results.","example":"US"},"size":{"type":"integer","minimum":1,"maximum":40,"description":"Maximum candidates to return (1–40). Defaults to 5 in the service layer.","example":5}},"required":["text"],"description":"Search for intersection candidates from a free-text cross-street query."},"DistanceMatrixLocation":{"type":"object","properties":{"lat":{"type":"number"},"lon":{"type":"number"},"label":{"type":"string"},"confidence":{"type":"number"},"method":{"type":"string"}},"required":["lat","lon"]},"DistanceMatrixCell":{"type":"object","properties":{"distance":{"type":["number","null"],"description":"Road distance in the requested units; null when no route."},"time_seconds":{"type":["number","null"],"description":"Travel time in seconds; null when no route."}},"required":["distance","time_seconds"]},"DistanceMatrixResponse":{"type":"object","properties":{"origins":{"type":"array","items":{"$ref":"#/components/schemas/DistanceMatrixLocation"}},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/DistanceMatrixLocation"}},"matrix":{"type":"array","items":{"type":"array","items":{"$ref":"#/components/schemas/DistanceMatrixCell"}},"description":"matrix[i][j] = origin i → destination j."},"units":{"type":"string","enum":["miles","kilometers"]},"costing":{"type":"string"},"duration_basis":{"type":"string","enum":["static_road_speeds"],"description":"How matrix durations were computed. `static_road_speeds` (the only value today): durations come from the road network's own speed data — posted limits and road-class defaults — never from historical or live traffic. Use `directions` for a traffic-adjusted ETA between two specific points."},"traffic_applied":{"type":"boolean","enum":[false],"description":"Whether live or historical traffic influenced any matrix duration. Always false today — see `duration_basis`."},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"origin_count":{"type":"number"},"destination_count":{"type":"number"}},"required":["upstream_ms","origin_count","destination_count"]},"upstream_error":{"type":["string","null"]}},"required":["origins","destinations","matrix","units","costing","duration_basis","traffic_applied","meta"]},"DistanceMatrixRequest":{"type":"object","properties":{"origins":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"example":"Houston, TX"},{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lon"],"example":{"lat":29.7604,"lon":-95.3698}}],"description":"Free-text address ('Houston, TX') OR a coordinate pair { lat, lon }."},"minItems":1,"maxItems":25,"description":"Origin endpoints (1–25). Each is free text or { lat, lon }."},"destinations":{"type":"array","items":{"anyOf":[{"type":"string","minLength":1,"maxLength":2000,"example":"Houston, TX"},{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lon"],"example":{"lat":29.7604,"lon":-95.3698}}],"description":"Free-text address ('Houston, TX') OR a coordinate pair { lat, lon }."},"minItems":1,"maxItems":25,"description":"Destination endpoints (1–25). origins.length × destinations.length must be ≤ 625."},"costing":{"type":"string","enum":["auto","truck","bicycle","pedestrian","motorcycle"],"description":"Transport mode. Default: auto.","example":"auto"},"units":{"type":"string","enum":["miles","kilometers"],"description":"Distance units. Default: kilometers.","example":"kilometers"}},"required":["origins","destinations"],"description":"N origins × M destinations distance + duration matrix. Endpoints accept free-text addresses or coordinate pairs; N × M is capped at 625 pairs."},"GeofenceContainsResponse":{"type":"object","properties":{"results":{"type":"array","items":{"type":"boolean"},"description":"results[i] = true when points[i] is inside the polygon (boundary counts as inside)."},"contained_count":{"type":"integer"},"point_count":{"type":"integer"},"warnings":{"type":"array","items":{"type":"string"}}},"required":["results","contained_count","point_count"]},"GeofencePolygon":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["Polygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":3}}}},"required":["type","coordinates"]},{"type":"object","properties":{"type":{"type":"string","enum":["MultiPolygon"]},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"type":"number"},"minItems":2,"maxItems":3}}}}},"required":["type","coordinates"]},{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"]},"properties":{"type":["object","null"],"additionalProperties":{}}},"required":["type","geometry"]},{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"]},"properties":{"type":["object","null"],"additionalProperties":{}}},"required":["type","geometry"]},"maxItems":100}},"required":["type","features"]}],"description":"A GeoJSON Polygon / MultiPolygon geometry, a Feature wrapping one, or a FeatureCollection of them. Ring geometry is validated server-side (closure, winding, self-intersection, complexity limits)."},"GeofencePoint":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}},"required":["lat","lon"]},"GeofenceContainsRequest":{"type":"object","properties":{"polygon":{"$ref":"#/components/schemas/GeofencePolygon"},"points":{"type":"array","items":{"$ref":"#/components/schemas/GeofencePoint"},"maxItems":1000,"description":"Points to test for containment (up to 1000). { lat, lon } each."}},"required":["polygon","points"]},"GeofenceValidateResponse":{"type":"object","properties":{"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}}},"required":["valid","errors","warnings"]},"GeofenceValidateRequest":{"type":"object","properties":{"polygon":{"$ref":"#/components/schemas/GeofencePolygon"}},"required":["polygon"]},"StructuredGeocodeRequest":{"type":"object","properties":{"address":{"type":"string","minLength":1,"maxLength":2000,"description":"Street address — house number + street name.","example":"1600 Pennsylvania Ave NW"},"locality":{"type":"string","minLength":1,"maxLength":2000,"description":"City / town name.","example":"Washington"},"region":{"type":"string","minLength":1,"maxLength":2000,"description":"State / province — full name or abbreviation (e.g. 'DC').","example":"DC"},"postalcode":{"type":"string","minLength":1,"maxLength":32,"description":"Postal / ZIP code.","example":"20500"},"country":{"type":"string","minLength":2,"maxLength":64,"description":"ISO 3166-1 alpha-2/alpha-3 country code (full names also resolve).","example":"US"},"size":{"type":"integer","minimum":1,"maximum":40,"description":"Maximum candidates to return (1–40). Defaults to 5.","example":5},"boundary_country":{"type":"string","minLength":2,"maxLength":3,"description":"Scope results to this ISO country code — wins over `country` for boundary filtering.","example":"US"},"focus_lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude of the proximity bias point.","example":38.8977},"focus_lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude of the proximity bias point.","example":-77.0365}},"description":"Component-wise geocode request. At least one component field is required. There is no cross-street field — use POST /v1/intersection for named cross-streets."},"BatchGeocodeRestItem":{"type":"object","properties":{"input":{"type":"string"},"name":{"type":"string"},"label":{"type":"string"},"confidence":{"type":"number"},"layer":{"type":"string"},"intersection_type":{"type":"string","enum":["corner","interchange"]},"intersection_precision":{"type":"string","enum":["geometric_crossing","tangent_intersection","centroid_cluster","approximate"]},"street_kind":{"type":"string","enum":["street","route"]},"cross_street_kind":{"type":"string","enum":["street","route"]},"street_route":{"type":"string","minLength":1,"maxLength":128},"cross_street_route":{"type":"string","minLength":1,"maxLength":128},"place_type":{"type":"string"},"method":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"low_confidence":{"type":"boolean","enum":[true]},"error":{"type":"string"},"error_message":{"type":"string"}},"required":["input"],"description":"One row per input address, order-preserving. Success rows carry the geocoded feature fields; failure rows carry `error` (+ `error_message`) instead — the two sets are disjoint."},"BatchGeocodeResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/BatchGeocodeRestItem"}},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"feature_count":{"type":"number"},"low_confidence_count":{"type":"number"},"failed_count":{"type":"number"},"low_confidence_threshold":{"type":"number"}},"required":["upstream_ms","feature_count","low_confidence_count","failed_count","low_confidence_threshold"]},"geojson":{"type":"object","properties":{"type":{"type":"string","enum":["FeatureCollection"]},"features":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["Feature"]},"geometry":{"type":"object","properties":{"type":{"type":"string","enum":["Point"]},"coordinates":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}]}},"required":["type","coordinates"]},"properties":{"type":"object","additionalProperties":{}}},"required":["type","geometry","properties"]}}},"required":["type","features"],"description":"FeatureCollection built from the successful (coordinate-bearing) rows only — failed rows still appear in `items`."}},"required":["items","meta","geojson"]},"BatchGeocodeRequest":{"type":"object","properties":{"addresses":{"type":"array","items":{"type":"string","maxLength":2000},"minItems":1,"maxItems":50,"description":"Addresses to geocode, in order (1–50 per batch). Empty rows are echoed back as per-row `empty_address` errors rather than failing the batch.","example":["1600 Pennsylvania Ave NW, Washington, DC","Austin, TX"]}},"required":["addresses"]},"MapMatchResponse":{"type":"object","properties":{"matched":{"type":"boolean","description":"True when a route was returned for the trace. It does NOT mean the whole trace matched — use `match_status`."},"distance_km":{"type":"number"},"duration_seconds":{"type":"number"},"confidence":{"type":"number","description":"0–1: fraction of input GPS points within the snap tolerance of the matched route. A distance heuristic: it ignores point order and which parallel roadway was matched. For the matcher's own per-point result use `match_status` / `coverage`."},"road_names":{"type":"array","items":{"type":"string"}},"legs":{"type":"array","items":{"type":"object","additionalProperties":{}}},"costing":{"type":"string"},"geojson":{"type":"object","additionalProperties":{}},"geometry":{"type":"object","additionalProperties":{}},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"point_count":{"type":"integer"},"breakage_distance_m":{"type":"integer","description":"Echoes the breakage_distance_m the caller requested, for confirmation — present only when the caller set it. This is the value SENT to the matcher, not a claim about what it did with it."}},"required":["upstream_ms","point_count"]},"match_status":{"type":["string","null"],"enum":["full","partial","none"],"description":"Whole-trace match status from the matcher's own per-point results. `full`: every input point was matched or interpolated onto the road network AND the match is one continuous section. `none`: no input point was matched or interpolated. `partial`: anything else — some points unmatched, or the match broke into more than one section. null when the per-point check could not be completed; `coverage.unavailable_reason` says why. Gate on this field, not on `matched`."},"coverage":{"type":"object","properties":{"point_count":{"type":"integer","description":"Number of input GPS points"},"matched_count":{"type":["integer","null"],"description":"Input points the matcher placed on the road network directly. null when unavailable."},"interpolated_count":{"type":["integer","null"],"description":"Input points the matcher placed on the road network by interpolating between matched neighbours; counted as covered. null when unavailable."},"unmatched_count":{"type":["integer","null"],"description":"Input points the matcher could not place on the road network. null when unavailable."},"ratio":{"type":["number","null"],"description":"0–1: (matched_count + interpolated_count) / point_count, rounded down to 3 decimals so a partial trace never reads 1. null when unavailable."},"unmatched_ranges":{"type":["array","null"],"items":{"type":"object","properties":{"start_index":{"type":"integer","description":"0-based index of the first unmatched input point in this run"},"end_index":{"type":"integer","description":"0-based index of the last unmatched input point in this run (inclusive)"},"point_count":{"type":"integer","description":"Number of input points in this run"}},"required":["start_index","end_index","point_count"]},"description":"Contiguous runs of unmatched input points, as 0-based inclusive index ranges into `shape`, in trace order. Empty when every point was covered; null when unavailable."},"route_sections":{"type":"integer","description":"Number of disjoint sections the match broke into (1 = continuous). When greater than 1, the returned geometry, distance and duration cover only the FIRST section — split the trace at the break and match the parts separately."},"unavailable_reason":{"type":["string","null"],"enum":["upstream_error","timeout","no_point_status","point_count_mismatch","unknown_point_type"],"description":"Why the per-point counts and `match_status` are null: `upstream_error` (the per-point check failed), `timeout` (it did not answer in time), `no_point_status` (it answered without per-point results), `point_count_mismatch` (its results did not line up one-to-one with the input points), `unknown_point_type` (it reported a status this API does not recognise). null when coverage is available."}},"required":["point_count","matched_count","interpolated_count","unmatched_count","ratio","unmatched_ranges","route_sections","unavailable_reason"],"description":"Per-point coverage of the trace from the matcher's own per-point status — independent of the distance-based `confidence`. Every key is always present."}},"required":["matched","distance_km","duration_seconds","confidence","road_names","legs","costing","meta","match_status","coverage"],"description":"Matched-route result. `geojson` carries the snapped LineString for small traces; longer traces return `geometry.encoded` (polyline6 — decode with @mapbox/polyline at precision 6). `match_status` and `coverage` report how much of the trace the matcher actually placed on the road network — `matched` alone only says a route was returned. Road names/classes and distances are surfaced; internal road-graph identifiers are never exposed."},"MapMatchRequest":{"type":"object","properties":{"shape":{"type":"array","items":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180},"time":{"type":"number","minimum":0,"description":"Optional epoch seconds for this fix — timestamps improve match quality on noisy traces."}},"required":["lat","lon"],"example":{"lat":47.6205,"lon":-122.3493}},"minItems":2,"maxItems":100,"description":"Raw GPS breadcrumb trail in travel order — 2 to 100 points of { lat, lon, time? }."},"costing":{"type":"string","enum":["auto","truck","bicycle","pedestrian"],"description":"Transport mode the trace was driven with. Default: auto.","example":"auto"},"vehicle_profile":{"type":"string","maxLength":64,"description":"Truck preset (DRY_VAN_53, FLATBED_48, BOX_TRUCK_26, …) — same 10-profile table as /v1/directions. Implies costing=truck."},"search_radius":{"type":"number","minimum":1,"maximum":100,"description":"Snap radius in meters around each point (1–100). Increase for noisy GPS."},"breakage_distance_m":{"type":"integer","minimum":100,"maximum":20000,"description":"Maximum gap, in meters, between consecutive fixes before the trace is split into separate sections (100–20000). Increase for sparsely-reported GPS, such as fleet units reporting every several minutes on a highway. Optional — omitted uses the default."}},"required":["shape"],"description":"Map-matching request — a raw GPS breadcrumb trail (2–100 points) to snap to the road network, with optional costing / truck profile / snap radius."},"TraceEdge":{"type":"object","properties":{"name":{"type":"string","description":"Road name (omitted for unnamed ways)."},"road_class":{"type":"string","description":"Road class (motorway, primary, residential, …)."},"speed_limit_kph":{"type":"number","description":"Posted speed limit in km/h, when mapped."},"length_m":{"type":"number","description":"Segment length in meters."},"surface":{"type":"string","description":"Surface type (paved_smooth, gravel, …)."},"truck_route":{"type":"boolean","description":"True when the segment is a designated truck route."},"elevation_m":{"type":["number","null"],"description":"Reserved until an elevation-enhanced road network is promoted — null today."},"elevation_ft":{"type":["number","null"],"description":"Reserved until an elevation-enhanced road network is promoted — null today."},"grade_pct":{"type":["number","null"],"description":"Reserved until an elevation-enhanced road network is promoted — null today."},"weighted_grade":{"type":["number","null"],"description":"Engine-reported per-edge weighted grade, when present."},"mean_elevation_m":{"type":["number","null"],"description":"Engine-reported per-edge mean elevation (m), when present."}},"required":["length_m","elevation_m","elevation_ft","grade_pct","weighted_grade","mean_elevation_m"]},"TraceAttributesResponse":{"type":"object","properties":{"matched":{"type":"boolean"},"confidence":{"type":"number","description":"Matcher confidence 0–1."},"distance_km":{"type":"number"},"duration_seconds":{"type":"number"},"edge_count":{"type":"number"},"edges":{"type":"array","items":{"$ref":"#/components/schemas/TraceEdge"}},"road_names":{"type":"array","items":{"type":"string"}},"admins":{"type":"array","items":{"type":"object","additionalProperties":{}}},"costing":{"type":"string"},"geojson":{"type":"object","additionalProperties":{}},"geometry":{"type":"object","additionalProperties":{}},"meta":{"type":"object","properties":{"upstream_ms":{"type":"number"},"point_count":{"type":"integer"},"breakage_distance_m":{"type":"integer","description":"Echoes the breakage_distance_m the caller requested, for confirmation — present only when the caller set it. This is the value SENT to the matcher, not a claim about what it did with it."}},"required":["upstream_ms","point_count"]}},"required":["matched","confidence","distance_km","edge_count","edges","road_names","costing","meta"],"description":"Per-segment road attributes for the matched trace. Each edge carries only the whitelisted public fields — internal road-graph identifiers are never exposed."},"TraceAttributesRequest":{"type":"object","properties":{"shape":{"type":"array","items":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180},"time":{"type":"number","minimum":0,"description":"Optional epoch seconds for this fix — timestamps improve match quality on noisy traces."}},"required":["lat","lon"],"example":{"lat":47.6205,"lon":-122.3493}},"minItems":2,"maxItems":100,"description":"Raw GPS breadcrumb trail in travel order — 2 to 100 points of { lat, lon, time? }."},"costing":{"type":"string","enum":["auto","truck","bicycle","pedestrian"],"description":"Transport mode the trace was driven with. Default: auto.","example":"auto"},"vehicle_profile":{"type":"string","maxLength":64,"description":"Truck preset (DRY_VAN_53, FLATBED_48, BOX_TRUCK_26, …) — same 10-profile table as /v1/directions. Implies costing=truck."},"search_radius":{"type":"number","minimum":1,"maximum":100,"description":"Snap radius in meters around each point (1–100). Increase for noisy GPS."},"breakage_distance_m":{"type":"integer","minimum":100,"maximum":20000,"description":"Maximum gap, in meters, between consecutive fixes before the trace is split into separate sections (100–20000). Increase for sparsely-reported GPS, such as fleet units reporting every several minutes on a highway. Optional — omitted uses the default."}},"required":["shape"],"description":"Trace-attributes request — a raw GPS breadcrumb trail (2–100 points) to match against the road network and describe segment-by-segment."},"FeedbackV1Response":{"type":"object","properties":{"bundle_id":{"type":"string","example":"eb_01J0000000000000000000000A"},"queue_id":{"type":"string","description":"Promotion-queue reference. Absent when the queue write soft-failed (see warnings) — never a dangling id.","example":"pq_01J0000000000000000000000B"},"class_key":{"type":"string","enum":["feedback_contradiction","feedback_confirmation"]},"status":{"type":"string","enum":["recorded"]},"warnings":{"type":"array","items":{"type":"string"},"description":"Soft-failure notices (e.g. evidence store unavailable). The feedback is still accepted."}},"required":["bundle_id","class_key","status"]},"FeedbackV1Request":{"type":"object","properties":{"query":{"type":"string","minLength":1,"maxLength":2000,"description":"The original query text this feedback is about.","example":"Santo Domingo & San Martin, Santiago"},"place_id":{"type":"string","pattern":"^ta_(place|intersection)_[0-9a-f]{7,32}$","description":"Stable ThinAir place id the feedback is about.","example":"ta_place_8f3a2c9e61c04d58"},"verdict":{"type":"string","enum":["wrong","right","moved"],"description":"'wrong' — the answer is incorrect; 'right' — confirms the answer; 'moved' — the place exists but sits at different coordinates (pair with `suggested`)."},"suggested":{"type":"object","properties":{"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in decimal degrees, WGS84.","example":29.7604},"lon":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in decimal degrees, WGS84.","example":-95.3698}},"required":["lat","lon"],"description":"Suggested correct coordinates (WGS84)."},"note":{"type":"string","maxLength":280,"description":"Free-text context for human review (≤280 chars). Stored in the evidence bundle only — never emitted to analytics."}},"required":["verdict"]},"FeedbackResponse":{"type":"object","properties":{"feedback_id":{"type":"string"},"status":{"type":"string"},"deduped":{"type":"boolean"}},"required":["feedback_id"]},"FeedbackRequest":{"type":"object","properties":{"verdict":{"type":"string","enum":["accepted","rejected","corrected"],"description":"Your judgement of the subject result."},"subject_event_id":{"type":"string","description":"Event id of the result you're commenting on (or provide tool + query_text)."},"tool":{"type":"string"},"query_text":{"type":"string"},"subject_entity_id":{"type":"string","description":"Stable id the feedback is about.","example":"ta_place_8f3a2c9e61c04d58"},"correction":{"type":"object","properties":{"entity_id":{"type":"string"},"label":{"type":"string","maxLength":120},"lat":{"type":"number"},"lon":{"type":"number"},"note":{"type":"string","maxLength":500,"description":"Free-text note for human review — never sent to analytics."}},"description":"Required when verdict is 'corrected' (entity_id, label, or lat+lon)."},"feedback_source":{"type":"string","enum":["user","agent","automated_validator","support"]}},"required":["verdict"]}},"parameters":{},"headers":{"X-Request-Id":{"description":"Edge-generated request correlation id (req_*).","schema":{"type":"string"}},"X-Trace-Id":{"description":"Alias of X-Request-Id for tracing tooling that auto-detects the Trace-Id spelling.","schema":{"type":"string"}},"X-Cache":{"description":"Edge cache status: HIT (served from the edge cache), MISS (cacheable, went upstream), BYPASS (endpoint has no cache).","schema":{"type":"string","enum":["HIT","MISS","BYPASS"]}},"X-Cache-Hit":{"description":"Legacy boolean cache signal (true|false). Prefer X-Cache.","schema":{"type":"string"}},"X-Cache-Tier":{"description":"Serving cache tier on a hit: l0 (in-region cache) or kv (durable edge store).","schema":{"type":"string","enum":["l0","kv"]}},"X-Edge-Ms":{"description":"Total wall time the worker held the request (ms).","schema":{"type":"string"}},"X-Upstream-Ms":{"description":"Wall time spent in engine round-trips (ms). Omitted on cache hits (no upstream call).","schema":{"type":"string"}},"X-Worker-Ms":{"description":"Worker-side overhead: total minus upstream (ms). Decomposes into X-Auth-Ms + X-Quota-Ms + unattributed residue (serialization, telemetry, routing).","schema":{"type":"string"}},"X-Total-Ms":{"description":"Equal to X-Edge-Ms; kept distinct per spec §6.","schema":{"type":"string"}},"X-Auth-Ms":{"description":"API-key validation time (ms). Present only on the ta_* api-key and demo-token paths; OAuth-JWT validation happens inside the auth library and is not measurable.","schema":{"type":"string"}},"X-Quota-Ms":{"description":"Synchronous quota work on the request path (ms). All API-key plans use a write-behind debit, so this reflects only the fast synchronous exhausted-flag check. The demo (Try-It) identity keeps the synchronous pre-flight peek + post-success debit (QuotaCounter round-trips).","schema":{"type":"string"}},"X-Quota-Cost":{"description":"Capability units this call debited.","schema":{"type":"string"}},"X-Quota-Used":{"description":"Units consumed in the current daily period. The debit is write-behind on all API-key plans: this value is eventually consistent (typically within ~30 seconds); GET /v1/quota is the authoritative synchronous count.","schema":{"type":"string"}},"X-Quota-Remaining":{"description":"Daily limit minus used (-1 = unlimited). Eventually consistent (see X-Quota-Used); GET /v1/quota is authoritative.","schema":{"type":"string"}},"X-Quota-Reset":{"description":"ISO-8601 instant the daily counter rolls over.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Plan cap proxy (-1 = unlimited).","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Remaining headroom (-1 = unlimited).","schema":{"type":"string"}},"X-Session-Id":{"description":"Session correlation id grouping requests issued under one client session.","schema":{"type":"string"}},"X-Edge-Location":{"description":"Edge location (IATA airport code) that served the request. Omitted when unavailable.","schema":{"type":"string"}},"X-Data-Age-S":{"description":"Age in seconds of the underlying data snapshot backing the response (e.g. a traffic observation). Only present on endpoints where data age is meaningful.","schema":{"type":"string"}}}},"paths":{"/v1/weather":{"get":{"description":"Current conditions + optional multi-day forecast and severe-weather alerts for a coordinate pair or place name.","summary":"Weather - current conditions, forecast, and alerts","tags":["Conditions"],"parameters":[{"schema":{"type":"number","minimum":-90,"maximum":90},"required":false,"name":"lat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180},"required":false,"name":"lon","in":"query"},{"schema":{"type":"string","minLength":1,"default":"Houston, TX","description":"Free-text place name; geocoded server-side. Provide either lat+lon or location.","example":"Houston, TX"},"required":false,"name":"location","in":"query"},{"schema":{"type":"string","enum":["imperial","metric"],"description":"Temperature/wind units (default: imperial).","example":"imperial"},"required":false,"name":"units","in":"query"},{"schema":{"type":["boolean","null"],"description":"Include multi-day hourly forecast.","example":false},"required":false,"name":"include_forecast","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":16,"description":"Forecast horizon in days (1-16, default 5).","example":5},"required":false,"name":"forecast_days","in":"query"},{"schema":{"type":["boolean","null"],"description":"Include severe-weather alerts (default: true).","example":true},"required":false,"name":"include_alerts","in":"query"},{"schema":{"type":["boolean","null"],"description":"Include minute-by-minute precipitation for the next 60 minutes.","example":false},"required":false,"name":"include_minutely","in":"query"}],"responses":{"200":{"description":"Weather observations and forecasts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Validation error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Weather provider unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"504":{"description":"Weather provider timed out.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/traffic":{"get":{"description":"Live traffic conditions and speed aggregates at a coordinate. Coverage is concentrated in major US metros; outside coverage areas the response carries `coverage: false`.","summary":"Live traffic - speed and congestion for a point","tags":["Conditions"],"parameters":[{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in decimal degrees, WGS84.","example":29.7604},"required":true,"name":"lat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in decimal degrees, WGS84.","example":-95.3698},"required":true,"name":"lon","in":"query"},{"schema":{"type":"string","enum":["miles","kilometers"],"description":"Speed units. Auto-detected from location (mph in US/UK/Myanmar/Liberia, km/h elsewhere) when omitted. Accepts 'km'/'mi' as aliases for 'kilometers'/'miles'.","example":"miles"},"required":false,"name":"units","in":"query"}],"responses":{"200":{"description":"Traffic conditions for the queried point.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrafficResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Validation error.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Live traffic capacity temporarily exhausted (code BACKPRESSURE). Retryable: `retry_after_ms` and the `Retry-After` header point at the next 00:00 UTC reset. Not charged.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/explore":{"get":{"description":"Browsing / discovery search — administrative places (cities, neighbourhoods, boroughs, counties, regions) near a location. Supports population filtering, ranking by significance or distance, and per-layer radius policy. Venues and points of interest are served by /v1/places, not here.","summary":"Explore administrative places near a point","tags":["Places"],"parameters":[{"schema":{"type":"string","minLength":1,"default":"Houston, TX","description":"Center point — free-text address or 'lat,lon' string.","example":"Houston, TX"},"required":false,"name":"location","in":"query"},{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Center latitude (alternative to `location`).","example":29.7604},"required":false,"name":"lat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Center longitude (alternative to `location`).","example":-95.3698},"required":false,"name":"lon","in":"query"},{"schema":{"type":"string","maxLength":200,"description":"Comma-separated layers: locality, neighbourhood, borough, county, region (default locality). venue, address and street are not served here — use /v1/places for venues and points of interest. An unsupported or unknown layer is rejected with 400 INVALID_QUERY.","example":"locality,neighbourhood"},"required":false,"name":"layers","in":"query"},{"schema":{"type":"string","maxLength":32,"description":"Search radius. Bare numbers are miles unless `units` is set — `radius=30` is 30 miles, `radius=30&units=kilometers` is 30 km; kilometers via `units` or a km suffix ('150km'); aliases mi/km/imperial/metric. A value carrying its own unit suffix ('150km', '30mi') is also accepted for compatibility and its suffix wins. Per-layer policy: locality / county / region default 150 km (93.206 mi) and cap at 500 km (~310 mi); neighbourhood / borough default 25 km (15.534 mi) and cap at 100 km (~62 mi). A request naming several layers takes the TIGHTEST default and cap among them. An over-maximum radius returns HTTP 200 with a `structured_error` of `radius_exceeds_max` naming the governing cap and no items.","example":"30"},"required":false,"name":"radius","in":"query"},{"schema":{"type":"string","enum":["miles","kilometers"],"description":"Units for a bare numeric `radius`: miles (default) or kilometers; aliases mi, mile, imperial (miles) and km, kilometer, kilometre, kilometres, metric (kilometers), case-insensitive, echoed as miles|kilometers. An empty `units=` is treated as absent. Ignored when `radius` already carries a unit suffix (a contradicting pair is rejected). On its own it does not supply a radius — the default is per layer (150 km for locality/county/region, 25 km for neighbourhood/borough; a mixed request takes the tightest) — but it selects the display unit of `meta.radius` / `meta.units`.","example":"miles"},"required":false,"name":"units","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"description":"Max results (default 10, clamped 1..50).","example":10},"required":false,"name":"size","in":"query"},{"schema":{"type":"integer","minimum":0,"description":"Minimum population filter; applied JS-side.","example":100000},"required":false,"name":"min_population","in":"query"},{"schema":{"type":"string","enum":["population","distance","combined"],"description":"Ranking basis (default: population — significance, using population where the source has it, with distance as the tiebreak). `distance` is nearest first; `combined` blends population against a distance decay. The basis applied is echoed in `meta.ranking`."},"required":false,"name":"sort","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":3,"description":"ISO 3166-1 alpha-2 or alpha-3 country code restriction.","example":"US"},"required":false,"name":"boundary_country","in":"query"}],"responses":{"200":{"description":"Explore results — items list with optional upstream_error fallback.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExploreResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid query parameters.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded or rate-limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/places":{"get":{"description":"Category-specific POI search near a point — gas stations, truck stops, restaurants, charging stations, etc. Provide either `query` or center coordinates (or both). For broader discovery use /v1/explore.","summary":"Search places by category or text near a point","tags":["Places"],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Center point — free-text address or 'lat,lon' string.","example":"Houston, TX"},"required":false,"name":"location","in":"query"},{"schema":{"type":"number","minimum":-90,"maximum":90,"default":29.7604,"description":"Center latitude (alternative to `location`).","example":29.7604},"required":false,"name":"lat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"default":-95.3698,"description":"Center longitude (alternative to `location`).","example":-95.3698},"required":false,"name":"lon","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":2000,"description":"Free-text place query (alternative to `category`). One of `query` or `category` is required — a bare center (`lat`+`lon`) with neither is rejected with a 400.","example":"truck stop"},"required":false,"name":"query","in":"query"},{"schema":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20,"default":["food"],"description":"Structured categories (repeatable / CSV): food, fuel, charging_station, education, health, entertainment, retail, accommodation, nightlife, transport, government, recreation. One of `category` or `query` is required — a bare center (`lat`+`lon`) with neither is rejected with a 400.","example":["food","fuel"]},"required":false,"name":"category","in":"query"},{"schema":{"type":"string","maxLength":32,"description":"Search radius. Bare numbers are miles unless `units` is set — `radius=30` is 30 miles, `radius=5&units=kilometers` is 5 km; kilometers via `units` or a km suffix ('5km'); aliases mi/km/imperial/metric. A value carrying its own unit suffix ('30mi', '5km') is also accepted and its suffix wins. Default 50 km (about 31 mi); maximum 50 km (up to 31.068 mi). Preferred over `radius_m`.","example":"30"},"required":false,"name":"radius","in":"query"},{"schema":{"type":"string","enum":["miles","kilometers"],"description":"Units for a bare numeric `radius`: miles (default) or kilometers; aliases mi, mile, imperial (miles) and km, kilometer, kilometre, kilometres, metric (kilometers), case-insensitive, echoed as miles|kilometers. An empty `units=` is treated as absent. Ignored when `radius` already carries a unit suffix (a contradicting pair is rejected). Not valid with `radius_m` alone — `radius_m` is always meters, so that pair is rejected rather than silently ignored. On its own (no `radius`) it does not supply a radius — the default 50 km applies — but it selects the display unit of the `units` echo.","example":"miles"},"required":false,"name":"units","in":"query"},{"schema":{"type":"integer","minimum":0,"maximum":50000,"description":"Search radius in meters (0..50000 — i.e. up to 50 km / ~31 mi; 0 means 'not supplied' and applies the default 50 km, as on the MCP tool). Accepted alias; `radius` + `units` is the preferred form. Sending both is only valid when they agree. Do not pair with `units` — `radius_m` is always meters and the pair is rejected.","example":1000},"required":false,"name":"radius_m","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"description":"Max results (default 10, clamped 1..50).","example":10},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","maxLength":200,"description":"Restrict to: venue, address.","example":"venue"},"required":false,"name":"layers","in":"query"}],"responses":{"200":{"description":"Places results — items list with optional rich metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlacesResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid query parameters.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded or rate-limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/isochrone":{"get":{"description":"Travel-time or travel-distance reachability polygons from an origin. Returns a GeoJSON FeatureCollection of nested Polygon contours — one per requested band. Largest natural latency budget on the API (p95 ≤500ms cache miss).","summary":"Generate reachability polygons (isochrones)","tags":["Routing"],"parameters":[{"schema":{"type":"string","minLength":1,"default":"Houston, TX","description":"Origin — free-text address or 'lat,lon' string.","example":"Houston, TX"},"required":false,"name":"location","in":"query"},{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Origin latitude (paired with `lon`; alternative to `location`).","example":29.7604},"required":false,"name":"lat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Origin longitude (paired with `lat`; alternative to `location`).","example":-95.3698},"required":false,"name":"lon","in":"query"},{"schema":{"type":"array","items":{"type":"number","exclusiveMinimum":0,"maximum":120},"minItems":1,"maxItems":4,"default":[15],"description":"Time bands in minutes; repeatable or CSV. Each value 1..120. Max 4 bands per call.","example":[10,20,30]},"required":false,"name":"contours_minutes","in":"query"},{"schema":{"type":"array","items":{"type":"number","exclusiveMinimum":0,"maximum":200},"minItems":1,"maxItems":4,"description":"Distance bands in km; repeatable or CSV. Each value 1..200. Max 4 bands per call.","example":[5,10,20]},"required":false,"name":"contours_km","in":"query"},{"schema":{"type":"string","enum":["auto","truck","bicycle","pedestrian"],"description":"Travel mode (default: auto)."},"required":false,"name":"costing","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Route preset name (overrides `costing` + truck params).","example":"long_haul"},"required":false,"name":"preset","in":"query"},{"schema":{"type":"number","exclusiveMinimum":0,"maximum":10,"description":"Truck height in meters.","example":4.1},"required":false,"name":"truck_height","in":"query"},{"schema":{"type":"number","exclusiveMinimum":0,"maximum":100,"description":"Truck weight in metric tons.","example":36},"required":false,"name":"truck_weight","in":"query"},{"schema":{"type":"boolean","description":"Hazmat-restricted routing flag."},"required":false,"name":"hazmat","in":"query"}],"responses":{"200":{"description":"Isochrone polygons + metadata. Service returns 200 with `upstream_error` populated on routing-engine failures.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IsochroneResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid query parameters.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded or rate-limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/geocode":{"get":{"summary":"Forward geocode — convert text into coordinates","description":"Resolve an address, place name, intersection, or free-form location string to one or more candidate coordinates. Results are ordered by relevance; the top hit is the geocoder's best guess. Identical inputs may be served from cache for up to 7 days (X-Cache: HIT; STALE once older than 3 days, refreshed in the background); cold lookups run through the regional engine.","tags":["Geocoding"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2000,"default":"1600 Pennsylvania Ave, Washington, DC 20500","description":"Free-text query — address, place name, intersection, etc. One of `text` or `query` is required.","example":"1600 Pennsylvania Ave, Washington, DC 20500"},"required":false,"name":"text","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":2000,"description":"Alias for `text`. The service layer accepts either; clients should prefer `text`."},"required":false,"name":"query","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":40,"description":"Maximum candidates to return (1–40). Defaults to 5 in the service layer.","example":5},"required":false,"name":"size","in":"query"},{"schema":{"type":"string","maxLength":200,"description":"Comma-separated layer filters: address, street, venue, locality, neighbourhood, region, country, postalcode, county, localadmin, coarse, intersection. Use layers=intersection to restrict results to street corners.","example":"address,venue"},"required":false,"name":"layers","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":3,"description":"ISO 3166-1 alpha-2 or alpha-3 country code to scope results.","example":"US"},"required":false,"name":"boundary_country","in":"query"},{"schema":{"type":["number","null"],"minimum":-90,"maximum":90,"description":"Latitude of the proximity bias point — results closer to this coordinate score higher.","example":30.2672},"required":false,"name":"focus_lat","in":"query"},{"schema":{"type":["number","null"],"minimum":-180,"maximum":180,"description":"Longitude of the proximity bias point.","example":-97.7431},"required":false,"name":"focus_lon","in":"query"}],"responses":{"200":{"description":"Geocoder result envelope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeocodeResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid query parameters.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream geocoder unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/place":{"get":{"summary":"Place lookup — resolve a stable ThinAir place id","description":"Re-fetch a place previously returned by geocode, reverse, autocomplete, places, or intersections using its stable public id (`ta_place_…` / `ta_intersection_…`). Ids are deterministic and survive re-serves — safe to store as favorites or foreign keys. Unknown or expired ids return 404 with re-search guidance; ids are refreshed (90-day sliding window) every time the place is served or looked up.","tags":["Geocoding"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^ta_(place|intersection)_[0-9a-f]{7,32}$","description":"Stable public ThinAir id exactly as returned in a previous response's `id` field — `ta_place_<key>` for venues/addresses/localities, `ta_intersection_<key>` for street corners. Ids are opaque and deterministic: the same place re-served mints the same id.","example":"ta_place_8f3a2c9e61c04d58"},"required":true,"name":"id","in":"query"}],"responses":{"200":{"description":"The identified place.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Malformed id.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Id unknown or expired — re-search to obtain a fresh id.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/autocomplete":{"get":{"summary":"Autocomplete — search-as-you-type place suggestions","description":"Purpose-built typeahead: partial input → ranked place suggestions at per-keystroke latency. Edge-cached on normalized prefix (24h) — repeat prefixes are served sub-50ms with `X-Cache: HIT`. Pass `focus_lat`/`focus_lon` for proximity-biased, distance-guarded results, or `boundary_country`/`bounds` for hard filters. Free tier. For full geocoding (addresses, intersections, confidence ladder) use /v1/geocode.","tags":["Geocoding"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"default":"pike pl","description":"Partial input under the user's cursor. Fires from 2 characters when the request is scoped (focus/bounds/country), or 3 characters for a global query; shorter inputs return an empty result with a `min_length` warning (HTTP 200 — no engine call, so a bare 1-char never hits the engine)."},"required":true,"name":"text","in":"query"},{"schema":{"type":["number","null"],"minimum":-90,"maximum":90,"description":"Latitude of the proximity bias point. When set (with focus_lon), results are biased toward it AND results farther than ~500 km are dropped unless nothing is within range.","example":47.6062},"required":false,"name":"focus_lat","in":"query"},{"schema":{"type":["number","null"],"minimum":-180,"maximum":180,"description":"Longitude of the proximity bias point.","example":-122.3321},"required":false,"name":"focus_lon","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":3,"description":"ISO 3166-1 alpha-2 or alpha-3 country code — hard filter.","example":"US"},"required":false,"name":"boundary_country","in":"query"},{"schema":{"type":"string","maxLength":100,"description":"Bounding-box hard filter as `minLon,minLat,maxLon,maxLat` (WGS84 degrees).","example":"-123.1,47.0,-121.9,48.0"},"required":false,"name":"bounds","in":"query"},{"schema":{"type":"string","maxLength":200,"description":"Comma-separated layer filter: venue, street, address, intersection, locality, neighbourhood, borough, county, localadmin, region, country, postalcode, coarse. Unknown layers are ignored.","example":"locality,venue"},"required":false,"name":"layers","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":10,"description":"Maximum suggestions to return (1–10). Default 5.","example":5},"required":false,"name":"size","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":10,"description":"Preferred label language (ISO 639-1 / BCP47), passed through to the engine.","example":"en"},"required":false,"name":"lang","in":"query"}],"responses":{"200":{"description":"Ranked suggestion list. `X-Cache: HIT|MISS` reports edge-cache status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutocompleteResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid query parameters.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded or rate-limited.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream engine unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/reverse":{"get":{"summary":"Reverse geocode — convert coordinates into the nearest address","description":"Look up the nearest address, street, or place around a `(lat, lon)` pair. Results ordered by proximity. Results may be served from cache for up to 7 days, so repeated nearby lookups can return `X-Cache: HIT`.","tags":["Geocoding"],"parameters":[{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in decimal degrees, WGS84.","example":30.2672},"required":true,"name":"lat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in decimal degrees, WGS84.","example":-97.7431},"required":true,"name":"lon","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":40,"description":"Maximum candidates to return (1–40). Defaults to 5 in the service layer (capped to 40).","example":5},"required":false,"name":"size","in":"query"},{"schema":{"type":"string","maxLength":200,"description":"Restrict to a comma list of layers: address, street, venue, neighbourhood, borough, locality, localadmin, county, macrocounty, region, macroregion, dependency, country, continent, postalcode, or coarse (all admin layers). Admin layers resolve from the nearest indexed feature's parent hierarchy.","example":"address"},"required":false,"name":"layers","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":3,"description":"ISO 3166-1 alpha-2 or alpha-3 country code to scope results.","example":"US"},"required":false,"name":"boundary_country","in":"query"}],"responses":{"200":{"description":"Reverse geocoder result envelope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReverseResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid coordinates or query parameters.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream geocoder unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/directions":{"post":{"summary":"Compute a route between two locations","description":"Generate routes, ETAs, and turn-by-turn directions between locations. Live traffic is post-route ETA enrichment and does not change the returned path; truck traffic adjustments are slowdown-only. Supports auto / truck / bicycle / pedestrian / motorcycle, with hazmat + dimension + toll avoidance for commercial routing. Costs 1 quota unit per call.","tags":["Routing"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectionsRequest"},"example":{"origin":"Houston, TX","destination":"Dallas, TX","costing":"auto"}}}},"responses":{"200":{"description":"Successful route. See `geojson` for FeatureCollection geometry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DirectionsResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"Authentication required or failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No route found between the origin and destination, or no road near a given coordinate.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream routing engine unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"504":{"description":"The routing engine exceeded its time budget.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/locate":{"get":{"summary":"Locate — road-network context for one coordinate","description":"Snap a `(lat, lon)` to the road network and return the matched road's public attributes (name, class, speed limit, access/legality, bridge/tunnel, bearing, snap distance, side of street) plus the timezone. `include_elevation=true` returns terrain-sourced `elevation_m`/`elevation_ft` across North America; outside that coverage they stay `null` with an `elevation_unavailable` warning rather than a guessed value. `grade_pct` stays `null` pending an elevation-enhanced road network. Results may be cached for up to 7 days on a ~65m grid — repeat nearby lookups can return `X-Cache: HIT`.","tags":["Routing"],"parameters":[{"schema":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in decimal degrees, WGS84.","example":47.6205},"required":true,"name":"lat","in":"query"},{"schema":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in decimal degrees, WGS84.","example":-122.3493},"required":true,"name":"lon","in":"query"},{"schema":{"type":"string","enum":["auto","truck","bicycle","pedestrian"],"description":"Travel profile that scopes edge access/legality. Defaults to `auto`.","example":"truck"},"required":false,"name":"costing","in":"query"},{"schema":{"type":"string","enum":["imperial","metric"],"description":"Unit system for the human summary. Auto-detected (US/UK/… → imperial) when omitted.","example":"imperial"},"required":false,"name":"units","in":"query"},{"schema":{"type":"string","enum":["true","false","1","0"],"description":"Opt-in to look up point elevation (`true`/`1`). Populates `elevation_m`/`elevation_ft` once terrain data is available; until then they stay `null` and an `elevation_unavailable` warning is emitted. `grade_pct` stays `null` regardless — it needs an elevation-enhanced road network.","example":"false"},"required":false,"name":"include_elevation","in":"query"}],"responses":{"200":{"description":"Road-network context. `matched:false` (with `road:null`) is a valid 200: `road_status` says whether no routable road is near the point (`no_road_nearby`) or the road lookup could not be answered (`unavailable`, with `retryable:true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocateResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid coordinates or query parameters.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream routing engine unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/quota":{"get":{"summary":"Get caller's current quota state","description":"Returns the caller's plan, daily usage, remaining queries, and upgrade options. **Free to call** — never debits quota and never returns 429 (a quota-exhausted user must still be able to discover their state).","tags":["Account"],"responses":{"200":{"description":"Caller's quota snapshot.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"401":{"description":"Authentication required or failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/intersection":{"post":{"summary":"Resolve a named cross-street to coordinates","description":"Resolve a named intersection (street1 × street2 within a locality/region/country) to a single best coordinate with a confidence score, admin context, and a fleet-safe fallback. The high-confidence tier matches US-metro highway corners; named non-highway streets resolve through the geocoder fallback. Costs 1 quota unit. `POST /v1/intersections/resolve` is a functional alias with the identical contract (retained for compatibility; not separately listed).","tags":["Intersections"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntersectionResolveRequest"},"example":{"street1":"Bellaire Blvd","street2":"Chimney Rock Rd","locality":"Houston","region":"TX","country":"US"}}}},"responses":{"200":{"description":"Resolved intersection (or honest-empty `feature: null` with a coverage warning).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntersectionResolveResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream geocoder unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/intersections/search":{"post":{"summary":"Search for street intersections","description":"Search for street-intersection candidates from a free-text cross-street query, scoped by optional locality/region/country. Returns ranked candidates plus a GeoJSON FeatureCollection. Costs 1 quota unit.","tags":["Intersections"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntersectionSearchRequest"},"example":{"text":"Bellaire Blvd and Chimney Rock Rd","locality":"Houston","region":"TX","country":"US"}}}},"responses":{"200":{"description":"Intersection search results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntersectionSearchResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream geocoder unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/distance-matrix":{"post":{"summary":"Compute an N×M distance + duration matrix","description":"Compute road distances and travel times for every origin → destination pair. Endpoints accept free-text addresses or coordinate pairs; N × M is capped at 625 pairs. Quota cost is origins.count + destinations.count + 1 (one unit per geocoded endpoint plus one for the matrix call). Matrix durations do NOT include live or historical traffic — see `duration_basis` on the response; use `directions` for a traffic-adjusted ETA between two specific points.","tags":["Routing"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DistanceMatrixRequest"},"example":{"origins":["Houston, TX","Austin, TX"],"destinations":["Dallas, TX",{"lat":29.4241,"lon":-98.4936}],"costing":"auto","units":"kilometers"}}}},"responses":{"200":{"description":"Distance + duration matrix with resolved endpoints.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DistanceMatrixResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body or matrix over the 625-pair limit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream routing engine unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/geofences/contains":{"post":{"summary":"Point-in-polygon containment test","description":"Test one or more points for containment inside a caller-supplied GeoJSON polygon (or MultiPolygon / Feature / FeatureCollection). Pure geometry — no upstream call, costs 0 quota units. Boundary points count as contained. Returns 400 when the polygon is structurally invalid.","tags":["Geofences"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeofenceContainsRequest"},"example":{"polygon":{"type":"Polygon","coordinates":[[[-95.4,29.7],[-95.3,29.7],[-95.3,29.8],[-95.4,29.8],[-95.4,29.7]]]},"points":[{"lat":29.75,"lon":-95.35},{"lat":40,"lon":-100}]}}}},"responses":{"200":{"description":"Containment results — one boolean per input point.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeofenceContainsResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body or structurally-invalid polygon.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/geofences/validate":{"post":{"summary":"Validate a GeoJSON polygon","description":"Check a caller-supplied GeoJSON polygon for well-formedness: ring closure, minimum vertex count, self-intersection, winding order (RFC 7946 right-hand rule), coordinate bounds, and complexity limits. Pure geometry — costs 0 quota units. A malformed polygon is NOT a 400; it returns `valid: false` with structured `errors`. Winding-order issues surface as `warnings` (containment is winding-agnostic).","tags":["Geofences"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeofenceValidateRequest"},"example":{"polygon":{"type":"Polygon","coordinates":[[[-95.4,29.7],[-95.3,29.7],[-95.3,29.8],[-95.4,29.8],[-95.4,29.7]]]}}}}},"responses":{"200":{"description":"Validation report. `valid: false` + errors on a malformed polygon (still a 200).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeofenceValidateResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body (non-JSON or schema mismatch).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/search/structured":{"post":{"summary":"Structured geocode — component fields into coordinates","description":"Forward geocode from discrete address components (address / locality / region / postalcode / country) instead of one free-text string. Skips the free-text parser entirely, so pre-fielded data (CRM records, checkout forms, fleet stop tables) can't be mis-tokenized. Response shape is identical to GET /v1/geocode. Costs 1 quota unit; responses are edge-cached (X-Cache: HIT/MISS). For named cross-streets use POST /v1/intersection.","tags":["Geocoding"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StructuredGeocodeRequest"},"example":{"address":"1600 Pennsylvania Ave NW","locality":"Washington","region":"DC","postalcode":"20500","country":"US"}}}},"responses":{"200":{"description":"Geocoder result envelope (same contract as GET /v1/geocode).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeocodeResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body (including no component fields at all).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream geocoder unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/batch":{"post":{"summary":"Batch geocode — up to 50 addresses in one call","description":"Geocode multiple addresses in one request with structured per-row results (per-row errors never fail the batch). Costs 1 quota unit per call — the same debit the MCP batch_geocode tool applies. Canonical path; `POST /v1/geocode/batch` is a retained alias with the identical contract.","tags":["Geocoding"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGeocodeRequest"},"example":{"addresses":["Houston, TX","Austin, TX"]}}}},"responses":{"200":{"description":"Per-row batch results (successes and failures interleaved, input order preserved).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGeocodeResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body (missing/empty/oversized addresses array).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream geocoder unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/geocode/batch":{"post":{"summary":"Batch geocode — up to 50 addresses in one call","description":"Geocode multiple addresses in one request with structured per-row results (per-row errors never fail the batch). Costs 1 quota unit per call — the same debit the MCP batch_geocode tool applies. Alias of `POST /v1/batch` (the canonical documented path) — retained for compatibility; not deprecated.","tags":["Geocoding"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGeocodeRequest"},"example":{"addresses":["Houston, TX","Austin, TX"]}}}},"responses":{"200":{"description":"Per-row batch results (successes and failures interleaved, input order preserved).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchGeocodeResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body (missing/empty/oversized addresses array).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream geocoder unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/map-match":{"post":{"summary":"Snap raw GPS breadcrumbs to the road network","description":"Map matching snaps raw GPS breadcrumbs to the road network — turn a noisy trace into the road-accurate route that was actually driven, with distance, time, road names, and a match confidence. `matched: true` only means a route was returned: gate on `match_status` (`full` | `partial` | `none`), and use `coverage.unmatched_ranges` to find the input points that did not match. `road_names` come from maneuver points — use /v1/trace-attributes for per-segment names. Sparsely-reported traces (consecutive fixes farther apart than the default breakage distance) can split into multiple sections — see `breakage_distance_m`. Total matched distance is limited to 200 km per call; split longer sparse traces by distance, not only by the 100-point cap. Costs 1 quota unit per call.","tags":["Routing"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MapMatchRequest"},"example":{"shape":[{"lat":47.6205,"lon":-122.3493},{"lat":47.6215,"lon":-122.348},{"lat":47.6227,"lon":-122.3447}],"costing":"auto"}}}},"responses":{"200":{"description":"Matched route. See `geojson` / `geometry` for the snapped line.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MapMatchResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body (fewer than 2 or more than 100 points, bad coordinates).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Trace could not be matched to any road (off-road, too sparse, or too noisy).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream matching engine unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/trace-attributes":{"post":{"summary":"Road attributes along a matched GPS trace","description":"Trace attributes return road/network attributes for matched traces — match a GPS breadcrumb trail to the road network and get per-segment road names, classes, speed limits, surfaces, and truck-route flags, plus the matcher's confidence. Sparsely-reported traces (consecutive fixes farther apart than the default breakage distance) can split into multiple sections — see `breakage_distance_m`. Total matched distance is limited to 200 km per call; split longer sparse traces by distance, not only by the 100-point cap. Costs 1 quota unit per call.","tags":["Routing"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TraceAttributesRequest"},"example":{"shape":[{"lat":47.6205,"lon":-122.3493},{"lat":47.6215,"lon":-122.348},{"lat":47.6227,"lon":-122.3447}],"costing":"auto"}}}},"responses":{"200":{"description":"Per-segment attributes for the matched trace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TraceAttributesResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid request body (fewer than 2 or more than 100 points, bad coordinates).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Trace could not be matched to any road (off-road, too sparse, or too noisy).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Quota exceeded for this tenant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Upstream matching engine unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/feedback":{"post":{"summary":"Submit map feedback into the learning loop","description":"Learning surface — report a wrong/right/moved verdict on a geocoding answer. Seeds an evidence bundle and a pending entry in the validation-gated promotion queue; a human adjudicates before anything can change an answer. Authenticated; never metered (X-Quota-Cost: 0); limited to 20 submissions per day per key. `note` is retained for human review only and never reaches analytics.","tags":["Learning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackV1Request"},"example":{"query":"Santo Domingo & San Martin, Santiago","verdict":"wrong","suggested":{"lat":-33.4335,"lon":-70.6535},"note":"Resolved to the wrong corner; should be one block west."}}}},"responses":{"202":{"description":"Feedback recorded into the promotion queue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackV1Response"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid body (bad verdict, note > 280 chars, neither query nor place_id, …).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Feedback daily limit (20/day per key) or plan quota exhausted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/geo/feedback":{"post":{"summary":"Submit feedback or a map correction (learning)","description":"Learning surface — submit a verdict or a correction on a prior result. This is NOT a normal geocoding query: it feeds ThinAir Geo's validation-gated promotion/learning pipeline. Authenticated (401 for anonymous — unattributed feedback is unusable), and never metered (no quota debit). `correction.note` is retained for human review and is never emitted to analytics.","tags":["Learning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}}},"responses":{"202":{"description":"Feedback recorded (200 with deduped:true on a repeat).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Trace-Id":{"$ref":"#/components/headers/X-Trace-Id"},"X-Cache":{"$ref":"#/components/headers/X-Cache"},"X-Cache-Hit":{"$ref":"#/components/headers/X-Cache-Hit"},"X-Cache-Tier":{"$ref":"#/components/headers/X-Cache-Tier"},"X-Edge-Ms":{"$ref":"#/components/headers/X-Edge-Ms"},"X-Upstream-Ms":{"$ref":"#/components/headers/X-Upstream-Ms"},"X-Worker-Ms":{"$ref":"#/components/headers/X-Worker-Ms"},"X-Total-Ms":{"$ref":"#/components/headers/X-Total-Ms"},"X-Auth-Ms":{"$ref":"#/components/headers/X-Auth-Ms"},"X-Quota-Ms":{"$ref":"#/components/headers/X-Quota-Ms"},"X-Quota-Cost":{"$ref":"#/components/headers/X-Quota-Cost"},"X-Quota-Used":{"$ref":"#/components/headers/X-Quota-Used"},"X-Quota-Remaining":{"$ref":"#/components/headers/X-Quota-Remaining"},"X-Quota-Reset":{"$ref":"#/components/headers/X-Quota-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-Session-Id":{"$ref":"#/components/headers/X-Session-Id"},"X-Edge-Location":{"$ref":"#/components/headers/X-Edge-Location"},"X-Data-Age-S":{"$ref":"#/components/headers/X-Data-Age-S"}}},"400":{"description":"Invalid feedback.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"Authentication required — anonymous feedback is not accepted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}}},"webhooks":{}}