{
  "components": {
    "parameters": {
      "bbox": {
        "description": "Bounding box: minLng,minLat,maxLng,maxLat",
        "in": "query",
        "name": "bbox",
        "schema": {
          "type": "string"
        }
      },
      "jurisdiction": {
        "description": "Filter by jurisdiction code (e.g., `GA`, `ON`, `CA`). Required on Free plan.\n\n**Auto-expansion:** querying a state's primary code that has WZDx/CWZ\nor sub-state regional siblings (see `GET /jurisdictions/groups`) is\nexpanded transparently to include all members. For example,\n`?jurisdiction=CA` returns rows from `CA`, `WZDX_CA`, and `511SF`.\n\nTo opt out, query a non-primary code directly (`?jurisdiction=WZDX_CA`\nreturns WZDx-only rows) or pass a comma-separated list explicitly\n(`?jurisdiction=CA,WZDX_CA`), which bypasses expansion and matches\neach code literally.\n",
        "in": "query",
        "name": "jurisdiction",
        "schema": {
          "type": "string"
        }
      },
      "lat": {
        "description": "Latitude for radius search",
        "in": "query",
        "name": "lat",
        "schema": {
          "type": "number"
        }
      },
      "limit": {
        "description": "Results per page (capped by plan)",
        "in": "query",
        "name": "limit",
        "schema": {
          "default": 100,
          "type": "integer"
        }
      },
      "lng": {
        "description": "Longitude for radius search",
        "in": "query",
        "name": "lng",
        "schema": {
          "type": "number"
        }
      },
      "offset": {
        "description": "Pagination offset",
        "in": "query",
        "name": "offset",
        "schema": {
          "default": 0,
          "type": "integer"
        }
      },
      "radius_km": {
        "description": "Radius in km (used with lat/lng)",
        "in": "query",
        "name": "radius_km",
        "schema": {
          "default": 50,
          "type": "number"
        }
      }
    },
    "schemas": {
      "APIKey": {
        "properties": {
          "allowed_origins": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          },
          "key_prefix": {
            "type": "string"
          },
          "last_used_at": {
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "owner_email": {
            "type": "string"
          },
          "rate_limit_rpm": {
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Customer": {
        "properties": {
          "company": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "email_verified": {
            "type": "boolean"
          },
          "id": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "plan_id": {
            "type": "integer"
          },
          "subscription_ends_at": {
            "format": "date-time",
            "type": "string"
          },
          "subscription_status": {
            "enum": [
              "none",
              "active",
              "canceled",
              "past_due",
              "paused"
            ],
            "type": "string"
          },
          "trial_ends_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "type": "object"
      },
      "Error": {
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ],
        "type": "object"
      },
      "EventWeatherSnapshot": {
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "distance_km": {
            "description": "Distance from event to weather station in km",
            "type": "number"
          },
          "event_id": {
            "type": "string"
          },
          "humidity": {
            "description": "Relative humidity percentage",
            "type": "number"
          },
          "id": {
            "type": "integer"
          },
          "precipitation": {
            "description": "Precipitation type (none, rain, snow, ice, etc.)",
            "type": "string"
          },
          "reading_recorded_at": {
            "description": "When the weather reading was taken",
            "format": "date-time",
            "type": "string"
          },
          "road_surface": {
            "description": "Road surface condition (dry, wet, icy, etc.)",
            "type": "string"
          },
          "station_feature_id": {
            "type": "string"
          },
          "temperature": {
            "description": "Temperature in °C",
            "type": "number"
          },
          "visibility": {
            "description": "Visibility in km",
            "type": "number"
          },
          "wind_direction": {
            "type": "string"
          },
          "wind_speed": {
            "description": "Wind speed in km/h",
            "type": "number"
          }
        },
        "type": "object"
      },
      "Feature": {
        "properties": {
          "description": {
            "type": "string"
          },
          "direction": {
            "type": [
              "string",
              "null"
            ]
          },
          "end_time": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "estimated_end_time": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "estimated_start_time": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "feature_type": {
            "type": "string"
          },
          "geometry": {
            "description": "Full GeoJSON geometry when the feature is a line or polygon; NULL for point-only features (read latitude/longitude instead).",
            "type": [
              "object",
              "null"
            ]
          },
          "has_details": {
            "description": "`true` when this feature's `(source, feature_type)` pair has a\nregistered lazy-load detail function — calling\n`GET /features/{id}/details` or batching via\n`POST /features/details/batch` will return strictly richer data\nthan what's in the list row (multi-view camera URLs, EV connector\navailability, sign message text, etc.). `false` means the list\nrow already carries everything the source publishes, and the\ndetail call would be a no-op.\nNot set on the `/features/geojson` path (that JSON is built in\nPostgres for performance — call `/features` for the hint).\n",
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "is_active": {
            "type": "boolean"
          },
          "jurisdiction": {
            "type": "string"
          },
          "last_updated": {
            "format": "date-time",
            "type": "string"
          },
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "name": {
            "type": "string"
          },
          "properties": {
            "description": "Type-specific fields (JSONB)",
            "type": "object"
          },
          "road_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string"
          },
          "source_id": {
            "description": "Upstream-provided identifier, before our synthetic `id` prefix.",
            "type": "string"
          },
          "start_time": {
            "description": "Populated for time-bounded feature types (future_construction, alerts, special_events, truck_restrictions, etc.). NULL for permanent features.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "HealthResponse": {
        "properties": {
          "db": {
            "description": "Combined Postgres and Redis liveness, formatted as\n`\u003cpostgres\u003e/\u003credis\u003e` where each segment is `ok` or `down`\n(e.g. `ok/ok`, `ok/down`). Probe timeout is 2 seconds; the\nunderlying error is logged server-side, not returned.\n",
            "example": "ok/ok",
            "type": "string"
          },
          "status": {
            "enum": [
              "ok",
              "degraded"
            ],
            "type": "string"
          },
          "uptime": {
            "type": "string"
          },
          "version": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "version",
          "uptime",
          "db"
        ],
        "type": "object"
      },
      "Jurisdiction": {
        "properties": {
          "code": {
            "description": "Identifier used everywhere else in the API (`source`, `jurisdiction`,\nwebhook filters, etc.). Two-letter postal codes for US states and\nCanadian provinces; `WZDX_\u003cSTATE\u003e` for the parallel WZDx feed; ad-hoc\ncodes for sub-state and federal sources (`511SF`, `EIA`, `FHWA`,\n`WZDX_NPS`, ...).\n",
            "type": "string"
          },
          "country": {
            "description": "ISO 3166-1 alpha-2 (`US`, `CA`, `DE`, ...).",
            "type": "string"
          },
          "group_id": {
            "description": "When non-null, points at a record in `GET /jurisdictions/groups` —\nthe canonical bundle that includes this row's primary code plus its\nWZDx / CWZ / regional siblings. Querying `?jurisdiction=\u003cprimary\u003e`\non `/events` or `/features` auto-expands to all members.\n",
            "type": [
              "string",
              "null"
            ]
          },
          "is_active": {
            "type": "boolean"
          },
          "last_sync_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "official_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "scope": {
            "description": "`state` is the default — real geographic jurisdictions (US states,\nCanadian provinces). `federal` covers cross-cutting national sources\n(`EIA`, `FHWA`, `WZDX_NPS`). `regional` covers sub-state feeds\n(`511SF`, `WZDX_AUSTIN`, `WZDX_MCDOT`, `WZDX_STC_MO`). The default\nresponse from `GET /jurisdictions` is `scope=state` only.\n",
            "enum": [
              "state",
              "federal",
              "regional"
            ],
            "type": "string"
          }
        },
        "required": [
          "code",
          "name",
          "country",
          "scope",
          "is_active"
        ],
        "type": "object"
      },
      "JurisdictionGroup": {
        "properties": {
          "description": {
            "type": "string"
          },
          "id": {
            "description": "Stable slug, e.g. `us-ca`, `us-co`, `us-tx`.",
            "type": "string"
          },
          "members": {
            "description": "Jurisdiction codes in this group, primary first. Includes the state\nfeed plus its `WZDX_\u003cSTATE\u003e`, optional `WZDX_CWZ_\u003cSTATE\u003e`, and any\nsub-state regional siblings.\n",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "primary_jurisdiction_code": {
            "description": "The canonical state/province code (`CA`, `CO`, `TX`). Querying this\ncode on `/events` or `/features` auto-expands to all `members`.\n",
            "type": "string"
          },
          "sort_order": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "primary_jurisdiction_code",
          "members"
        ],
        "type": "object"
      },
      "PagedEvents": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/TrafficEvent"
            },
            "type": "array"
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PagedFeatures": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Feature"
            },
            "type": "array"
          },
          "has_more": {
            "type": "boolean"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        },
        "type": "object"
      },
      "Plan": {
        "properties": {
          "allow_analytics": {
            "type": "boolean"
          },
          "allow_geojson": {
            "type": "boolean"
          },
          "allow_truck_data": {
            "type": "boolean"
          },
          "allow_webhooks": {
            "type": "boolean"
          },
          "code": {
            "type": "string"
          },
          "data_delay_sec": {
            "description": "0 = real-time",
            "type": "integer"
          },
          "description": {
            "type": "string"
          },
          "features": {
            "description": "Marketing bullets shown on pricing pages. Admin-managed via\n`/admin/plans/{id}/features`. Resolution order on the frontend:\n`i18n_key` (if the active locale has it) → raw `label` → the key\nstring itself.\n",
            "items": {
              "$ref": "#/components/schemas/PlanFeature"
            },
            "type": "array"
          },
          "id": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          },
          "is_contact_sales": {
            "description": "When true, the pricing page renders a \"Contact sales\" CTA instead of Paddle checkout.",
            "type": "boolean"
          },
          "is_popular": {
            "description": "Marketing flag — highlights this plan on the pricing page.",
            "type": "boolean"
          },
          "max_batch_size": {
            "description": "Maximum number of ids accepted by batch endpoints\n(`GET /events?ids=…`, `POST /features/details/batch`). 0 = unlimited.\n",
            "type": "integer"
          },
          "max_jurisdictions": {
            "description": "0 = unlimited",
            "type": "integer"
          },
          "max_keys": {
            "type": "integer"
          },
          "max_requests_day": {
            "description": "0 = unlimited",
            "type": "integer"
          },
          "max_results_per_page": {
            "description": "0 = unlimited",
            "type": "integer"
          },
          "max_webhooks": {
            "description": "0 = unlimited (only meaningful when `allow_webhooks` is true).",
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "paddle_price_id": {
            "description": "Paddle price ID this plan is wired to for checkout. NULL for free/trial plans.",
            "type": [
              "string",
              "null"
            ]
          },
          "price_monthly": {
            "description": "Monthly price in the currency the Paddle account is configured for.",
            "type": "number"
          },
          "rate_limit_rpm": {
            "type": "integer"
          },
          "trial_days": {
            "description": "0 = no trial limit",
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PlanFeature": {
        "description": "One marketing bullet on a plan's pricing card. Either `i18n_key` or\n`label` is always populated (DB CHECK constraint). Stable bullets use\n`i18n_key` so translations live in frontend locale files; short-lived\nmarketing copy uses `label` only and ships unchanged in every locale.\n",
        "properties": {
          "i18n_key": {
            "description": "Translation key, e.g. `plan_feature.events_cameras`.",
            "type": [
              "string",
              "null"
            ]
          },
          "icon": {
            "description": "Short icon hint (`check`, `zap`, `shield`, ...) the frontend maps to an SVG.",
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "integer"
          },
          "label": {
            "description": "Raw fallback shown when no `i18n_key` is set or the locale lacks it.",
            "type": [
              "string",
              "null"
            ]
          },
          "plan_id": {
            "type": "integer"
          },
          "sort_order": {
            "type": "integer"
          },
          "tooltip": {
            "type": [
              "string",
              "null"
            ]
          },
          "tooltip_i18n_key": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "plan_id",
          "sort_order"
        ],
        "type": "object"
      },
      "TrafficEvent": {
        "properties": {
          "affected_roads": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "archive_reason": {
            "description": "How the event was archived. NULL while `status='active'`; set\nalongside `archived_at` at archival time.\n\n- `observed` — upstream returned a clean response and the event\n  was not in it, so the per-poll archival path retired it. This\n  is the normal case.\n- `stale_sweep` — the per-poll path could not retire the event\n  because upstream responses were empty or errored (so we cannot\n  prove the event is gone vs. a transient outage). After roughly\n  `poll_interval × sweep_factor` with no fresh observation\n  (default factor = 15), a safety-net sweep forces the event to\n  archived. A persistently high `stale_sweep` ratio on a source\n  signals that the upstream feed often fails to emit clearance\n  records or is unreliable in general — treat the\n  `archived_at` for such events as \"presumed gone by\", not a\n  precise end timestamp.\n",
            "enum": [
              "observed",
              "stale_sweep",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "archived_at": {
            "description": "Set the moment our sweep first observed the event missing from the upstream feed.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "direction": {
            "type": [
              "string",
              "null"
            ]
          },
          "effective_end_time": {
            "description": "Best available end time for the event. Equals `end_time` when\nthe upstream feed reported one; otherwise falls back to\n`archived_at` (the moment our sweep first observed the event\nhad disappeared from the feed). NULL only for active events\nwith no upstream-reported end — i.e., events that are still\nongoing. Use this field for \"incident duration\" analytics.\n",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "end_time": {
            "description": "Upstream-reported end time. NULL for events the upstream feed\nnever assigned an explicit end (the common case — most 511 feeds\nsimply drop resolved incidents rather than emitting a final\ntimestamp).\n",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "estimated_end_time": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "estimated_start_time": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "jurisdiction": {
            "type": "string"
          },
          "lanes_affected": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_updated": {
            "format": "date-time",
            "type": "string"
          },
          "latitude": {
            "type": "number"
          },
          "location": {
            "description": "GeoJSON geometry (Point, LineString, or Polygon) describing where the event applies.",
            "type": "object"
          },
          "longitude": {
            "type": "number"
          },
          "metadata": {
            "description": "Raw upstream attributes preserved as opaque JSON. Shape varies by source.",
            "type": "object"
          },
          "road_class": {
            "enum": [
              "interstate",
              "us_highway",
              "state_highway",
              "local"
            ],
            "type": "string"
          },
          "severity": {
            "enum": [
              "minor",
              "moderate",
              "major",
              "critical"
            ],
            "type": "string"
          },
          "source": {
            "description": "Jurisdiction code",
            "type": "string"
          },
          "source_created_at": {
            "description": "Upstream-reported creation timestamp, when available.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "source_id": {
            "type": "string"
          },
          "source_updated_at": {
            "description": "Upstream-reported last-modified timestamp, when available.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "start_time": {
            "format": "date-time",
            "type": "string"
          },
          "status": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "type": {
            "enum": [
              "incident",
              "construction",
              "closure",
              "special_event",
              "weather",
              "road_condition",
              "planned",
              "hazard"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "WeatherCorrelationStats": {
        "properties": {
          "avg_severity": {
            "type": "number"
          },
          "avg_temp_c": {
            "type": "number"
          },
          "avg_visibility": {
            "type": "number"
          },
          "avg_wind_speed": {
            "type": "number"
          },
          "event_count": {
            "type": "integer"
          },
          "precipitation": {
            "type": "string"
          },
          "road_surface": {
            "type": "string"
          }
        },
        "type": "object"
      }
    },
    "securitySchemes": {
      "ApiKeyHeader": {
        "in": "header",
        "name": "X-API-Key",
        "type": "apiKey"
      },
      "ApiKeyQuery": {
        "in": "query",
        "name": "api_key",
        "type": "apiKey"
      },
      "BearerAuth": {
        "bearerFormat": "JWT",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "contact": {
      "email": "support@road511.com"
    },
    "description": "Aggregated North American 511 traffic data API. Normalizes data from 45+ US state\nand Canadian province 511 systems into a single REST API.\n\n## Authentication\n- **API Key**: Pass via `X-API-Key` header or `api_key` query parameter\n- **Admin**: Bearer JWT token via `Authorization: Bearer \u003ctoken\u003e`\n- **Customer Portal**: Bearer JWT token via `Authorization: Bearer \u003ctoken\u003e`\n\n## Plan Limits\n| Plan | RPM | Daily Requests | Jurisdictions | Results/Page | GeoJSON | Analytics | Truck | Data Delay | Trial |\n|------|-----|----------------|---------------|--------------|---------|-----------|-------|------------|-------|\n| Free | 60 | 1,000 | 2 | 100 | No | No | No | 15 min | 14 days |\n| Starter | 300 | 50,000 | 10 | 500 | Yes | No | No | Real-time | - |\n| Pro | 1,000 | 500,000 | Unlimited | 1,000 | Yes | Yes | Yes | Real-time | - |\n| Enterprise | 5,000 | Unlimited | Unlimited | Unlimited | Yes | Yes | Yes | Real-time | - |\n",
    "license": {
      "name": "Proprietary"
    },
    "title": "Road511 Traffic API",
    "version": "1.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/analytics/changes": {
      "get": {
        "description": "Latest event lifecycle changes across all jurisdictions. Requires\nPro+ plan. Archival entries carry `new_value=archived` or\n`new_value=stale_sweep` — see `archive_reason` on the\n`TrafficEvent` schema for what each means.\n",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "change_type": "severity_change",
                      "event_id": "on-evt-401-001",
                      "event_type": "incident",
                      "id": 45,
                      "jurisdiction": "ON",
                      "new_value": "major",
                      "old_value": "moderate",
                      "recorded_at": "2026-03-29T14:35:00Z",
                      "road_name": "Highway 401",
                      "severity": "major",
                      "source": "ON"
                    },
                    {
                      "change_type": "end_time_change",
                      "event_id": "ga-evt-i75-002",
                      "event_type": "construction",
                      "id": 44,
                      "jurisdiction": "GA",
                      "new_value": "2026-04-15T18:00:00Z",
                      "old_value": "2026-04-10T18:00:00Z",
                      "recorded_at": "2026-03-29T10:00:00Z",
                      "road_name": "I-75",
                      "severity": "moderate",
                      "source": "ga"
                    }
                  ],
                  "has_more": false,
                  "limit": 100,
                  "offset": 0,
                  "total": 2
                }
              }
            },
            "description": "Paginated recent changes"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "analytics access requires Pro plan or higher"
                }
              }
            },
            "description": "Analytics not available on current plan"
          }
        },
        "summary": "Recent changes feed",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/clearance": {
      "get": {
        "description": "Event clearance time statistics. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "avg_minutes": 62.8,
                    "count": 320,
                    "event_type": "incident",
                    "jurisdiction": "ON",
                    "max_minutes": 540,
                    "min_minutes": 8,
                    "p50_minutes": 45.2,
                    "p95_minutes": 180.5,
                    "severity": "major"
                  },
                  {
                    "avg_minutes": 7200,
                    "count": 150,
                    "event_type": "construction",
                    "jurisdiction": "GA",
                    "max_minutes": 43200,
                    "min_minutes": 360,
                    "p50_minutes": 4320,
                    "p95_minutes": 21600,
                    "severity": "moderate"
                  }
                ]
              }
            },
            "description": "Clearance statistics"
          }
        },
        "summary": "Clearance times (P50/P95)",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/corridors": {
      "get": {
        "description": "Event frequency and clearance by road corridor. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "avg_severity": 2.6,
                    "event_count": 245,
                    "jurisdiction": "ON",
                    "last_event": "2026-03-29T14:35:00Z",
                    "road_name": "Highway 401"
                  },
                  {
                    "avg_severity": 2.1,
                    "event_count": 112,
                    "jurisdiction": "GA",
                    "last_event": "2026-03-29T11:08:00Z",
                    "road_name": "I-75"
                  }
                ]
              }
            },
            "description": "Corridor stats"
          }
        },
        "summary": "Corridor reliability",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/event-weather/{id}": {
      "get": {
        "description": "Returns the weather conditions near an event at the time it was created.\nCaptures nearest weather station data (temperature, wind, visibility,\nprecipitation, road surface, humidity) via PostGIS spatial query.\nRequires Pro+ plan.\n",
        "parameters": [
          {
            "description": "Event ID",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "created_at": "2026-03-29T14:30:00Z",
                  "distance_km": 3.2,
                  "event_id": "on-evt-401-001",
                  "humidity": 88,
                  "id": 42,
                  "precipitation": "snow",
                  "reading_recorded_at": "2026-03-29T14:28:00Z",
                  "road_surface": "icy",
                  "station_feature_id": "on-wx-401-north-york",
                  "temperature": -2.5,
                  "visibility": 1.5,
                  "wind_direction": "NW",
                  "wind_speed": 35
                },
                "schema": {
                  "$ref": "#/components/schemas/EventWeatherSnapshot"
                }
              }
            },
            "description": "Weather snapshot for the event"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "no weather snapshot for this event"
                }
              }
            },
            "description": "No weather snapshot for this event"
          }
        },
        "summary": "Event weather snapshot",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/feature-history": {
      "get": {
        "description": "Lifecycle changes for features. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "change_type": "deactivated",
                    "changed_field": "is_active",
                    "feature_id": "on-cam-401-yonge",
                    "feature_type": "cameras",
                    "id": 4011,
                    "new_value": "false",
                    "old_value": "true",
                    "recorded_at": "2026-03-15T08:00:00Z",
                    "source": "ON"
                  },
                  {
                    "change_type": "reactivated",
                    "changed_field": "is_active",
                    "feature_id": "on-cam-401-yonge",
                    "feature_type": "cameras",
                    "id": 4012,
                    "new_value": "true",
                    "old_value": "false",
                    "recorded_at": "2026-03-16T06:00:00Z",
                    "source": "ON"
                  }
                ]
              }
            },
            "description": "Feature history entries"
          }
        },
        "summary": "Feature history",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/fetch-log": {
      "get": {
        "description": "Fetch operation statistics. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "country": "CA",
                    "created_at": "2026-03-29T14:55:00Z",
                    "duration_ms": 380,
                    "error_message": null,
                    "id": 9821445,
                    "items_count": 125,
                    "jurisdiction_name": "Ontario",
                    "resource": "events",
                    "source_code": "on",
                    "source_resource_id": 12,
                    "status": "success"
                  },
                  {
                    "country": "US",
                    "created_at": "2026-03-29T14:54:00Z",
                    "duration_ms": 520,
                    "error_message": null,
                    "id": 9821444,
                    "items_count": 450,
                    "jurisdiction_name": "Georgia",
                    "resource": "cameras",
                    "source_code": "ga",
                    "source_resource_id": 8,
                    "status": "success"
                  }
                ]
              }
            },
            "description": "Fetch log data"
          }
        },
        "summary": "Fetch log analytics",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/history/{id}": {
      "get": {
        "description": "Lifecycle changes for a specific event. Requires Pro+ plan.\n\n`change_type` values include `created`, `severity_change`,\n`lanes_change`, `end_time_change`, `estimated_end_time_change`,\n`archived`, and `stale_sweep`. The last two both mean the event\nis no longer active — see `archive_reason` on the `TrafficEvent`\nschema for the distinction.\n",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "change_type": "created",
                    "event_id": "on-evt-401-001",
                    "event_type": "incident",
                    "id": 0,
                    "jurisdiction": "ON",
                    "recorded_at": "2026-03-29T14:30:00Z",
                    "road_name": "Highway 401",
                    "severity": "moderate",
                    "source": "ON"
                  },
                  {
                    "change_type": "severity_change",
                    "event_id": "on-evt-401-001",
                    "event_type": "incident",
                    "id": 2,
                    "jurisdiction": "ON",
                    "new_value": "major",
                    "old_value": "moderate",
                    "recorded_at": "2026-03-29T14:35:00Z",
                    "road_name": "Highway 401",
                    "severity": "major",
                    "source": "ON"
                  },
                  {
                    "change_type": "lanes_change",
                    "event_id": "on-evt-401-001",
                    "event_type": "incident",
                    "id": 3,
                    "jurisdiction": "ON",
                    "new_value": "2 of 4 lanes blocked",
                    "old_value": "1 of 4 lanes blocked",
                    "recorded_at": "2026-03-29T14:40:00Z",
                    "road_name": "Highway 401",
                    "severity": "major",
                    "source": "ON"
                  }
                ]
              }
            },
            "description": "Array of history entries"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "analytics access requires Pro plan or higher"
                }
              }
            },
            "description": "Analytics not available on current plan"
          }
        },
        "summary": "Event history timeline",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/hotspots": {
      "get": {
        "description": "Geographic clustering of events. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "avg_severity": 2.8,
                    "event_count": 28,
                    "latitude": 43.651,
                    "longitude": -79.347,
                    "top_road": "Highway 401"
                  },
                  {
                    "avg_severity": 2.2,
                    "event_count": 15,
                    "latitude": 33.758,
                    "longitude": -84.392,
                    "top_road": "I-75"
                  }
                ]
              }
            },
            "description": "Hotspot clusters"
          }
        },
        "summary": "Hotspot clustering",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/source-health": {
      "get": {
        "description": "Per-source fetch success/failure rates. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "avg_duration_ms": 450,
                    "avg_items_count": 124.5,
                    "avg_response_bytes": 184320,
                    "error_count": 5,
                    "p95_duration_ms": 980,
                    "resource": "events",
                    "source_code": "on",
                    "success_count": 2875,
                    "success_rate": 99.83,
                    "total_fetches": 2880
                  },
                  {
                    "avg_duration_ms": 620,
                    "avg_items_count": 450,
                    "avg_response_bytes": 612800,
                    "error_count": 18,
                    "p95_duration_ms": 1340,
                    "resource": "cameras",
                    "source_code": "ga",
                    "success_count": 2862,
                    "success_rate": 99.38,
                    "total_fetches": 2880
                  }
                ]
              }
            },
            "description": "Source health data"
          }
        },
        "summary": "Source health stats",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/trends": {
      "get": {
        "description": "Time-series event counts. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "count": 58,
                    "timestamp": "2026-03-29T00:00:00Z",
                    "type": "incident"
                  },
                  {
                    "count": 62,
                    "timestamp": "2026-03-29T00:00:00Z",
                    "type": "construction"
                  },
                  {
                    "count": 45,
                    "timestamp": "2026-03-28T00:00:00Z",
                    "type": "incident"
                  },
                  {
                    "count": 60,
                    "timestamp": "2026-03-28T00:00:00Z",
                    "type": "construction"
                  }
                ]
              }
            },
            "description": "Trend data points"
          }
        },
        "summary": "Event trends",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/weather": {
      "get": {
        "description": "Historical weather station data. Requires Pro+ plan.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "feature_id": "ga-wx-i75-atl",
                    "humidity": 62,
                    "id": 88412,
                    "precipitation": "none",
                    "recorded_at": "2026-03-29T14:45:00Z",
                    "road_surface": "dry",
                    "source": "ga",
                    "temperature": 18.5,
                    "visibility": 16,
                    "wind_direction": "NW",
                    "wind_speed": 12.3
                  }
                ]
              }
            },
            "description": "Weather readings"
          }
        },
        "summary": "Weather readings history",
        "tags": [
          "analytics"
        ]
      }
    },
    "/analytics/weather-correlation": {
      "get": {
        "description": "Returns aggregated weather conditions across events, grouped by\nprecipitation type and road surface. Shows how weather conditions\ncorrelate with incident frequency and severity. Requires Pro+ plan.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "description": "Filter by event type",
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "severity",
            "schema": {
              "enum": [
                "minor",
                "moderate",
                "major",
                "critical"
              ],
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "from",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "to",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Max station distance in km (default 50)",
            "in": "query",
            "name": "max_dist_km",
            "schema": {
              "default": 50,
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "avg_severity": 3.2,
                    "avg_temp_c": -5.1,
                    "avg_visibility": 2.3,
                    "avg_wind_speed": 28.4,
                    "event_count": 84,
                    "precipitation": "snow",
                    "road_surface": "icy"
                  },
                  {
                    "avg_severity": 2.1,
                    "avg_temp_c": 8.3,
                    "avg_visibility": 8.5,
                    "avg_wind_speed": 18.7,
                    "event_count": 156,
                    "precipitation": "rain",
                    "road_surface": "wet"
                  },
                  {
                    "avg_severity": 1.8,
                    "avg_temp_c": 15.2,
                    "avg_visibility": 16,
                    "avg_wind_speed": 10.1,
                    "event_count": 320,
                    "precipitation": "none",
                    "road_surface": "dry"
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/WeatherCorrelationStats"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Correlation statistics"
          }
        },
        "summary": "Weather-incident correlation",
        "tags": [
          "analytics"
        ]
      }
    },
    "/events": {
      "get": {
        "description": "Paginated list of traffic events with filters. Free plan requires\njurisdiction parameter.\n\n**Batch lookup**: pass `?ids=id1,id2,...` to fetch a known set of\nevents by id. When `ids` is set, all other filters except plan\ndata delay are ignored, status is not enforced (archived events\nare returned alongside active ones), and pagination is disabled —\nthe response always returns the full set in one page. The batch\nsize is capped by the plan's `max_batch_size`.\n",
        "parameters": [
          {
            "description": "Comma-separated event IDs for batch lookup. Mutually exclusive\nwith the filter parameters below (when present, other filters\nare ignored). Bounded by the plan's `max_batch_size`.\n",
            "in": "query",
            "name": "ids",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/offset"
          },
          {
            "$ref": "#/components/parameters/bbox"
          },
          {
            "$ref": "#/components/parameters/lat"
          },
          {
            "$ref": "#/components/parameters/lng"
          },
          {
            "$ref": "#/components/parameters/radius_km"
          },
          {
            "description": "Filter by event type",
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "severity",
            "schema": {
              "enum": [
                "minor",
                "moderate",
                "major",
                "critical"
              ],
              "type": "string"
            }
          },
          {
            "description": "`active` (default) returns active events, `archived` returns\narchived events only, `all` disables the filter and returns both.\n",
            "in": "query",
            "name": "status",
            "schema": {
              "default": "active",
              "enum": [
                "active",
                "archived",
                "all"
              ],
              "type": "string"
            }
          },
          {
            "description": "Filter by road name (partial match)",
            "in": "query",
            "name": "road",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "affected_roads": [
                        "Highway 401"
                      ],
                      "created_at": "2026-03-29T14:30:00Z",
                      "description": "Two right lanes blocked. Emergency services on scene. Expect delays of 30+ minutes.",
                      "direction": "westbound",
                      "end_time": null,
                      "estimated_end_time": "2026-03-29T17:00:00Z",
                      "id": "on-evt-401-001",
                      "jurisdiction": "ON",
                      "lanes_affected": "2 of 4 lanes blocked",
                      "last_updated": "2026-03-29T14:45:00Z",
                      "latitude": 43.65,
                      "location": {
                        "coordinates": [
                          -79.38,
                          43.65
                        ],
                        "type": "Point"
                      },
                      "longitude": -79.38,
                      "road_class": "interstate",
                      "severity": "major",
                      "source": "on",
                      "source_id": "EVT-2026-04521",
                      "start_time": "2026-03-29T14:30:00Z",
                      "status": "active",
                      "title": "Multi-vehicle collision on Highway 401 WB near Yonge St",
                      "type": "incident"
                    },
                    {
                      "affected_roads": [
                        "I-75"
                      ],
                      "created_at": "2026-03-25T06:00:00Z",
                      "description": "Road resurfacing. Right lane closed from mile marker 248 to 251.",
                      "direction": "northbound",
                      "end_time": null,
                      "estimated_end_time": "2026-04-15T18:00:00Z",
                      "id": "ga-evt-i75-002",
                      "jurisdiction": "GA",
                      "lanes_affected": "1 of 3 lanes closed",
                      "last_updated": "2026-03-29T08:00:00Z",
                      "latitude": 33.75,
                      "location": {
                        "coordinates": [
                          -84.39,
                          33.75
                        ],
                        "type": "Point"
                      },
                      "longitude": -84.39,
                      "road_class": "interstate",
                      "severity": "moderate",
                      "source": "ga",
                      "source_id": "GA-CONST-8832",
                      "start_time": "2026-03-25T06:00:00Z",
                      "status": "active",
                      "title": "Lane closure on I-75 NB near Exit 249",
                      "type": "construction"
                    }
                  ],
                  "has_more": false,
                  "limit": 100,
                  "offset": 0,
                  "total": 2
                },
                "schema": {
                  "$ref": "#/components/schemas/PagedEvents"
                }
              }
            },
            "description": "Paginated events"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "jurisdiction parameter required on Free plan"
                }
              }
            },
            "description": "Plan limit exceeded (jurisdiction required, trial expired)"
          }
        },
        "summary": "List traffic events",
        "tags": [
          "events"
        ]
      }
    },
    "/events/geojson": {
      "get": {
        "description": "Returns events as a GeoJSON FeatureCollection. Requires Starter+ plan.",
        "parameters": [
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "$ref": "#/components/parameters/bbox"
          },
          {
            "$ref": "#/components/parameters/lat"
          },
          {
            "$ref": "#/components/parameters/lng"
          },
          {
            "$ref": "#/components/parameters/radius_km"
          },
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "severity",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`active` (default) returns active events, `archived` returns\narchived events only, `all` disables the filter and returns both.\n",
            "in": "query",
            "name": "status",
            "schema": {
              "default": "active",
              "enum": [
                "active",
                "archived",
                "all"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/geo+json": {
                "example": {
                  "features": [
                    {
                      "geometry": {
                        "coordinates": [
                          -79.38,
                          43.65
                        ],
                        "type": "Point"
                      },
                      "properties": {
                        "affected_roads": [
                          "Highway 401"
                        ],
                        "direction": "westbound",
                        "id": "on-evt-401-001",
                        "jurisdiction": "ON",
                        "severity": "major",
                        "source": "on",
                        "start_time": "2026-03-29T14:30:00Z",
                        "status": "active",
                        "title": "Multi-vehicle collision on Highway 401 WB near Yonge St",
                        "type": "incident"
                      },
                      "type": "Feature"
                    }
                  ],
                  "type": "FeatureCollection"
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "GeoJSON FeatureCollection"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "GeoJSON access requires Starter plan or higher"
                }
              }
            },
            "description": "GeoJSON not available on Free plan"
          }
        },
        "summary": "Events as GeoJSON",
        "tags": [
          "events"
        ]
      }
    },
    "/events/{id}": {
      "get": {
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "affected_roads": [
                    "Highway 401"
                  ],
                  "created_at": "2026-03-29T14:30:00Z",
                  "description": "Two right lanes blocked. Emergency services on scene. Expect delays of 30+ minutes.",
                  "direction": "westbound",
                  "end_time": null,
                  "estimated_end_time": "2026-03-29T17:00:00Z",
                  "id": "on-evt-401-001",
                  "jurisdiction": "ON",
                  "lanes_affected": "2 of 4 lanes blocked",
                  "last_updated": "2026-03-29T14:45:00Z",
                  "latitude": 43.65,
                  "location": {
                    "coordinates": [
                      -79.38,
                      43.65
                    ],
                    "type": "Point"
                  },
                  "longitude": -79.38,
                  "road_class": "interstate",
                  "severity": "major",
                  "source": "on",
                  "source_id": "EVT-2026-04521",
                  "start_time": "2026-03-29T14:30:00Z",
                  "status": "active",
                  "title": "Multi-vehicle collision on Highway 401 WB near Yonge St",
                  "type": "incident"
                },
                "schema": {
                  "$ref": "#/components/schemas/TrafficEvent"
                }
              }
            },
            "description": "Event detail"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "event not found"
                }
              }
            },
            "description": "Event not found"
          }
        },
        "summary": "Get event by ID",
        "tags": [
          "events"
        ]
      }
    },
    "/features": {
      "get": {
        "description": "Paginated features filtered by type or group. Either `type` or `group` is required. Free plan requires jurisdiction parameter.",
        "parameters": [
          {
            "description": "Feature type: cameras, rest_areas, weigh_stations, weather_stations, ev_charging, signs, road_conditions, etc. Required unless `group` is provided.",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Feature group id. Convenience expansion to all feature_types in the group. See GET /features/groups.",
            "in": "query",
            "name": "group",
            "required": false,
            "schema": {
              "enum": [
                "imagery",
                "weather",
                "road_conditions",
                "traffic_performance",
                "planned_events",
                "alerts_advisories",
                "wildfires",
                "trucking",
                "traveler_services",
                "fuel_charging",
                "borders",
                "ferries",
                "transit",
                "tolls",
                "static_infrastructure",
                "operations"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/offset"
          },
          {
            "$ref": "#/components/parameters/bbox"
          },
          {
            "$ref": "#/components/parameters/lat"
          },
          {
            "$ref": "#/components/parameters/lng"
          },
          {
            "$ref": "#/components/parameters/radius_km"
          },
          {
            "description": "Tri-state filter on `is_active`. `true` returns active only,\n`false` returns inactive (archived) only, omitted returns both.\n",
            "in": "query",
            "name": "active",
            "schema": {
              "enum": [
                "true",
                "false"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "description": "Traffic camera - Highway 401 westbound at Yonge Street",
                      "direction": "westbound",
                      "feature_type": "cameras",
                      "has_details": true,
                      "id": "on-cam-401-yonge",
                      "is_active": true,
                      "jurisdiction": "ON",
                      "last_updated": "2026-03-29T14:50:00Z",
                      "latitude": 43.6532,
                      "longitude": -79.3832,
                      "name": "Hwy 401 at Yonge St",
                      "properties": {
                        "url": "https://511on.ca/cameras/cam_401_yonge.jpg",
                        "video_url": null
                      },
                      "road_name": "Highway 401",
                      "source": "on"
                    },
                    {
                      "description": "RWIS weather station on I-75 near downtown Atlanta",
                      "direction": null,
                      "feature_type": "weather_stations",
                      "has_details": false,
                      "id": "ga-wx-i75-atl",
                      "is_active": true,
                      "jurisdiction": "GA",
                      "last_updated": "2026-03-29T14:45:00Z",
                      "latitude": 33.749,
                      "longitude": -84.388,
                      "name": "I-75 Atlanta Weather Station",
                      "properties": {
                        "humidity": 62,
                        "precipitation": "none",
                        "road_surface": "dry",
                        "temperature": 18.5,
                        "visibility": 16,
                        "wind_direction": "NW",
                        "wind_speed": 12.3
                      },
                      "road_name": "I-75",
                      "source": "ga"
                    }
                  ],
                  "has_more": false,
                  "limit": 100,
                  "offset": 0,
                  "total": 2
                },
                "schema": {
                  "$ref": "#/components/schemas/PagedFeatures"
                }
              }
            },
            "description": "Paginated features"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "type parameter is required"
                }
              }
            },
            "description": "Missing type parameter"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "jurisdiction parameter required on Free plan"
                }
              }
            },
            "description": "Plan limit exceeded"
          }
        },
        "summary": "List features",
        "tags": [
          "features"
        ]
      }
    },
    "/features/details/batch": {
      "post": {
        "description": "Fan-out variant of `/features/{id}/details` for callers that need to\nresolve many features at once. POST is used so the id list isn't\nconstrained by URL length.\n\nPer-id caching is identical to the single endpoint (Redis-backed\nkeyed on the id), so a batch that's a superset of previously\nrequested ids costs nothing extra for the overlap. Partial success\nis preserved: a single failed upstream fetch sets `cache: \"error\"`\nwith an `error` string on that entry without failing the whole\nrequest. Missing ids land in the response with `not_found: true`.\n\nBounded by the plan's `max_batch_size`.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "ids": [
                  "on-cam-401-yonge",
                  "ga-cam-i75-249",
                  "missing-id"
                ]
              },
              "schema": {
                "properties": {
                  "ids": {
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "type": "array"
                  }
                },
                "required": [
                  "ids"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "count": 3,
                  "data": [
                    {
                      "cache": "hit",
                      "data": {
                        "feature_type": "cameras",
                        "id": "on-cam-401-yonge",
                        "jurisdiction": "ON",
                        "name": "Hwy 401 at Yonge St",
                        "properties": {
                          "url": "https://511on.ca/cameras/cam_401_yonge.jpg"
                        },
                        "source": "on"
                      },
                      "detail_available": true,
                      "id": "on-cam-401-yonge"
                    },
                    {
                      "cache": "miss",
                      "data": {
                        "feature_type": "cameras",
                        "id": "ga-cam-i75-249",
                        "jurisdiction": "GA",
                        "name": "I-75 at Exit 249",
                        "source": "ga"
                      },
                      "detail_available": true,
                      "id": "ga-cam-i75-249"
                    },
                    {
                      "cache": "",
                      "detail_available": false,
                      "id": "missing-id",
                      "not_found": true
                    }
                  ]
                }
              }
            },
            "description": "Per-id detail results (one entry per requested id, in order)"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "ids is required and must contain at least one non-empty id"
                }
              }
            },
            "description": "Invalid JSON body or empty ids array"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "batch too large: 150 ids requested, max 100 on the Pro plan — upgrade for more"
                }
              }
            },
            "description": "Batch exceeds plan's max_batch_size"
          }
        },
        "summary": "Feature detail batch (lazy-loaded)",
        "tags": [
          "features"
        ]
      }
    },
    "/features/geojson": {
      "get": {
        "description": "Returns features as a GeoJSON FeatureCollection. Requires Starter+ plan.",
        "parameters": [
          {
            "in": "query",
            "name": "type",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "$ref": "#/components/parameters/bbox"
          },
          {
            "description": "Defaults to active-only for map rendering. `active=false` drops the\nfilter and returns both active and inactive features (asymmetric vs\n`/features` for back-compat with map clients — use `/features?active=false`\nto fetch inactive-only).\n",
            "in": "query",
            "name": "active",
            "schema": {
              "enum": [
                "true",
                "false"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/geo+json": {
                "example": {
                  "features": [
                    {
                      "geometry": {
                        "coordinates": [
                          -79.3832,
                          43.6532
                        ],
                        "type": "Point"
                      },
                      "properties": {
                        "feature_type": "cameras",
                        "id": "on-cam-401-yonge",
                        "is_active": true,
                        "jurisdiction": "ON",
                        "name": "Hwy 401 at Yonge St",
                        "source": "on"
                      },
                      "type": "Feature"
                    }
                  ],
                  "type": "FeatureCollection"
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "GeoJSON FeatureCollection"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "GeoJSON access requires Starter plan or higher"
                }
              }
            },
            "description": "GeoJSON not available on Free plan"
          }
        },
        "summary": "Features as GeoJSON",
        "tags": [
          "features"
        ]
      }
    },
    "/features/groups": {
      "get": {
        "description": "Returns the 16 feature-type groups with their member feature_types. Use the group `id` with `GET /features?group=\u003cid\u003e` to fetch all features in that group.",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "description": "Live traffic camera feeds",
                    "feature_types": [
                      "cameras"
                    ],
                    "id": "imagery",
                    "name": "Imagery",
                    "sort_order": 1
                  },
                  {
                    "description": "Weather stations, forecasts, alerts and warnings",
                    "feature_types": [
                      "weather_stations",
                      "regional_weather",
                      "weather_forecasts",
                      "weather_alerts",
                      "wind_warnings"
                    ],
                    "id": "weather",
                    "name": "Weather \u0026 Environment",
                    "sort_order": 2
                  },
                  {
                    "description": "Truck restrictions, routes, parking, inspections",
                    "feature_types": [
                      "truck_restrictions",
                      "weight_restrictions",
                      "bridge_clearances",
                      "truck_routes",
                      "freight_corridors",
                      "truck_parking",
                      "truck_rest_areas",
                      "weigh_stations",
                      "inspection_stations"
                    ],
                    "id": "trucking",
                    "name": "Trucking \u0026 Commercial Vehicles",
                    "sort_order": 8
                  }
                ]
              }
            },
            "description": "Feature group taxonomy"
          }
        },
        "summary": "List feature groups (taxonomy)",
        "tags": [
          "features"
        ]
      }
    },
    "/features/types": {
      "get": {
        "description": "Returns all feature types with total and active counts",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "active_count": 11893,
                    "count": 12450,
                    "type": "cameras"
                  },
                  {
                    "active_count": 3142,
                    "count": 3200,
                    "type": "weather_stations"
                  },
                  {
                    "active_count": 4651,
                    "count": 4870,
                    "type": "signs"
                  },
                  {
                    "active_count": 1580,
                    "count": 1580,
                    "type": "rest_areas"
                  },
                  {
                    "active_count": 98500,
                    "count": 98500,
                    "type": "ev_charging"
                  },
                  {
                    "active_count": 45200,
                    "count": 45200,
                    "type": "bridge_clearances"
                  },
                  {
                    "active_count": 7200,
                    "count": 8900,
                    "type": "truck_restrictions"
                  },
                  {
                    "active_count": 1850,
                    "count": 2100,
                    "type": "road_conditions"
                  }
                ]
              }
            },
            "description": "Feature type counts"
          }
        },
        "summary": "List feature types",
        "tags": [
          "features"
        ]
      }
    },
    "/features/{id}/details": {
      "get": {
        "description": "Fetches detailed properties for a feature on demand",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "cache": "miss",
                  "data": {
                    "description": "Traffic camera - Highway 401 westbound at Yonge Street",
                    "direction": "westbound",
                    "feature_type": "cameras",
                    "id": "on-cam-401-yonge",
                    "is_active": true,
                    "jurisdiction": "ON",
                    "last_updated": "2026-03-29T14:50:00Z",
                    "latitude": 43.6532,
                    "longitude": -79.3832,
                    "name": "Hwy 401 at Yonge St",
                    "properties": {
                      "url": "https://511on.ca/cameras/cam_401_yonge.jpg",
                      "video_url": "https://511on.ca/cameras/cam_401_yonge.m3u8",
                      "views": [
                        {
                          "name": "Westbound",
                          "url": "https://511on.ca/cameras/cam_401_yonge_wb.jpg"
                        },
                        {
                          "name": "Eastbound",
                          "url": "https://511on.ca/cameras/cam_401_yonge_eb.jpg"
                        }
                      ]
                    },
                    "road_name": "Highway 401",
                    "source": "on"
                  },
                  "detail_available": true
                }
              }
            },
            "description": "Feature with full properties"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "feature not found"
                }
              }
            },
            "description": "Feature not found"
          }
        },
        "summary": "Feature detail (lazy-loaded)",
        "tags": [
          "features"
        ]
      }
    },
    "/fuel-prices": {
      "get": {
        "description": "Returns regional fuel prices. Weekly US state-level (EIA) and monthly Canadian\nprovince-level (StatCan). Use `latest=true` to get only the most recent observation\nper jurisdiction + fuel_type. 24-hour cache (data refreshes daily at the source).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "description": "Filter by country",
            "in": "query",
            "name": "country",
            "schema": {
              "enum": [
                "US",
                "CA"
              ],
              "type": "string"
            }
          },
          {
            "description": "Filter by fuel grade",
            "in": "query",
            "name": "fuel_type",
            "schema": {
              "enum": [
                "regular",
                "midgrade",
                "premium",
                "diesel"
              ],
              "type": "string"
            }
          },
          {
            "description": "Start date (YYYY-MM-DD)",
            "in": "query",
            "name": "from",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End date (YYYY-MM-DD)",
            "in": "query",
            "name": "to",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Return only the most recent observation per jurisdiction + fuel_type",
            "in": "query",
            "name": "latest",
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/offset"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "country": "US",
                      "currency": "USD",
                      "frequency": "weekly",
                      "fuel_type": "regular",
                      "id": 12048,
                      "jurisdiction": "CA",
                      "period_date": "2026-05-05",
                      "price": 4.85,
                      "source": "EIA",
                      "unit": "gallon"
                    },
                    {
                      "country": "CA",
                      "currency": "CAD",
                      "frequency": "monthly",
                      "fuel_type": "regular",
                      "id": 12122,
                      "jurisdiction": "ON",
                      "period_date": "2026-04-01",
                      "price": 1.62,
                      "source": "StatCan",
                      "unit": "litre"
                    }
                  ],
                  "has_more": true,
                  "limit": 100,
                  "offset": 0,
                  "total": 312
                }
              }
            },
            "description": "Paginated fuel prices"
          }
        },
        "summary": "Regional fuel prices",
        "tags": [
          "fuel-prices"
        ]
      }
    },
    "/health": {
      "get": {
        "description": "Returns server status, version, uptime, and the combined live status\nof the Postgres and Redis dependencies in `db` (formatted as\n`\u003cpostgres\u003e/\u003credis\u003e`, each segment `ok` or `down`). Returns 200 when\nboth respond within 2 seconds; otherwise 503 with the same body shape.\n",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "db": "ok/ok",
                  "status": "ok",
                  "uptime": "2h30m",
                  "version": "0.1.0"
                },
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "All dependencies healthy"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "db": "ok/down",
                  "status": "degraded",
                  "uptime": "2h30m",
                  "version": "0.1.0"
                },
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            },
            "description": "One or more dependencies unhealthy"
          }
        },
        "security": [],
        "summary": "Health check",
        "tags": [
          "metadata"
        ]
      }
    },
    "/jurisdictions": {
      "get": {
        "description": "Returns all active jurisdictions. By default, only real geographic\njurisdictions (US states + Canadian provinces, `scope=state`) are\nreturned — cross-cutting federal sources and sub-state regional\nfeeds are filtered out so customer pickers stay clean.\n\nUse `?scope=federal` to list cross-cutting federal sources (EIA,\nFHWA, WZDX_NPS), `?scope=regional` for sub-state feeds (511SF,\nWZDX_AUSTIN, ...), or `?scope=all` to return everything.\n\nEach row's `group_id` (when present) points at a record in\n`GET /jurisdictions/groups`: a state's primary feed plus its WZDx\nand sub-state siblings collapsed under one umbrella id.\n",
        "parameters": [
          {
            "description": "Filter by jurisdiction scope; default `state`",
            "in": "query",
            "name": "scope",
            "schema": {
              "default": "state",
              "enum": [
                "state",
                "federal",
                "regional",
                "all"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "code": "ON",
                    "country": "CA",
                    "group_id": null,
                    "is_active": true,
                    "name": "Ontario",
                    "scope": "state"
                  },
                  {
                    "code": "GA",
                    "country": "US",
                    "group_id": null,
                    "is_active": true,
                    "name": "Georgia",
                    "scope": "state"
                  },
                  {
                    "code": "CA",
                    "country": "US",
                    "group_id": "us-ca",
                    "is_active": true,
                    "name": "California",
                    "scope": "state"
                  },
                  {
                    "code": "NY",
                    "country": "US",
                    "group_id": "us-ny",
                    "is_active": true,
                    "name": "New York",
                    "scope": "state"
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Jurisdiction"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Array of jurisdictions"
          }
        },
        "summary": "List jurisdictions",
        "tags": [
          "metadata"
        ]
      }
    },
    "/jurisdictions/groups": {
      "get": {
        "description": "Returns the canonical bundles for states whose feed is split across\nmultiple codes (state + WZDx + optional CWZ + optional sub-state\nregional). Querying `?jurisdiction=\u003cprimary_jurisdiction_code\u003e` on\n`/events` or `/features` is transparently expanded to all members.\n",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "description": "California feeds (state + WZDx + 511SF Bay Area)",
                    "id": "us-ca",
                    "members": [
                      "CA",
                      "WZDX_CA",
                      "511SF"
                    ],
                    "name": "California",
                    "primary_jurisdiction_code": "CA",
                    "sort_order": 2
                  },
                  {
                    "description": "Colorado feeds (state + WZDx + CWZ)",
                    "id": "us-co",
                    "members": [
                      "CO",
                      "WZDX_CO",
                      "WZDX_CWZ_CO"
                    ],
                    "name": "Colorado",
                    "primary_jurisdiction_code": "CO",
                    "sort_order": 3
                  },
                  {
                    "description": "Texas feeds (state + WZDx + sub-state regional)",
                    "id": "us-tx",
                    "members": [
                      "TX",
                      "WZDX_TX",
                      "WZDX_AUSTIN"
                    ],
                    "name": "Texas",
                    "primary_jurisdiction_code": "TX",
                    "sort_order": 24
                  }
                ],
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/JurisdictionGroup"
                  },
                  "type": "array"
                }
              }
            },
            "description": "Array of jurisdiction groups"
          }
        },
        "summary": "List jurisdiction groups",
        "tags": [
          "metadata"
        ]
      }
    },
    "/map/config": {
      "get": {
        "description": "Returns map detail level and other config",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "detail_level": "compact",
                  "max_results": 100,
                  "rpm": 60
                }
              }
            },
            "description": "Map config"
          }
        },
        "summary": "Map configuration",
        "tags": [
          "map"
        ]
      }
    },
    "/map/events": {
      "get": {
        "description": "Public, capped to MAP_MAX_RESULTS, no pagination (offset always 0). No API key required.",
        "parameters": [
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/bbox"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": "on-evt-401-001",
                      "jurisdiction": "ON",
                      "last_updated": "2026-03-29T14:45:00Z",
                      "latitude": 43.65,
                      "longitude": -79.38,
                      "road_class": "interstate",
                      "severity": "major",
                      "source": "on",
                      "start_time": "2026-03-29T14:30:00Z",
                      "status": "active",
                      "title": "Multi-vehicle collision on Highway 401 WB",
                      "type": "incident"
                    }
                  ],
                  "has_more": false,
                  "limit": 100,
                  "offset": 0,
                  "total": 1
                }
              }
            },
            "description": "Paginated events"
          }
        },
        "security": [],
        "summary": "Map events",
        "tags": [
          "map"
        ]
      }
    },
    "/map/events/geojson": {
      "get": {
        "description": "Public, same restrictions as /map/events (capped, no pagination)",
        "responses": {
          "200": {
            "content": {
              "application/geo+json": {
                "example": {
                  "features": [
                    {
                      "geometry": {
                        "coordinates": [
                          -79.38,
                          43.65
                        ],
                        "type": "Point"
                      },
                      "properties": {
                        "id": "on-evt-401-001",
                        "jurisdiction": "ON",
                        "severity": "major",
                        "source": "on",
                        "title": "Multi-vehicle collision on Highway 401 WB",
                        "type": "incident"
                      },
                      "type": "Feature"
                    }
                  ],
                  "type": "FeatureCollection"
                }
              }
            },
            "description": "GeoJSON FeatureCollection"
          }
        },
        "summary": "Map events GeoJSON",
        "tags": [
          "map"
        ]
      }
    },
    "/map/features": {
      "get": {
        "description": "Public, capped to MAP_MAX_RESULTS, no pagination (offset always 0)",
        "parameters": [
          {
            "in": "query",
            "name": "type",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/bbox"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "feature_type": "cameras",
                      "id": "on-cam-401-yonge",
                      "is_active": true,
                      "jurisdiction": "ON",
                      "last_updated": "2026-03-29T14:50:00Z",
                      "latitude": 43.6532,
                      "longitude": -79.3832,
                      "name": "Hwy 401 at Yonge St",
                      "source": "on"
                    }
                  ],
                  "has_more": false,
                  "limit": 100,
                  "offset": 0,
                  "total": 1
                }
              }
            },
            "description": "Paginated features (capped)"
          }
        },
        "summary": "Map features",
        "tags": [
          "map"
        ]
      }
    },
    "/map/features/geojson": {
      "get": {
        "description": "Public, same restrictions as /map/features (capped, no pagination)",
        "responses": {
          "200": {
            "content": {
              "application/geo+json": {
                "example": {
                  "features": [
                    {
                      "geometry": {
                        "coordinates": [
                          -79.3832,
                          43.6532
                        ],
                        "type": "Point"
                      },
                      "properties": {
                        "feature_type": "cameras",
                        "id": "on-cam-401-yonge",
                        "is_active": true,
                        "jurisdiction": "ON",
                        "name": "Hwy 401 at Yonge St",
                        "source": "on"
                      },
                      "type": "Feature"
                    }
                  ],
                  "type": "FeatureCollection"
                }
              }
            },
            "description": "GeoJSON FeatureCollection"
          }
        },
        "summary": "Map features GeoJSON",
        "tags": [
          "map"
        ]
      }
    },
    "/map/features/types": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": [
                  {
                    "active_count": 11893,
                    "count": 12450,
                    "type": "cameras"
                  },
                  {
                    "active_count": 3142,
                    "count": 3200,
                    "type": "weather_stations"
                  },
                  {
                    "active_count": 4651,
                    "count": 4870,
                    "type": "signs"
                  }
                ]
              }
            },
            "description": "Feature type counts"
          }
        },
        "summary": "Map feature types",
        "tags": [
          "map"
        ]
      }
    },
    "/plans": {
      "get": {
        "description": "Returns active subscription plans with their gating columns (RPM,\nrequest quotas, allow_* flags) **and** their admin-curated marketing\nbullets (`features`). The response is wrapped in a `plans` array;\nwhen Paddle is configured the response also carries\n`paddle_client_token` + `paddle_env` for the checkout overlay.\n\nResolution order for each feature on the frontend:\n`i18n_key` (when the current locale has it) → raw `label` → key.\n",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "paddle_client_token": "live_abc123",
                  "paddle_env": "live",
                  "plans": [
                    {
                      "allow_analytics": false,
                      "allow_geojson": false,
                      "allow_truck_data": false,
                      "code": "free",
                      "data_delay_sec": 900,
                      "description": "14-day trial with basic access",
                      "features": [
                        {
                          "icon": "check",
                          "id": 11,
                          "label": "60 RPM · 1k req/day",
                          "plan_id": 1,
                          "sort_order": 10
                        },
                        {
                          "icon": "check",
                          "id": 12,
                          "label": "2 jurisdictions",
                          "plan_id": 1,
                          "sort_order": 20
                        },
                        {
                          "i18n_key": "plan_feature.events_cameras",
                          "icon": "check",
                          "id": 13,
                          "plan_id": 1,
                          "sort_order": 30
                        },
                        {
                          "i18n_key": "plan_feature.email_support",
                          "icon": "check",
                          "id": 14,
                          "plan_id": 1,
                          "sort_order": 40
                        }
                      ],
                      "id": 1,
                      "is_active": true,
                      "max_jurisdictions": 2,
                      "max_keys": 1,
                      "max_requests_day": 1000,
                      "max_results_per_page": 100,
                      "name": "Free",
                      "rate_limit_rpm": 60,
                      "trial_days": 14
                    },
                    {
                      "allow_analytics": false,
                      "allow_geojson": true,
                      "allow_truck_data": false,
                      "code": "starter",
                      "data_delay_sec": 0,
                      "description": "For small apps and prototypes",
                      "features": [
                        {
                          "icon": "check",
                          "id": 21,
                          "label": "300 RPM · 50k req/day",
                          "plan_id": 2,
                          "sort_order": 10
                        },
                        {
                          "icon": "check",
                          "id": 22,
                          "label": "All 65 jurisdictions",
                          "plan_id": 2,
                          "sort_order": 20
                        },
                        {
                          "i18n_key": "plan_feature.geojson_export",
                          "icon": "check",
                          "id": 23,
                          "plan_id": 2,
                          "sort_order": 30
                        },
                        {
                          "icon": "check",
                          "id": 24,
                          "label": "500 results per page",
                          "plan_id": 2,
                          "sort_order": 40
                        }
                      ],
                      "id": 2,
                      "is_active": true,
                      "max_jurisdictions": 10,
                      "max_keys": 3,
                      "max_requests_day": 50000,
                      "max_results_per_page": 500,
                      "name": "Starter",
                      "rate_limit_rpm": 300,
                      "trial_days": 0
                    }
                  ]
                }
              }
            },
            "description": "Active plans + Paddle config"
          }
        },
        "security": [],
        "summary": "List subscription plans",
        "tags": [
          "metadata"
        ]
      }
    },
    "/routing/quota": {
      "get": {
        "description": "Returns the same `quota` block emitted alongside `/routing/truck`\nresponses, without performing a routing call (zero quota charge).\nUsed by the portal's subscription card to render the usage bar +\nactive top-ups list.\n",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "reset_at": "2026-06-01T00:00:00Z",
                  "subscription": {
                    "limit": 7500,
                    "plan": "pro_7500",
                    "tier": "pro",
                    "used": 234
                  },
                  "topups": [
                    {
                      "calls_added": 1000,
                      "calls_remaining": 750,
                      "expires_at": "2026-05-31T23:59:59Z",
                      "id": "01HK...",
                      "pack_sku": "topup_routing_truck_1000",
                      "purchased_at": "2026-05-04T09:12:00Z",
                      "title": "Standard Pack"
                    }
                  ],
                  "total": {
                    "remaining": 8016
                  }
                }
              }
            },
            "description": "Quota envelope"
          }
        },
        "summary": "Probe the customer's current routing quota envelope",
        "tags": [
          "routing"
        ]
      }
    },
    "/routing/truck": {
      "post": {
        "description": "Returns a routed polyline annotated with `warnings[]` — every nearby\nhazard, restriction, and operational note our database knows about\n(active incidents, planned construction, bridge clearance/weight\nproblems for the requested truck, public at-grade rail crossings\nwith 49 CFR 392.10 hazmat-stop awareness, truck/weight restrictions,\nweigh stations, weather, alerts, special events).\n\nEvery successful call:\n  * Counts against the customer's monthly routing quota (subscription\n    bucket first, then top-ups FIFO by expiry). Cache hits still\n    charge — the route is the API call regardless of whether we\n    re-used a cached payload.\n  * Auto-saves the result to `saved_routes` with a 30-minute TTL.\n    The returned `route_id` can be refetched (with re-evaluated\n    warnings) via `GET /routing/truck/saved/{id}` or persisted via\n    `POST /routing/truck/saved/{id}/persist`.\n\nThe `truck` block is required on every request. See the request\nschema for the full set of vehicle parameters.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "alternatives": {
                    "maximum": 3,
                    "minimum": 0,
                    "type": "integer"
                  },
                  "arrival_time": {
                    "description": "Mutually exclusive with departure_time",
                    "type": "string"
                  },
                  "avoid": {
                    "items": {
                      "enum": [
                        "highway",
                        "toll_road",
                        "ferry",
                        "tunnel",
                        "dirt_road",
                        "car_shuttle_train",
                        "seasonal_closure"
                      ],
                      "type": "string"
                    },
                    "type": "array"
                  },
                  "cargo": {
                    "description": "Cargo metadata. Not proxied to HERE; stored on the saved route\nand available to future enrichment rules.\n",
                    "properties": {
                      "hazmat_class": {
                        "description": "DOT placard class (e.g., \"1.3D\", \"3\", \"8\"). Finer-grained than\nthe truck.hazardous bucket; future enrichment rules can use the\nclass digit for stricter 49 CFR 392.10 rail-crossing logic.\n",
                        "type": "string"
                      }
                    },
                    "type": "object"
                  },
                  "currency": {
                    "description": "ISO 4217; only honored when tolls is requested",
                    "type": "string"
                  },
                  "customer_route_id": {
                    "description": "Opaque client-side correlation key. Echoed verbatim in the response.\nNot used by enrichment.\n",
                    "maxLength": 128,
                    "type": "string"
                  },
                  "departure_time": {
                    "description": "ISO 8601 or 'now'; defaults to 'now'",
                    "type": "string"
                  },
                  "destination": {
                    "properties": {
                      "lat": {
                        "type": "number"
                      },
                      "lng": {
                        "type": "number"
                      }
                    },
                    "required": [
                      "lat",
                      "lng"
                    ],
                    "type": "object"
                  },
                  "enrichment": {
                    "description": "Per-request tuning for the post-HERE truck-aware filter pipeline.\nNone of these fields are sent to HERE — they only shape what\n`warnings[]` contains. Omit the block to keep defaults.\n",
                    "properties": {
                      "buffer_m": {
                        "default": 100,
                        "description": "Spatial-join buffer radius around the polyline, in metres.",
                        "maximum": 1000,
                        "minimum": 10,
                        "type": "number"
                      },
                      "clearance_pad_m": {
                        "default": 0.15,
                        "description": "Safety pad added to `truck.height_m` when evaluating\nbridge_clearances. A warning fires when\n`clearance_m \u003c truck.height_m + clearance_pad_m`.\n",
                        "maximum": 1,
                        "minimum": 0,
                        "type": "number"
                      },
                      "exclude_types": {
                        "description": "Drop these feature_types from the spatial-join entirely. Useful\nfor trimming the response when a UI doesn't display, e.g., weigh\nstations.\n",
                        "items": {
                          "enum": [
                            "future_construction",
                            "bridge_clearances",
                            "rail_crossings",
                            "truck_restrictions",
                            "weight_restrictions",
                            "weigh_stations",
                            "inspection_stations",
                            "alerts",
                            "special_events",
                            "weather_stations"
                          ],
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "max_distance_m": {
                        "description": "Only emit warnings within this distance from the route origin.\n0 (default) = unlimited.\n",
                        "minimum": 0,
                        "type": "number"
                      },
                      "min_severity": {
                        "description": "Filter out warnings below this threshold. Ranking is\ninfo \u003c warning \u003c critical. Critical warnings (e.g., bridge\nclearance violations) always pass.\n",
                        "enum": [
                          "info",
                          "warning",
                          "critical"
                        ],
                        "type": "string"
                      },
                      "skip_temporal_filter": {
                        "default": false,
                        "description": "When true, keeps traffic events whose end_time is already past\nprojected arrival. Useful for planning-ahead runs.\n",
                        "type": "boolean"
                      }
                    },
                    "type": "object"
                  },
                  "language": {
                    "description": "BCP-47 (e.g. en-US)",
                    "type": "string"
                  },
                  "optimize": {
                    "default": "time",
                    "enum": [
                      "time",
                      "distance"
                    ],
                    "type": "string"
                  },
                  "origin": {
                    "properties": {
                      "lat": {
                        "type": "number"
                      },
                      "lng": {
                        "type": "number"
                      }
                    },
                    "required": [
                      "lat",
                      "lng"
                    ],
                    "type": "object"
                  },
                  "tags": {
                    "description": "Free-form labels for fleet/lane analytics rollup. Echoed and stored\non the saved route. Max 16 tags, each up to 64 chars.\n",
                    "items": {
                      "maxLength": 64,
                      "type": "string"
                    },
                    "maxItems": 16,
                    "type": "array"
                  },
                  "traffic": {
                    "default": "live",
                    "enum": [
                      "live",
                      "historical",
                      "off"
                    ],
                    "type": "string"
                  },
                  "truck": {
                    "description": "Required block (the endpoint is truck-only).",
                    "properties": {
                      "axle_count": {
                        "type": "integer"
                      },
                      "axle_weight_t": {
                        "type": "number"
                      },
                      "commercial": {
                        "type": "boolean"
                      },
                      "fuel": {
                        "enum": [
                          "diesel",
                          "gasoline",
                          "lpg",
                          "cng"
                        ],
                        "type": "string"
                      },
                      "hazardous": {
                        "items": {
                          "enum": [
                            "explosive",
                            "gas",
                            "flammable",
                            "combustible",
                            "organic",
                            "poison",
                            "radioactive",
                            "corrosive",
                            "poison_inhalation",
                            "harmful_to_water",
                            "other"
                          ],
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "height_m": {
                        "maximum": 5,
                        "type": "number"
                      },
                      "length_m": {
                        "maximum": 30,
                        "type": "number"
                      },
                      "max_speed_kph": {
                        "type": "integer"
                      },
                      "occupancy": {
                        "type": "integer"
                      },
                      "permit_number": {
                        "description": "Oversize/overweight permit identifier carried by this truck.\nNot proxied to HERE — stored on the saved route and echoed in\nthe response for client-side correlation.\n",
                        "type": "string"
                      },
                      "profile": {
                        "default": "tractor",
                        "enum": [
                          "tractor",
                          "straight",
                          "box"
                        ],
                        "type": "string"
                      },
                      "trailer_axle_count": {
                        "type": "integer"
                      },
                      "trailer_count": {
                        "type": "integer"
                      },
                      "tunnel_category": {
                        "enum": [
                          "B",
                          "C",
                          "D",
                          "E"
                        ],
                        "type": "string"
                      },
                      "weight_t": {
                        "description": "Gross combined weight in tonnes",
                        "type": "number"
                      },
                      "width_m": {
                        "maximum": 4.5,
                        "type": "number"
                      }
                    },
                    "required": [],
                    "type": "object"
                  },
                  "units": {
                    "default": "metric",
                    "enum": [
                      "metric",
                      "imperial"
                    ],
                    "type": "string"
                  },
                  "waypoints": {
                    "items": {
                      "properties": {
                        "lat": {
                          "type": "number"
                        },
                        "lng": {
                          "type": "number"
                        },
                        "stop_over": {
                          "type": "boolean"
                        }
                      },
                      "type": "object"
                    },
                    "type": "array"
                  }
                },
                "required": [
                  "origin",
                  "destination",
                  "truck"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "cached": false,
                  "customer_route_id": "my-route-42",
                  "expires_at": "2026-05-18T13:30:00Z",
                  "is_persisted": false,
                  "quota": {
                    "reset_at": "2026-06-01T00:00:00Z",
                    "subscription": {
                      "limit": 7500,
                      "plan": "pro_7500",
                      "tier": "pro",
                      "used": 1
                    },
                    "topups": [],
                    "total": {
                      "remaining": 7499
                    }
                  },
                  "route_id": "01HKZ9P5XJVQ6NB8E2RTH4FMW7",
                  "routes": [
                    {
                      "geometry": {
                        "coordinates": [
                          [
                            -74.006,
                            40.7128
                          ]
                        ],
                        "type": "LineString"
                      },
                      "summary": {
                        "distance_m": 350000,
                        "duration_s": 12600
                      },
                      "warnings": [
                        {
                          "description": "I-95 NB overpass at MP 23: 4.0m clearance; your truck is 4.2m.",
                          "distance_along_route_m": 12450,
                          "geometry": {
                            "coordinates": [
                              -74.006,
                              40.7128
                            ],
                            "type": "Point"
                          },
                          "projected_arrival_time": "2026-05-18T11:12:00Z",
                          "properties": {
                            "clearance_m": 4,
                            "truck_height_m": 4.2
                          },
                          "severity": "critical",
                          "source": "FHWA NBI",
                          "source_id": "feat_bridge_clearances_nbi_12345",
                          "title": "Bridge clearance below truck height",
                          "type": "bridge_clearance"
                        }
                      ]
                    }
                  ],
                  "tags": [
                    "fleet-east",
                    "lane-LA-NYC"
                  ]
                }
              }
            },
            "description": "Route computed with warnings enriched"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "conflicting_times": {
                    "value": {
                      "code": "conflicting_times",
                      "message": "departure_time and arrival_time are mutually exclusive"
                    }
                  },
                  "invalid_enrichment_severity": {
                    "value": {
                      "code": "invalid_enrichment_severity",
                      "message": "enrichment.min_severity must be info, warning, or critical (got \"loud\")"
                    }
                  },
                  "invalid_enrichment_type": {
                    "value": {
                      "code": "invalid_enrichment_type",
                      "message": "enrichment.exclude_types: unknown feature_type \"made_up\""
                    }
                  },
                  "out_of_range_enrichment": {
                    "value": {
                      "code": "out_of_range",
                      "message": "enrichment.buffer_m must be in [10, 1000]"
                    }
                  },
                  "truck_block_missing": {
                    "value": {
                      "code": "truck_block_required",
                      "message": "every routing request must include a truck block"
                    }
                  },
                  "unrouteable": {
                    "value": {
                      "code": "unrouteable",
                      "message": "no route found between the given coordinates"
                    }
                  }
                }
              }
            },
            "description": "Validation failure — see `code` for the specific reason"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "no_subscription",
                  "message": "routing is not enabled on this plan"
                }
              }
            },
            "description": "Plan does not include routing"
          },
          "429": {
            "content": {
              "application/json": {
                "examples": {
                  "daily_cap": {
                    "value": {
                      "code": "trial_daily_cap",
                      "message": "Free plan daily limit reached (5/day). Upgrade to remove the cap."
                    }
                  },
                  "quota_exhausted": {
                    "value": {
                      "code": "quota_exhausted",
                      "message": "Routing quota exhausted. Upgrade or buy a top-up pack to continue."
                    }
                  }
                }
              }
            },
            "description": "Quota or rate limit hit"
          },
          "502": {
            "content": {
              "application/json": {
                "example": {
                  "code": "routing_unavailable",
                  "message": "routing service unavailable"
                }
              }
            },
            "description": "Upstream routing provider unavailable"
          },
          "504": {
            "content": {
              "application/json": {
                "example": {
                  "code": "routing_timeout",
                  "message": "routing service timed out"
                }
              }
            },
            "description": "Upstream routing provider timed out"
          }
        },
        "summary": "Compute a truck route with hazard enrichment",
        "tags": [
          "routing"
        ]
      }
    },
    "/routing/truck/saved/recent": {
      "get": {
        "description": "Returns the slim row shape (`id`, `label`, `is_persisted`, `created_at`,\n`expires_at`) — fetch the full payload via the by-id refetch\nendpoint. Used by the portal's \"Recent routes (last 30 min)\" section.\n",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "routes": [
                    {
                      "created_at": "2026-05-18T10:00:00Z",
                      "expires_at": "2026-05-18T10:30:00Z",
                      "id": "01HKZ9P5XJVQ6NB8E2RTH4FMW7",
                      "is_persisted": false,
                      "label": ""
                    }
                  ]
                }
              }
            },
            "description": "List returned"
          }
        },
        "summary": "List the customer's recent auto-saved + persisted routes",
        "tags": [
          "routing"
        ]
      }
    },
    "/routing/truck/saved/{id}": {
      "delete": {
        "description": "Removes the row early. Works for both auto-saved and persisted rows.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Route not found (or owned by another customer)"
          }
        },
        "summary": "Delete a saved route",
        "tags": [
          "routing"
        ]
      },
      "get": {
        "description": "Returns the previously-computed route with `warnings[]` re-run against\ncurrent data. Does NOT count against routing quota, but is subject to\na separate refetch rate limit (1 / 30 s per route, 60 / minute per\ncustomer; both configurable). A 15-second refetch cache absorbs\nthe dispatcher's tweak-and-re-evaluate flow.\n\nAuto-saved routes (`is_persisted=false`) expire 30 minutes after the\noriginal routing call. After that, this endpoint returns `route_expired`.\n",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Route returned (warnings re-evaluated)"
          },
          "404": {
            "content": {
              "application/json": {
                "examples": {
                  "expired": {
                    "value": {
                      "code": "route_expired",
                      "message": "route auto-save window has expired"
                    }
                  },
                  "not_found": {
                    "value": {
                      "code": "route_not_found",
                      "message": "route not found"
                    }
                  }
                }
              }
            },
            "description": "Route not found or expired"
          },
          "429": {
            "content": {
              "application/json": {
                "example": {
                  "code": "refetch_rate_limit_exceeded",
                  "message": "refetch rate limit exceeded"
                }
              }
            },
            "description": "Refetch rate limit exceeded"
          }
        },
        "summary": "Refetch a saved route with re-evaluated warnings",
        "tags": [
          "routing"
        ]
      }
    },
    "/routing/truck/saved/{id}/persist": {
      "post": {
        "description": "Counts against the customer's per-plan persisted-route cap\n(`max_saved_routes`, or the per-customer override when set). Records\nthe Phase-2 monitoring config (label, buffer, validity window,\nnotification preferences) which the future matching engine will\nconsume on every ingested traffic_events / features row.\n",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "buffer_m": {
                    "default": 100,
                    "type": "integer"
                  },
                  "label": {
                    "type": "string"
                  },
                  "notify": {
                    "properties": {
                      "min_severity": {
                        "default": "warning",
                        "enum": [
                          "info",
                          "warning",
                          "critical"
                        ],
                        "type": "string"
                      },
                      "types": {
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "webhook_id": {
                        "type": "integer"
                      }
                    },
                    "type": "object"
                  },
                  "valid_from": {
                    "format": "date-time",
                    "type": "string"
                  },
                  "valid_to": {
                    "format": "date-time",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "description": "Persisted"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "saved_routes_quota_exhausted",
                  "message": "persisted-routes cap reached (5/5)"
                }
              }
            },
            "description": "Persisted-routes cap reached"
          },
          "404": {
            "description": "Route not found or already persisted"
          }
        },
        "summary": "Mark a saved route as persisted (keeps it past the 30-min TTL)",
        "tags": [
          "routing"
        ]
      }
    },
    "/stats": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "active_events": 4523,
                  "features_by_type": {
                    "bridge_clearances": 45200,
                    "cameras": 12450,
                    "ev_charging": 98500,
                    "rest_areas": 1580,
                    "road_conditions": 2100,
                    "signs": 4870,
                    "truck_restrictions": 8900,
                    "weather_stations": 3200
                  },
                  "jurisdictions": 47,
                  "total_features": 176800,
                  "uptime": "72h15m"
                }
              }
            },
            "description": "Event and feature counts"
          }
        },
        "summary": "Basic stats",
        "tags": [
          "metadata"
        ]
      }
    },
    "/stats/summary": {
      "get": {
        "description": "Jurisdictions breakdown, events by type/severity, feature counts, last sync times",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "events": {
                    "active": 4523,
                    "by_severity": {
                      "critical": 123,
                      "major": 780,
                      "minor": 1520,
                      "moderate": 2100
                    },
                    "by_type": {
                      "closure": 380,
                      "construction": 2150,
                      "incident": 1230,
                      "road_condition": 553,
                      "weather": 210
                    },
                    "total": 158420
                  },
                  "features": {
                    "by_type": {
                      "cameras": 12450,
                      "ev_charging": 98500,
                      "rest_areas": 1580,
                      "signs": 4870,
                      "weather_stations": 3200
                    },
                    "total": 176800
                  },
                  "jurisdictions": {
                    "countries": {
                      "CA": 12,
                      "US": 35
                    },
                    "list": [
                      {
                        "code": "CA",
                        "country": "US",
                        "name": "California"
                      },
                      {
                        "code": "ON",
                        "country": "CA",
                        "name": "Ontario"
                      }
                    ],
                    "total": 47
                  },
                  "last_sync": "2026-03-29T14:55:00Z",
                  "sources": {
                    "resources": 162,
                    "servers": 38
                  },
                  "uptime": "72h15m"
                }
              }
            },
            "description": "Summary stats object"
          }
        },
        "summary": "Rich summary stats",
        "tags": [
          "metadata"
        ]
      }
    },
    "/status": {
      "get": {
        "description": "Aggregate health snapshot suitable for an unauthenticated status page or external uptime monitor.\nReturns the worker heartbeat freshness, total/healthy/warning/critical resource counts, and a\nper-jurisdiction breakdown with 7-day uptime. 60-second cache. No API key required.\n",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "critical": 5,
                  "generated_at": "2026-05-08T14:00:00Z",
                  "healthy": 295,
                  "jurisdictions": [
                    {
                      "code": "ON",
                      "country": "CA",
                      "name": "Ontario",
                      "open_circuits": 0,
                      "status": "operational",
                      "total_resources": 8,
                      "uptime_7day_percent": 99.8
                    },
                    {
                      "code": "WA",
                      "country": "US",
                      "name": "Washington",
                      "open_circuits": 1,
                      "status": "degraded",
                      "total_resources": 6,
                      "uptime_7day_percent": 94.5
                    }
                  ],
                  "overall": "operational",
                  "total_resources": 312,
                  "warning": 12,
                  "worker": {
                    "healthy": true,
                    "last_seen": "2026-05-08T13:59:50Z",
                    "staleness_seconds": 10
                  }
                }
              }
            },
            "description": "Aggregate status snapshot"
          }
        },
        "security": [],
        "summary": "Public status snapshot",
        "tags": [
          "metadata"
        ]
      }
    },
    "/truck/corridor": {
      "get": {
        "description": "Returns all truck-related restrictions within a buffer around the straight-line path\nbetween two points. Uses PostGIS spatial intersection (ST_Buffer + ST_Intersects).\n\nSearches across feature types: `bridge_clearances`, `bridges`, `weight_restrictions`,\n`truck_restrictions`, `truck_routes`, `freight_corridors`, `truck_parking`.\n\nResults are ordered by distance from the corridor centerline. Each feature includes\n`_distance_km` in its properties.\n\nRequires Pro+ plan with truck data access.\n",
        "parameters": [
          {
            "description": "Origin latitude",
            "in": "query",
            "name": "from_lat",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Origin longitude",
            "in": "query",
            "name": "from_lng",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Destination latitude",
            "in": "query",
            "name": "to_lat",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Destination longitude",
            "in": "query",
            "name": "to_lng",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Buffer distance around corridor in km (default 5, max 50)",
            "in": "query",
            "name": "buffer_km",
            "schema": {
              "default": 5,
              "maximum": 50,
              "type": "number"
            }
          },
          {
            "description": "Vehicle height in meters — filters bridge clearances below this value",
            "in": "query",
            "name": "height",
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Vehicle weight in metric tons — filters weight limits below this value",
            "in": "query",
            "name": "weight",
            "schema": {
              "type": "number"
            }
          },
          {
            "description": "Filter by jurisdiction code (e.g., WA, NY, ON)",
            "in": "query",
            "name": "jurisdiction",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Maximum results (default 500, max 2000)",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 500,
              "maximum": 2000,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "corridor": {
                    "buffer_km": 5,
                    "distance_km": 58.3,
                    "from": [
                      43.65,
                      -79.38
                    ],
                    "to": [
                      43.25,
                      -79.87
                    ]
                  },
                  "data": [
                    {
                      "feature_type": "bridge_clearances",
                      "id": "on-br-qew-001",
                      "is_active": true,
                      "jurisdiction": "ON",
                      "latitude": 43.4521,
                      "longitude": -79.7032,
                      "name": "QEW Overpass at Trafalgar Rd",
                      "properties": {
                        "_distance_km": 1.2,
                        "clearance_m": 4.35,
                        "over_road": "Trafalgar Rd",
                        "road_name": "QEW"
                      },
                      "source": "on_arcgis"
                    },
                    {
                      "feature_type": "weight_restrictions",
                      "id": "on-wr-hwy6-003",
                      "is_active": true,
                      "jurisdiction": "ON",
                      "latitude": 43.3812,
                      "longitude": -79.7654,
                      "name": "Hwy 6 Spring Load Restriction",
                      "properties": {
                        "_distance_km": 3.8,
                        "max_weight_tonnes": 5,
                        "restriction_type": "spring_load"
                      },
                      "source": "on_arcgis"
                    }
                  ],
                  "limit": 500,
                  "total": 2
                },
                "schema": {
                  "properties": {
                    "corridor": {
                      "properties": {
                        "buffer_km": {
                          "type": "number"
                        },
                        "distance_km": {
                          "type": "number"
                        },
                        "from": {
                          "items": {
                            "type": "number"
                          },
                          "type": "array"
                        },
                        "to": {
                          "items": {
                            "type": "number"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "data": {
                      "items": {
                        "$ref": "#/components/schemas/Feature"
                      },
                      "type": "array"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Corridor restrictions with metadata"
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "from_lat, from_lng, to_lat, to_lng are required"
                }
              }
            },
            "description": "Missing required coordinates"
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "error": "API key required"
                }
              }
            },
            "description": "API key required"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "truck data access requires Pro plan or higher"
                }
              }
            },
            "description": "Plan does not include truck data access"
          }
        },
        "summary": "Query truck restrictions along a corridor",
        "tags": [
          "truck"
        ]
      }
    },
    "/truck/corridor/geojson": {
      "get": {
        "description": "Same query as `/truck/corridor` but returns results as a GeoJSON FeatureCollection\nfor direct map rendering. Requires Pro+ plan with truck data + GeoJSON access.\n",
        "parameters": [
          {
            "in": "query",
            "name": "from_lat",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "from_lng",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "to_lat",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "to_lng",
            "required": true,
            "schema": {
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "buffer_km",
            "schema": {
              "default": 5,
              "maximum": 50,
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "jurisdiction",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 500,
              "maximum": 2000,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/geo+json": {
                "example": {
                  "features": [
                    {
                      "geometry": {
                        "coordinates": [
                          -79.7032,
                          43.4521
                        ],
                        "type": "Point"
                      },
                      "properties": {
                        "_distance_km": 1.2,
                        "clearance_m": 4.35,
                        "feature_type": "bridge_clearances",
                        "id": "on-br-qew-001",
                        "name": "QEW Overpass at Trafalgar Rd"
                      },
                      "type": "Feature"
                    },
                    {
                      "geometry": {
                        "coordinates": [
                          [
                            -79.76,
                            43.38
                          ],
                          [
                            -79.77,
                            43.39
                          ]
                        ],
                        "type": "LineString"
                      },
                      "properties": {
                        "_distance_km": 3.8,
                        "feature_type": "weight_restrictions",
                        "id": "on-wr-hwy6-003",
                        "max_weight_tonnes": 5,
                        "name": "Hwy 6 Spring Load Restriction"
                      },
                      "type": "Feature"
                    }
                  ],
                  "type": "FeatureCollection"
                },
                "schema": {
                  "properties": {
                    "features": {
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "type": {
                      "enum": [
                        "FeatureCollection"
                      ],
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "GeoJSON FeatureCollection"
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "error": "truck data access requires Pro plan or higher"
                }
              }
            },
            "description": "Plan does not include truck data or GeoJSON access"
          }
        },
        "summary": "Query truck restrictions along a corridor (GeoJSON)",
        "tags": [
          "truck"
        ]
      }
    }
  },
  "security": [
    {
      "ApiKeyHeader": []
    },
    {
      "ApiKeyQuery": []
    }
  ],
  "servers": [
    {
      "description": "Production",
      "url": "https://api.road511.com/api/v1"
    },
    {
      "description": "Local development",
      "url": "http://localhost:8080/api/v1"
    }
  ],
  "tags": [
    {
      "description": "Traffic events (incidents, construction, closures, etc.)",
      "name": "events"
    },
    {
      "description": "Point-of-interest features (cameras, rest areas, signs, etc.)",
      "name": "features"
    },
    {
      "description": "Map-optimized endpoints (compact mode, same data)",
      "name": "map"
    },
    {
      "description": "Historical analytics and trends (Pro+ plans)",
      "name": "analytics"
    },
    {
      "description": "Truck corridor restriction queries (Pro+ plans with truck data access)",
      "name": "truck"
    },
    {
      "description": "Truck routing — compute a hazard-enriched route via HERE, refetch / persist\n/ delete saved routes. Counts against monthly routing quota (subscription\nbucket first, then top-ups FIFO).\n",
      "name": "routing"
    },
    {
      "description": "Jurisdictions, stats, health",
      "name": "metadata"
    },
    {
      "description": "Regional fuel prices — weekly US state-level (EIA), monthly Canadian province-level (StatCan)",
      "name": "fuel-prices"
    }
  ]
}