# MountainSwitch > Structured environmental intelligence API for California mountain > destinations. Built for agents that need trustworthy destination access, route > status, weather context, source freshness, and current restrictions in one > place. MountainSwitch normalizes messy official source data into consistent, attributed API responses for humans and AI agents. Current live surfaces cover road/access data and National Weather Service trip-planning enrichment. Use it to answer questions like "Can I get there?", "Which route is affected?", "What weather context should I consider?", and "How confident is this data right now?" ## Using This API ### Which endpoint to use - **Discover destinations** (for example, "What places can I plan for right now?"): Use `GET /v1/destinations` first to discover the live supported destination set before requesting destination access or route-specific drilldown. - **Check one destination** (for example, "Is any way into Big Bear confidently open?"): Use `GET /v1/destinations/{destination}/access` after discovery to answer whether a specific destination is confidently reachable. MountainSwitch currently supports California (`US-CA`) only in v1, so omit `jurisdiction` or pass `US-CA`. - **Check one destination's weather** (for example, "What should I expect in Big Bear this weekend?"): Use `GET /v1/destinations/{destination}/weather` for National Weather Service forecast and active-alert enrichment. Weather does not change road status, destination access, or chain-control severity. - **Inspect one route** (for example, "What is happening on SR-18 specifically?"): Use `GET /v1/routes/{route}/summary` when you already know the road you need to inspect. Start with destination access instead if the question is "Can I get there?" - **Filter detailed conditions** (for example, "Show all closed roads in California"): Use `GET /v1/conditions?jurisdiction=US-CA&overall_status={status}`. This returns the individual condition records you need for filtering, troubleshooting, or segment-level review. - **Single segment lookup**: Use `GET /v1/conditions/{id}` with the segment ID. - **System health**: Use `GET /v1/health` to check if upstream data sources are operational. ### Authentication Include an API key via the `Authorization` header: ``` Authorization: Bearer {token} ``` Anonymous access is allowed on all read endpoints at 60 requests/hour per IP. Authenticated access allows 1,000 requests/hour per key. ### Base URL ``` https://api.mountainswitch.co ``` > **Path convention:** All endpoint paths include the version prefix (e.g., `/v1/conditions`). The base URL does NOT include `/v1` — concatenating base + path yields `https://api.mountainswitch.co/v1/conditions`. ### Freshness — always check this Data endpoints (`GET /v1/conditions`, `GET /v1/conditions/{id}`, `GET /v1/destinations/{destination}/access`, `GET /v1/destinations/{destination}/weather`, and `GET /v1/routes/{route}/summary`) include a `_meta.sources[]` array. - Data endpoints use consumer data usability. For road-condition data endpoints, `freshness: "stale"` means the source is older than 30 minutes but may still be usable when `live_eligible: true`; weather uses the source-family windows below. `freshness: "down"` means the source is unusable for this response. - Weather endpoint freshness uses weather-specific source windows, not road-condition windows: forecasts are current through 2 hours, stale-usable through 12 hours, and down after 12 hours; active alerts are current through 15 minutes, stale-usable through 60 minutes, and down after 60 minutes. A stale-but-usable weather source may remain `live_eligible: true` only for transient fetch/upstream failures, or forecast semantic validation failures, while the last successful snapshot is inside the weather freshness window. Alert semantic validation failures and D1 write failures make `live_eligible: false` immediately. - `live_eligible` is `true` when this source has consumer-usable data for live results in the requested scope. Stale-but-usable sources (`freshness: "stale"`) may still be `live_eligible: true` through their endpoint-specific consumer window. Road-condition sources use the 60-minute consumer window; weather sources use the source-family windows defined below. `false` means the source has no usable live data for this request because it is consumer-down, bootstrap, or blocked by a semantic integrity failure. For road-condition sources, a latest `D1_WRITE_FAILURE` is an internal transient refresh failure and does not by itself make the prior trusted snapshot consumer-unusable while that snapshot remains inside the consumer window. There are two `_meta` exceptions to keep in mind: - Health endpoints (`GET /v1/health` and `GET /v1/health/{adapterId}`) omit `sources`, `query_coverage`, and `pagination` because the health endpoint is the strict operator source-of-truth for adapter status. Do not use health status to override data endpoint usability. - `GET /v1/destinations` omits `sources` and `query_coverage` because it returns destination-registry metadata rather than live source-derived condition truth. It still returns `_meta.pagination = null`. - `live_aggregation_eligible` is the strict processor publish-safety bit from adapter health after strict read-time aging on health endpoints. It is operator-facing and is intentionally not the same as `_meta.sources[].live_eligible` on data endpoints, which represents consumer data usability. Clients must not use `/v1/health` to override data endpoint `_meta.sources[].live_eligible`, `_meta.sources[].freshness`, or `query_coverage`. - Health adapter objects use `last_successful_fetch` as the operator/agent source-check freshness anchor. The nested `processor` object (`last_published_snapshot_at`, `last_job_completed_at`, `last_job_outcome`, etc.) is coalesced troubleshooting telemetry and may lag during repeated successful no-op cycles. Do not use `processor` fields to override data endpoint `live_eligible`, `freshness`, or `query_coverage`. If data endpoint `freshness` is `stale` and `live_eligible` is `true`, present the result as last-known usable data and include the age, for example "Last updated 34 min ago." If `freshness` is `down` or `live_eligible` is `false`, do not treat rows as proof of current conditions. Do not treat `last_successful_fetch`, health `status`, or timestamps alone as proof that live list/summary data is usable; check data endpoint `live_eligible`. Also check `_meta.query_coverage`: - `full` — covering sources exist, and every covering source is currently consumer `live_eligible: true`. A full response may include stale-but-usable sources. - `partial` — at least one covering source is currently `live_eligible: true`, but not every covering source qualifies for `full`; results may be incomplete. - `none` — no data available. Either no sources exist for this query, or no covering source is currently `live_eligible: true`. Important: an empty `data` array with `query_coverage: "full"` means **no active non-open conditions were reported** by every relevant consumer-live-eligible covering source. If any source freshness is `stale`, include the last-updated age instead of implying a brand-new check. An empty `data` array with `query_coverage: "none"` means **no data available** because no relevant sources exist or no covering source is currently consumer-live-eligible. If data is empty and `query_coverage` is `"partial"`, say that no restrictions were reported by available sources but no active restrictions can be verified for the route. ### Chain control severity scale The `chain_control_severity` field is an integer from 0 to 4: | Severity | Code | Meaning | |---|---|---| | 0 | `none` | No chain or traction requirements reported | | 1 | `traction_advisory` | Traction devices recommended but not required | | 2 | `traction_required` | Snow tires or traction devices required | | 3 | `chains_awd_exempt` | Chains required, AWD with snow tires exempt | | 4 | `chains_required` | Chains required on all vehicles, no exceptions | > Note: severity 4 covers both maximum non-closure restrictions (`overall_status: "restricted"`) and true closures (`overall_status: "closed"`). Always check `overall_status` rather than inferring closure from severity alone. The corresponding `chain_control_code` field is the string enum: `none`, `traction_advisory`, `traction_required`, `chains_awd_exempt`, `chains_required`. The `chain_control_raw` field preserves the original source value (e.g., "R-2", "Traction Law") for display purposes. ### Common route queries Current live California destinations and their route sets: - **Big Bear Lake**: `SR-18`, `SR-38`, `SR-138`, `SR-330` - **Lake Arrowhead Rim**: `SR-18`, `SR-189`, `SR-330` - **Idyllwild**: `SR-74`, `SR-243` - **Mammoth Lakes**: `US-395`, `SR-203` - **June Lake / Mono Basin**: `US-395`, `SR-158` - **Oakhurst / Bass Lake**: `SR-41` - **Shaver Lake**: `SR-168` - **Kern River Valley**: `SR-178`, `SR-155` - **Dodge Ridge**: `SR-108` - **Bear Valley Mountain Resort**: `SR-4` Example — check all routes to Big Bear: ``` GET /v1/routes/SR-18/summary?jurisdiction=US-CA GET /v1/routes/SR-38/summary?jurisdiction=US-CA GET /v1/routes/SR-138/summary?jurisdiction=US-CA GET /v1/routes/SR-330/summary?jurisdiction=US-CA ``` Route-summary requests and route-scoped `GET /v1/conditions` default an omitted `jurisdiction` to `US-CA`. If `jurisdiction` is provided, it must be `US-CA`. Cross-jurisdiction route aggregation is not supported in v1. Or query multiple routes at once: ``` GET /v1/conditions?jurisdiction=US-CA&route=SR-18,SR-38,SR-138,SR-330 ``` ### Timestamps All timestamps are ISO 8601 format in UTC with a `Z` suffix (e.g., `2026-03-19T12:47:30Z`). ### Pagination List endpoints are always paginated. Use `cursor` and `limit` query parameters: - `limit`: Number of results per page. Default: 50. Max: 200. - `cursor`: Opaque cursor string from the previous response's `_meta.pagination.cursor`. Check `_meta.pagination.has_more` to determine if more pages exist. The response pagination object uses `cursor` as the field name (e.g., `_meta.pagination.cursor`). Pass this value as the `cursor` query parameter to fetch the next page. `GET /v1/conditions` pagination follows the stable list ordering: `overall_status` priority (`closed` > `restricted` > `advisory` > `open`), then `chain_control_severity` descending, then `id` ascending. The cursor is opaque, but the mirrored payload contract is `CursorPayload = { id: string, o: number, s: number }`, where `o` is `overall_status` rank (`closed=3`, `restricted=2`, `advisory=1`, `open=0`) and `s` is `chain_control_severity`. ### Error handling If the API returns an error, check the `error.code` field. ### Error Codes | HTTP Status | Code | When | |---|---|---| | 400 | `INVALID_PARAMETER` | Malformed query parameter | | 400 | `INVALID_JURISDICTION` | Unknown jurisdiction code | | 401 | `UNAUTHORIZED` | Bearer token present but invalid or expired | | 404 | `ROUTE_NOT_FOUND` | Canonical route value is unknown or unsupported for the requested jurisdiction | | 404 | `DESTINATION_NOT_FOUND` | Canonical destination slug is unknown or unsupported for the requested jurisdiction | | 404 | `CONDITION_NOT_FOUND` | Segment ID does not exist | | 404 | `ADAPTER_NOT_FOUND` | No adapter with this ID exists | | 429 | `RATE_LIMIT_EXCEEDED` | Too many requests | | 500 | `INTERNAL_ERROR` | Unexpected server error | | 503 | `SERVICE_UNAVAILABLE` | D1 or critical infrastructure is unavailable | Common handling guidance: - `ROUTE_NOT_FOUND` (HTTP 404) — the route value is unknown or unsupported. This is NOT the same as "no active conditions." Check `error.details.invalid_routes` to see which requested routes failed validation, and `error.details.known_routes` for valid route names. Retry with one of the known routes. Unknown routes always return 404; they never return 200 with empty data. - `DESTINATION_NOT_FOUND` (HTTP 404) — the canonical destination slug is unknown or unsupported. Use `GET /v1/destinations` to discover supported destination slugs before calling destination access. - **Known route with no data** — if the API returns HTTP 200 with an empty `data` array, the route IS known but has no active conditions. Check `_meta.query_coverage` to distinguish: `"full"` means no active restrictions are currently reported, `"none"` means data sources are unavailable (unknown conditions). Never confuse this with `ROUTE_NOT_FOUND`. - **Source unavailability** is NOT an HTTP error. If a known route's sources are all down, the API returns HTTP 200 with `data: []` and `query_coverage: "none"`. Check `GET /v1/health` for adapter status. 503 is reserved for infrastructure-level failures (e.g., D1 database unavailable), not source outages. - `RATE_LIMIT_EXCEEDED` — slow down requests. Check `X-RateLimit-Remaining` and `X-RateLimit-Reset` response headers. Rate limiting uses a fixed hourly window, and `X-RateLimit-Reset` is always the Unix timestamp for the top of the next hour. - Health endpoints are outside the public fixed-window quota and may omit `X-RateLimit-*` headers so status checks do not consume caller quota. - `INVALID_PARAMETER` — a query parameter is malformed. Route-scoped requests may omit `jurisdiction` and default to `US-CA`; providing a non-`US-CA` jurisdiction for a supported California route returns `400 INVALID_PARAMETER`. - `INVALID_JURISDICTION` — unknown jurisdiction code. Use ISO 3166-2 format (e.g., `US-CA`). - `CONDITION_NOT_FOUND` — the segment ID does not exist. Returned by `GET /v1/conditions/{id}`. - `UNAUTHORIZED` — Bearer token was present but invalid or expired. - `ADAPTER_NOT_FOUND` — requested health adapter ID does not exist. - `INTERNAL_ERROR` — unexpected server failure. - `SERVICE_UNAVAILABLE` — critical infrastructure such as D1 is unavailable. Errors always include `_meta.request_id` for debugging. ### Multi-value filtering Comma-separate values for multi-value parameters: ``` GET /v1/conditions?jurisdiction=US-CA&route=SR-18,SR-38&overall_status=advisory,restricted,closed ``` Do not repeat parameter names. `?route=SR-18&route=SR-38` returns `400 INVALID_PARAMETER`; use comma separation instead. Boolean parameters accept only `true` or `false` (case-insensitive). Values like `1`, `0`, `yes`, or `no` are invalid. ### Overall status values The `overall_status` field is one of: `open`, `advisory`, `restricted`, `closed`. - `open` = no active restrictions are currently reported for that record. - `advisory` = informational warning (winter driving advisory, traction recommended). Not a restriction, but not all-clear either. - `restricted` = chains or traction devices required. - `closed` = road is closed. **Default behavior:** Live responses exclude rows where `overall_status === "open"`. Advisory, restricted, and closed conditions are returned by default. This includes lane closures that may have `chain_control_severity = 0` but still have `overall_status: "restricted"` or `"closed"`. To include live open/no-active-restrictions rows, pass `?include_open=true`. When `is_active=false`, inactive rows are returned regardless of current source health and `include_open` is ignored. ## API Reference - `GET /` — Minimal API index for direct visits to the API origin. Use the versioned endpoints below for data access. - `GET /v1/conditions` — List active road conditions. Filter by jurisdiction, route, status, severity. - `GET /v1/conditions/:id` — Get a single road condition segment by ID. - `GET /v1/destinations` — Lists the currently supported destinations. It returns a non-paginated metadata envelope with `destination`, `label`, `jurisdiction`, `jurisdiction_slug`, `routes`, and `communities`. `_meta.sources` and `_meta.query_coverage` are omitted, `_meta.pagination` is `null`, and the endpoint is `no-store`. - `GET /v1/destinations/:destination/access` — Returns the destination access answer in a non-paginated response envelope with destination, path, and segment coverage fields. - `GET /v1/destinations/:destination/weather` — Returns destination-scoped National Weather Service forecast and active-alert enrichment in a non-paginated response envelope. Weather `_meta.query_coverage` describes only weather source availability and does not alter road status or destination access. - `GET /v1/routes/:route/summary` — Returns the route summary after destination discovery or when you already know which route you need to inspect. Includes human-readable summary text. `active_restrictions` counts all non-open conditions, including advisory-only segments (`overall_status: "advisory"`). To determine whether traction or chain requirements exist, check `worst_chain_control_severity`: severity `>= 2` means equipment is required. - `GET /v1/health` — All strict adapter health statuses. Each health object includes `live_aggregation_eligible`, the strict processor publish-safety bit, plus nested `processor` troubleshooting fields. Clients must not use health status or processor fields to override data endpoint `_meta.sources[].live_eligible`, `_meta.sources[].freshness`, or `query_coverage`. - `GET /v1/health/:adapterId` — Get a single adapter's health status. Returns `{ data: , _meta: { request_id, response_generated_at } }`, where `data` has the same shape as one entry from the adapters array in `GET /v1/health`, including `live_aggregation_eligible` and `processor`. - `GET /llms.txt` — Compact agent-oriented API guide. - `GET /openapi.json` — Full OpenAPI 3.0 specification. ## Mirrored Query Parameter Rules | Endpoint | Parameter | Rule | |---|---|---| | `GET /v1/conditions` | `jurisdiction` | ISO 3166-2 code. MountainSwitch currently supports California only in v1, so omit this field or use `US-CA`. Comma-separated for multiple. When `route` is supplied and `jurisdiction` is omitted, default the effective jurisdiction to `US-CA`. | | `GET /v1/conditions` | `route` | Route name. Current live routes are `SR-18`, `SR-38`, `SR-138`, `SR-173`, `SR-189`, `SR-330`, `SR-74`, `SR-243`, `US-395`, `SR-203`, `SR-158`, `SR-41`, `SR-168`, `SR-178`, `SR-155`, `SR-108`, and `SR-4`. Comma-separated for multiple. | | `GET /v1/conditions` | `overall_status` | `open`, `advisory`, `restricted`, `closed`. Comma-separated for multiple. | | `GET /v1/conditions` | `chain_control_severity_gte` | Minimum chain control severity (0-4). Returns conditions >= this level. See Chain Control Severity Scale below. | | `GET /v1/conditions` | `include_open` | Default: `false`. Set `true` to include open/no-active-restrictions records in the response. Without this flag, only active non-open records are returned (`overall_status != 'open'`). If the caller explicitly includes `open` in the `overall_status` filter, treat `include_open` as effectively `true` for that request even when the query param is omitted. | | `GET /v1/conditions` | `is_active` | Default: `true`. Set `false` to return ONLY inactive records (inactive only, not all records). Inactive rows may include future scheduled lane closures that have not started yet and off-season chain-control checkpoints from the latest published adapter snapshot. v1 intentionally has no single-call "return active + inactive together" mode; clients that need both must make two calls and merge client-side. When `is_active=false`, current-health filtering does NOT suppress rows; inactive records remain queryable regardless of the source adapter's current health. | | `GET /v1/conditions` | `cursor` | Pagination cursor from previous response. Cursors are `base64(JSON.stringify(CursorPayload))` where `CursorPayload = { id: string, o: number, s: number }`. Invalid cursors return 400 `INVALID_PARAMETER`. | | `GET /v1/conditions` | `limit` | Default: 50. Max: 200. | | `GET /v1/destinations` | `jurisdiction` | Optional jurisdiction code filter. If omitted, return all live supported destinations. In v1, omit it or use `US-CA`. Any other provided known jurisdiction value after normalization returns 400 `INVALID_PARAMETER`. Repeated or unrecognized query parameters return 400 `INVALID_PARAMETER`. | | `GET /v1/destinations/:destination/weather` | `jurisdiction` | Defaults to `US-CA` in v1. A value that cannot be canonicalized to a known jurisdiction returns `400 INVALID_JURISDICTION`; a known but unsupported non-`US-CA` jurisdiction returns `400 INVALID_PARAMETER` unless a later jurisdiction canon expands weather support. Repeated or unknown query parameters return `400 INVALID_PARAMETER`. | | `GET /v1/routes/:route/summary` | `jurisdiction` | Optional in v1. If omitted, default to `US-CA`. If provided, it must resolve to `US-CA`. | | `GET /v1/routes/:route/summary` | `destination` | Optional canonical destination slug for additive route-row scoping. When provided, it must be a supported destination for the requested route. Unknown slugs return `404 DESTINATION_NOT_FOUND`; known but invalid-for-route slugs return `400 INVALID_PARAMETER`. | ## Coverage Current live coverage: - **Jurisdiction:** `US-CA` - **Destinations:** `big-bear`, `lake-arrowhead-rim`, `idyllwild`, `mammoth-lakes`, `june-lake-mono-basin`, `oakhurst-bass-lake`, `shaver-lake`, `kern-river-valley`, `dodge-ridge`, `bear-valley-mountain-resort` - **Routes:** `SR-18`, `SR-38`, `SR-138`, `SR-173`, `SR-189`, `SR-330`, `SR-74`, `SR-243`, `US-395`, `SR-203`, `SR-158`, `SR-41`, `SR-168`, `SR-178`, `SR-155`, `SR-108`, `SR-4` If a route or jurisdiction is not listed here, treat it as unsupported. ## Optional - [OpenAPI spec](https://api.mountainswitch.co/openapi.json): Full machine-readable API specification with request/response schemas. - [Health endpoint](https://api.mountainswitch.co/v1/health): Check individual adapter status and upstream feed availability.