API reference
The public contract of the Grid's /api/v1 API. Base URL
https://data.sierragridteam.org; everything lives
under /api/v1/. Read-only, keyless, and CORS-open — no
registration or token. Responses are camelCase throughout
(observedAt, nextPageToken,
sourceStatus, severityRank) —
the proto RPCs via the gRPC-Gateway marshaler, the hand-built
.geojson layers via matching camelCase keys.
Timestamps are RFC 3339, errors are gRPC-standard
{code, codeName, message, details}, and most reads
carry an ETag (If-None-Match → 304). The proto RPCs
are described by the OpenAPI spec at
/api/openapi.json;
the hand-built .geojson map layers are not in it
(see §5), so read those from this page.
Each example below is a live link into the running
service: click any GET /api/v1/… to see the exact
JSON a client receives. A feed's sourceStatus is
OK, STALE, or
UNAVAILABLE; an unavailable feed is reported as such,
never as an all-clear (see §6).
- 1. Overview & conventions
- 2. Endpoint reference
- 3. The Event envelope
- 4. Places & the id scheme
- 5. The GeoJSON layer contract
- 6. The evacuation fail-loud contract
- 7. Source registry
1. Overview & conventions
Base URL and media types
/api/v1/observedAt, areaLabel, nextPageToken) — the gRPC RPCs via the gateway marshaler and the hand-built .geojson layers via matching camelCase keys (sourceStatus, severityRank).geojson path extension marks the RFC 7946 projection — a pure path, no headers needed, so map clients can consume the URL directlyConventions
- Timestamps are RFC 3339 strings (e.g.
2026-07-05T14:02:00Z). - Enum values render as their proto names (
WILDFIRE,ACTIVE,SEVERE). Query parameters accept them case-insensitively, andlayerparameters additionally accept the lowercase layer slugs used in.geojsonpaths (wildfire). - Repeatable filters: parameters documented as repeatable may appear multiple times —
?layer=wildfire&layer=evacuation. - Pagination is cursor-based on the event lists —
events,events/{id}/history, andhistory: pass the opaquenextPageTokenfrom a response as thepage_tokenquery parameter on the next request (base64 keyset cursors; treat them as opaque). The directory lists —places,sources, andscanners— are not paginated: each returns its complete set in one response by design (a bounded, slowly-changing directory), with nonextPageToken. Read them whole; don't build paging logic for them. - Query parameters stay snake_case regardless of response casing:
severity_min,page_token,page_size(camelCase forms are also accepted). - ETags: most read endpoints carry a weak
ETag; a conditional request with a matchingIf-None-Matchgets304 Not Modifiedand skips the work. Coverage:events/{id}+.../history, theeventsandhistorylists,places+places/{place}, and the.geojsonlayers (body-hash). Not yet instrumented:conditions,sources,places:resolve,scanners, andsummary. Independently, every proto RPC carriesCache-Control: public, max-age=30, so a proxy/CDN may serve any of them up to 30 s stale (the.geojsonlayers usemax-age=60). - Errors from the proto RPCs are gRPC-standard
{code, codeName, message, details}alongside the mapped HTTP status; the one hand-built endpoint (.geojson) emits the oldergoogle.rpc.Statusshape ({code, message, details}, nocodeName):
HTTP/1.1 404 Not Found
Content-Type: application/json
{"code": 5, "codeName": "NOT_FOUND", "message": "event not found"}
code is the numeric gRPC status code
(5 = NOT_FOUND, echoed as
codeName). Clients should key on the
HTTP status and surface message.
- Canonical client sort for events:
severitydescending, thenobservedAtdescending. - Places are addressable by slug or id:
ebbetts-passandarea:ebbetts-passboth work anywhere a{place}appears. Slugs are globally unique (§4).
CORS
All /api/v1 endpoints are served with open CORS —
any origin can fetch them from a browser. This site is itself an
unprivileged client of that same surface: every number on every page here
is a same-origin browser fetch you can replay with curl (see the footer of
any page).
2. Endpoint reference
Two idioms, one rule: entities (events, places, sources, conditions, scanners) are global collections filtered by query parameter; place-derived documents (summary, map layers) nest under the place path. The two hot paths — summary and map layers — take no query string at all, so CDN cache keys stay clean.
GET /api/v1/places/{place}/summary
One fetch returns a place's current state. The
GetPlaceSummary proto RPC (camelCase, like the
rest) with the area
mode, a cross-layer summary, per-domain
statuses, top events, and a source-health sidecar. Carries the evacuation
fail-loud invariant — activeEvacuations is
int | null, and null
means unknown, never zero (§6). Full response
shape quoted in §6.
| Field | Values | Semantics |
|---|---|---|
| mode | QUIET | WATCH | ACTIVE | ACTIVE: any active evacuation (ORDER / WARNING / SHELTER_IN_PLACE), any active EXTREME event, or a SEVERE wildfire. WATCH: any active SEVERE, an evacuation ADVISORY, fire weather elevated/red-flag, or any layer UNAVAILABLE while another signal is ≥ MODERATE. QUIET: otherwise. An UNAVAILABLE evacuation source forces mode ≥ WATCH — unknown is not quiet. |
| domains[] | fire, evacuation, weather, roads, seismic, comms | fire = wildfire + fire_weather; weather = weather_alert; roads = road_incident + chain_control + road_segment; seismic = earthquake; comms = mesh_node (present only when the MeshCore source is enabled). Each domain carries status (worst sourceStatus), highestSeverity, activeCount, and up to 3 headlines. activeCount and headlines count active items only: every event, but a condition feature (road segment, chain control, fire-weather banner) only when it is above INFO. Baseline monitoring — an open road, "normal" fire weather — is excluded, so a QUIET domain reads activeCount: 0 even while the map layer still draws those features. Mesh-node presence is ambient INFO state: it feeds the comms domain but is excluded from totalActive, severityCounts, topEvents, and mode. |
GET /api/v1/places/{place}/map/{layer}.geojson
One RFC 7946 FeatureCollection per layer,
ready for MapLibre / Leaflet / OpenLayers. Full contract in §5.
Cache-Control: max-age=60.
| Layer slug | Backing | Contents |
|---|---|---|
| wildfire | event store | CAL FIRE incidents + WFIGS perimeters (polygon when available, else point) |
| evacuation | event store | Cal OES evacuation zones — fail-loud, see §6 |
| weather_alert | event store | NWS watches / warnings / advisories for the place's zones |
| earthquake | event store | USGS quakes (point epicenters) |
| road_incident | event store | CHP / Caltrans incidents (points) |
| road_segment | live conditions | Monitored road corridors with congestion state (LineString) |
| chain_control | live conditions | Chain-control status per corridor (R1/R2/R3) |
| fire_weather | live conditions | Fire-weather state per NWS zone (normal / elevated / red-flag) |
| mesh_node | event store | MeshCore mesh-node presence (points) — repeaters, room servers, and located companion nodes heard in the region |
GET /api/v1/events
The cross-layer query: every discrete occurrence — wildfire, evacuation,
weather alert, earthquake, road incident — through one surface. Returns
EventList
({"events": [Event…], "nextPageToken": "…"}).
Results are keyset-paginated in canonical order (severity desc,
observedAt desc, id).
| Param | Type | Semantics | Default |
|---|---|---|---|
| place | place slug or id | Only events intersecting this place (precomputed at ingest — geometry is never touched at query time) | all places |
| layer | Layer enum, repeatable | Filter by layer; accepts WILDFIRE or the slug wildfire, case-insensitive | all layers |
| status | EventStatus enum, repeatable | Lifecycle filter; include RESOLVED/EXPIRED explicitly to see closed events | ACTIVE,SCHEDULED |
| severity_min | Severity enum | Inclusive floor on the unified scale (§3) | INFO (no floor) |
| since | RFC 3339 | Only events observed at or after this time | no time floor |
| page_size | int | Max events per page | server default |
| page_token | opaque string | Cursor from a previous response's nextPageToken | first page |
GET /api/v1/events/{id}
One Event (§3), current revision, as protojson.
Ids are source-namespaced (calfire:2026-salt-springs,
usgs:nc12345678) — take one from a
/api/v1/events response. The
event explorer links every row here.
GET /api/v1/events/{id}
GET /api/v1/events/{id}/history
The per-incident timeline: EventRevisionList —
every revision of the event, newest first, each as
{"revision": n, "observedAt": …, "ingestedAt": …,
"event": Event}. A revision is written only when content actually
changes (upstream re-stamps with identical content produce no revision),
and lifecycle transitions — including the all-clear to
RESOLVED/EXPIRED —
are themselves revisions, so the record ends with the ending.
GET /api/v1/history
Cross-event archive and replay over all revisions — the after-action
review query. Returns EventRevisionList.
| Param | Type | Semantics | Default |
|---|---|---|---|
| place | place slug or id | Revisions of events intersecting this place | all places |
| from | RFC 3339 | Start of the time range | open |
| to | RFC 3339 | End of the time range | open |
| layer | Layer enum, repeatable | Filter by layer | all layers |
| page_size | int | Max revisions per page | server default |
| page_token | opaque string | Cursor from a previous response's nextPageToken | first page |
GET /api/v1/places · /api/v1/places/{place}
The place directory (PlaceList /
Place): areas, counties, towns, corridors —
the id scheme is §4. Filters:
kind (a PlaceKind
name, e.g. COUNTY) and
q (text filter on place names).
GET /api/v1/places:resolve
Point or address → containing places (PlaceList).
Pass either lat + lng
(WGS84 decimal degrees; pure point-in-polygon, no external calls) or
address (one-line address, geocoded via the
US Census geocoder, then point-in-polygon). Results are ordered
most-specific-first: SITE,
EVAC_ZONE, TOWN,
CORRIDOR, COUNTY,
AREA.
GET /api/v1/conditions
Conditions, not events: the GetConditions
RPC returns current weather for the monitored locations plus the region's
fireWeather classification
(normal / elevated /
red-flag, derived only from authoritative NWS
products). Optional ?place= filters locations to a
place's bounding box. Per-location alerts are dropped —
weather alerts are events: query
/api/v1/events?layer=weather_alert.
There is no roads passthrough either: road conditions are the
road_segment and
chain_control
.geojson map layers, and
road incidents are events
(/api/v1/events?layer=road_incident).
GET /api/v1/scanners
Broadcastify scanner-feed configuration (ScannerList)
— link-out only, no audio rebroadcast. Filter with
?place=.
GET /api/v1/sources
The provenance and health registry behind every
sourceStatus in the system
(SourceList): per source, its poll interval,
last successful fetch, last attempt, last error text, and derived status
OK | STALE | UNAVAILABLE. Registry contents in
§7; live board on the
Sources page.
3. The Event envelope
Every discrete occurrence is one grid.v1.Event
message. The common envelope is identical across layers — a client can
render any event's card from
headline / severity / provenance / observedAt
without knowing its kind. Typed per-kind data lives in exactly one
detail block.
Fields
| Field | Type | Semantics |
|---|---|---|
| id | string | Globally unique, source-namespaced: {namespace}:{native_id}. Namespaces: chp: (road incidents), wx: (weather alerts), usgs: (earthquakes), calfire: (CAL FIRE incidents), wfigs: (fire perimeters), evac: (evacuation zones), meshcore: (mesh nodes, keyed by Ed25519 public key). WFIGS ids are wfigs:{normalized-name} with -2, -3 disambiguators. |
| layer | Layer | Layer taxonomy (table below) |
| category | string | Source sub-type slug ("active", "order", …) |
| severity | Severity | Unified 5-level scale (table below) |
| status | EventStatus | Lifecycle state (table below) |
| headline | string | Card-renderable one-liner, kind-agnostic |
| summary | string | AI-enhanced 2–3 sentences where applicable (see enhancement) |
| description | string | Long form / original upstream text, always preserved verbatim |
| areaLabel | string | Human area label ("Hathaway Pines & Avery") |
| canonicalUrl | string | Upstream detail URL; scheme-validated — only https:///http:// survive |
| geometry | Geometry | May be absent (county-wide advisories). See the base64 note below. |
| placeIds | repeated string | Place intersections, precomputed at ingest (§4 ids) |
| provenance | Provenance | sourceId, sourceName, attribution, sourceUrl, fetchedAt |
| effective | Timestamp | When the event takes effect (future for SCHEDULED) |
| expires | Timestamp | Upstream expiry, when the source provides one |
| observedAt | Timestamp | Upstream last-update time |
| ingestedAt | Timestamp | When this service ingested the current revision |
| revision | uint32 | Monotonic per event; a new revision is written only on content change |
| enhancement | Enhancement | AI-enhancement provenance: model, enhancedAt, fields (which Event fields were generated), and the model I/O — request (the prompt sent) and response (the raw structured JSON returned). Present only on enhanced events — clients badge AI-summarized text, show what was sent/returned, and can always fall back to the verbatim description. Persisted with the event (in every revision); excluded from the content hash. The heavy request/response are omitted unless ?enhancement_io=true is passed (event endpoints); the lightweight fields are always present. |
| detail | oneof | Exactly one typed block, keyed by kind (below) |
Detail blocks (the oneof)
Each block carries only kind-specific fields. Anything
already in the envelope is not repeated: the incident/alert type is
category, the human location is
areaLabel, the short line is
headline, the sending office is
provenance.sourceName, and the source page is
canonicalUrl.
| Key | Fields |
|---|---|
| wildfire | acres (double), containment (int32), county, cause (string), hasPerimeter (bool) |
| evacuation | zoneId, level (ORDER | WARNING | ADVISORY | SHELTER_IN_PLACE, as string), eventType, county (string) |
| weatherAlert | nwsSeverity, certainty, urgency, instruction, areaDesc (string), zones (repeated string) |
| earthquake | magnitude (double), depthKm (double), felt (int32) |
| roadIncident | logNumber (string), impact, duration (string), metadata (map<string,string>) |
| mesh | publicKey (hex string), nodeType (companion | repeater | room_server | sensor), name (string), telemetry { snr (double), rssi (int32), hopCount (uint32), path (repeated string — relay hops as repeater pubkey-prefix hashes), pathNodes (repeated string — path resolved to full node public keys, parallel to path), gateways (repeated string), lastAdvertAt (timestamp) } |
Unit-bearing fields name their unit (depthKm).
A fire_weather detail slot is reserved in the
schema but fire weather is condition-backed (a map layer, not an event
stream). The mesh block's
telemetry is the last-heard signal state and is
volatile — it never mints a new event revision (only a node's identity,
role, name, location, or status does). For "when did this node last
advertise", the trustworthy answer is the event's
observedAt (when the Grid last heard it);
telemetry.lastAdvertAt is the node's
self-reported stamp and is diagnostic only (mesh node clocks skew). Slots are also reserved for future
layers: power, gauge, air_quality, announcement.
Enum: Layer
| Name | # | Slug | Backing |
|---|---|---|---|
| WILDFIRE | 1 | wildfire | events |
| EVACUATION | 2 | evacuation | events |
| WEATHER_ALERT | 3 | weather_alert | events |
| FIRE_WEATHER | 4 | fire_weather | conditions (map layer only) |
| EARTHQUAKE | 5 | earthquake | events |
| ROAD_INCIDENT | 6 | road_incident | events |
| MESH | 13 | mesh_node | events (MeshCore presence) — ?layer= accepts mesh, mesh_node, or the legacy network |
| POWER, GAUGE, AIR_QUALITY, ANNOUNCEMENT | 10–14 | — | reserved for future pollers |
road_segment and
chain_control are condition-backed map-layer
slugs only — they are projections of live road state, not Layer enum
values, and never appear in /api/v1/events.
Enum: Severity (the canonical scale + color ramp)
One comparable scale across every source. It expresses response urgency to the public, not physical magnitude — an Evacuation Order (EXTREME) intentionally outranks an M5 earthquake (SEVERE). Rank equals the enum value. Always pair the color with the text label — color is never the only signal.
| Severity | Rank | Color | Badge |
|---|---|---|---|
| EXTREME | 4 | #e5544e | EXTREME |
| SEVERE | 3 | #e2673f | SEVERE |
| MODERATE | 2 | #d0a24a | MODERATE |
| MINOR | 1 | #6ba15a | MINOR |
| INFO | 0 | #5aa6e8 | INFO |
Hexes are the canonical dark-console ramp
(--sev-* in the design system); the swatch is
only ever a second signal beside the always-present text label.
Enum: EventStatus (lifecycle)
SCHEDULED ──▶ ACTIVE ──▶ RESOLVED (upstream confirmed over)
└──▶ EXPIRED (aged out; upstream went quiet)
| Status | # | Meaning |
|---|---|---|
| SCHEDULED | 1 | effective is in the future — e.g. an NWS watch issued ahead of its window, a planned PSPS |
| ACTIVE | 2 | In effect now |
| RESOLVED | 3 | Disappeared from an authoritative active-only feed (Cal OES, CAL FIRE, CHP, Caltrans) — upstream confirmed it over |
| EXPIRED | 4 | Missing from feed and past expires (or a per-source grace period) — sources that don't confirm endings (NWS, WFIGS) |
Every status transition writes a revision — the all-clear is part of
history. By default /api/v1/events returns only
ACTIVE,SCHEDULED; closed events remain
queryable by passing status= explicitly and
through /api/v1/history.
Geometry and the base64 note
Event.geometry is
{geojson: bytes, bbox: {minLat, minLng, maxLat,
maxLng}, centroid: {lat, lng}}. The
geojson field is a raw RFC 7946 geometry
object stored as proto bytes — protojson
renders bytes as base64, so decode before parsing:
const g = event.geometry; // may be absent entirely const geom = g?.geojson ? JSON.parse(atob(g.geojson)) : null; // geom is an RFC 7946 geometry object — coordinates are [lng, lat]
bbox and centroid
are always populated at ingest when geometry exists, so list views never
need to decode. For map rendering, prefer the
.geojson endpoints —
the base64 field exists for detail views.
4. Places & the id scheme
Place ids are {kind}:{slug}. Slugs are
globally unique, so every place is addressable by bare slug
(ebbetts-pass) or full id
(area:ebbetts-pass) anywhere a
{place} path segment or
place= parameter appears. Area slugs are short,
stable ids (e.g. ebbetts-pass).
| Prefix | Kind | # | What | Geometry | Example |
|---|---|---|---|---|---|
| area: | AREA | 1 | Configured service areas (the legacy hazard areas; org footprints live here too) | bbox polygon | area:ebbetts-pass |
| county: | COUNTY | 2 | Service-area counties (Calaveras, Tuolumne, Amador, Alpine, Stanislaus, San Joaquin, El Dorado, Mariposa) — simplified Census TIGERweb polygons | polygon | county:calaveras-county |
| town: | TOWN | 3 | The monitored towns (the weather locations) | point | town:arnold |
| evac_zone: | EVAC_ZONE | 4 | Evacuation zones (Zonehaven/Genasys scheme). Full zone-inventory import is pending; active zones appear as events in the meantime. | polygon | — |
| corridor: | CORRIDOR | 5 | The monitored road corridors | linestring | corridor:hwy4-arnold-bearvalley |
| site: | SITE | 6 | Specific sites (reserved) | point | — |
Each Place carries
id, kind, name, slug, geometry, parentId.
Parent links: towns point at their county; areas have no parent.
/api/v1/places:resolve returns containing places
ordered most-specific-first:
SITE, EVAC_ZONE, TOWN, CORRIDOR, COUNTY, AREA.
5. The GeoJSON layer contract
Each /api/v1/places/{place}/map/{layer}.geojson
response is one RFC 7946 FeatureCollection
with a foreign top-level metadata member
(allowed by RFC 7946, ignored by map libraries, available to clients that read it):
{
"type": "FeatureCollection",
"features": [ … ],
"metadata": {
"layer": "evacuation",
"area": "ebbetts-pass",
"generatedAt": "2026-07-05T15:42:11Z",
"sourceStatus": "OK", // OK | STALE | UNAVAILABLE
"lastSourceUpdate": "2026-07-05T15:38:00Z",
"attribution": "Cal OES / California County Governments — reference only",
"sourceUrl": "https://protect.genasys.com/…",
"schemaVersion": 1
}
}
metadata.sourceStatus
Layers are fail-loud: a source error never becomes a fabricated clear state. The three states render differently and must not be collapsed:
| sourceStatus | Features | Required client rendering |
|---|---|---|
| OK | current | Normal. |
| STALE | last-good served | Render with a "data ~N min old" indicator (N from generatedAt − lastSourceUpdate). |
| UNAVAILABLE | empty | Suppress the map layer; show an empty-state banner linking metadata.sourceUrl. Never render as "all clear". |
The properties envelope
Every feature, in every layer, shares one common envelope — that is the
unification. A client renders any feature's card from
headline / severity / source / updatedAt
without knowing its kind:
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-120.5402, 38.0674] },
"properties": {
// ---- common envelope (present on every feature) ----
"id": "calfire:2026-salt-springs", // globally unique, source-namespaced
"layer": "WILDFIRE", // Layer enum name (same value as event.layer)
"kind": "Wildfire", // human label
"category": "active", // source sub-type slug (free-form, lowercase)
"severity": "SEVERE", // unified scale, §3
"severityRank": 3, // 0..4, for sort/color
"headline": "Salt Springs Fire — 1,377 ac, 20% contained",
"description": "…", // optional, long
"status": "ACTIVE", // EventStatus enum name (lifecycle)
"effective": "2026-07-05T14:02:00Z", // RFC 3339, nullable
"expires": null,
"updatedAt": "2026-07-05T15:40:00Z",
"areaLabel": "Hathaway Pines & Avery", // optional
"source": {
"id": "calfire", "name": "CAL FIRE",
"url": "https://www.fire.ca.gov/incidents/…",
"attribution": "CAL FIRE / WFIGS",
"fetchedAt": "2026-07-05T15:42:10Z"
},
// ---- per-kind typed block (only the relevant one) ----
"wildfire": { "acres": 1377, "containment": 20, … }
}
}
- Per-kind data lives under a single namespaced key (
wildfire,earthquake,evacuation,weather,incident,mesh) — consumers ignore kinds they don't handle; no field collisions. - Coordinate order is
[longitude, latitude](GeoJSON axis order), WGS84, trimmed to 5 decimals (~1.1 m). Polygons are simplified to keep features small. - Null geometry is valid (e.g. a county-wide advisory): the feature has
"geometry": nullwith the same properties envelope. Exclude it from the map layer and render it as a full-width banner/list card sorted byseverityRankalongside located features, showingheadline,source.name,updatedAt, andsource.url. - URL fields are scheme-validated upstream: any upstream-sourced URL that is not
https:///http://is dropped before it reaches the response. Treat all upstream text (headlines, descriptions) as untrusted anyway. - Unit-bearing fields name their unit (
windGustMph,depthKm). layerhas two forms, by role. As a value —properties.layerhere, andevent.layer/topEvents[].layerelsewhere — it is the UPPERCASE Layer enum name (WILDFIRE). As an address — the{layer}path segment, the?layer=query value, and themetadata.layer/metadata.areaechoes above — it is the lowercase slug (wildfire). One rule: values are enum names, URLs and the metadata block are slugs. Mesh-node presence is the one layer whose slug (mesh_node) is not just the lowercased enum name (MESH): as a?layer=value the query acceptsmesh(the enum name),mesh_node(the slug), and the legacynetwork— all resolve to the same layer. (Before 2026-07-25 this layer's enum wasNETWORK; it was renamed toMESH, withnetworkkept as a legacy query alias.)- This endpoint is not in the OpenAPI spec. The
.geojsonlayers are hand-built (RFC 7946 geometry doesn't model cleanly in proto3), so a client generated from the spec alone will be missing all eight map layers — integrate them from this section.
6. The evacuation fail-loud contract
An error never becomes a 0.
Evacuation data is life-safety data, and it is reference only. This service surfaces the presence of orders from Cal OES and links out to the authoritative source; it never asserts "you are safe." Every consumer of this API inherits the following obligations — they are part of the contract, not styling advice.
The invariant, mechanically
In /api/v1/places/{place}/summary,
summary.activeEvacuations is
int | null and
summary.evacuationStatus is
OK | STALE | UNAVAILABLE:
| activeEvacuations | evacuationStatus | Meaning | Required client rendering |
|---|---|---|---|
| null | UNAVAILABLE | The Cal OES feed errored. Unknown — not zero. | "Evacuation status unknown — check Genasys Protect" (or the sourceUrl provided). Never an all-clear, never a blank. |
| 0 | OK | Cal OES fetched cleanly and reports no active zones — a caveated confirmed-empty. | "No active evacuations reported (per Cal OES)" — still linked to the source, never phrased as a guarantee. |
| n > 0 | OK / STALE | Active zones exist (STALE = counted from the last good fetch). | Show the count and the zones; if STALE, show data age. |
The distinction between the first two rows is the entire point:
null and 0 are
different values with different meanings, and the API will never
collapse one into the other. Clients must not either. An UNAVAILABLE
evacuation source also forces the area
mode to at least
WATCH — unknown is not quiet.
The same rule applies to the evacuation map layer
(/api/v1/places/ebbetts-pass/map/evacuation.geojson):
sourceStatus: UNAVAILABLE comes with empty
features and must render as an unknown-state banner, never an empty
(implicitly safe) map. metadata.sourceUrl
links the authoritative Genasys viewer in every state.
Finally: directive life-safety text is never rewritten.
Evacuation orders and instructions appear verbatim in
description — AI enhancement is prohibited
for this layer. Render orders verbatim, with the link out.
The summary shape (quoted from the contract)
{
"place": "ebbetts-pass", "placeId": "area:ebbetts-pass",
"placeName": "Calaveras County", "generatedAt": "RFC3339",
"mode": "QUIET|WATCH|ACTIVE",
"summary": {
"highestSeverity": "SEVERE", "highestSeverityRank": 3,
"severityCounts": {"SEVERE": 1}, "totalActive": 4,
"activeEvacuations": null, "evacuationStatus": "UNAVAILABLE",
"topEvents": [{"id","layer","severity","severityRank","headline","source"}]
},
"domains": [{"domain","status","highestSeverity","activeCount",
"headlines":[{"id","severity","headline"}]}],
"sources": [{"id","status","lastSuccessAt"}]
}
7. Source registry
Every event and every layer traces to one of these sources — the registry
behind /api/v1/sources (live board:
Sources). Each
Source carries
id, name, attribution, homepageUrl,
pollIntervalSeconds, staleAfterSeconds, expireAfterSeconds,
lastSuccessAt, lastAttemptAt, lastError, status.
A source is STALE once its last success is
older than staleAfterSeconds (default 3×
its poll interval) and UNAVAILABLE on
persistent failure.
| id | Provides | Event ids | Poll | Disappearance policy |
|---|---|---|---|---|
| nws | National Weather Service zone alerts (api.weather.gov) — weather_alert events; also the authority behind fire_weather conditions | wx: | 5m | expire — missing and past expires ⇒ EXPIRED |
| usgs | USGS earthquake feed for the service-area bounds | usgs: | 5m | — (see notes) |
| calfire | CAL FIRE active incidents (acres, containment) | calfire: | 5m | resolve — active-only feed; missing ⇒ RESOLVED |
| wfigs | WFIGS fire perimeters (joined to CAL FIRE incidents by name; standalone perimeters get wfigs: ids) | wfigs: | 5m | expire |
| caloes | Cal OES California Evacuation Aggregation Layer (Zonehaven/Genasys zone schema) — reference only, see §6 | evac: | 2m | resolve |
| chp | CHP incidents (AI-enhanced road_incident events) | chp: | 5m | resolve |
| caltrans | Caltrans lane closures (road_incident events); also attributed for chain-control conditions | chp: | 5m | resolve |
A poller may update multiple source rows (the wildfire tick updates
calfire + wfigs;
the incidents tick updates chp +
caltrans) — poller ≠ source. Poll intervals
and staleness thresholds are server configuration; read the live values
from /api/v1/sources rather than
hard-coding this table.
Attribution requirements
- Display the
attributionstring (fromSource.attribution,Event.provenance.attribution, ormetadata.attributionon GeoJSON layers) wherever you render the data. - Link the upstream:
provenance.sourceUrl(events) /metadata.sourceUrl(GeoJSON) on records,homepageUrlon the source. - Evacuation data must carry its reference-only framing and the Genasys link in every state (§6).