{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/dataquoll-io/json-schema/dataquoll-io-incident-collection-schema.json",
  "title": "IncidentCollection",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/dataquoll-io-openapi.json#/components/schemas/IncidentCollection",
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "const": "FeatureCollection"
    },
    "features": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/IncidentFeature"
      }
    },
    "meta": {
      "type": "object",
      "properties": {
        "total_count": {
          "type": "integer"
        },
        "has_more": {
          "type": "boolean"
        },
        "next_cursor": {
          "type": "string"
        },
        "size_limited": {
          "type": "boolean",
          "description": "/api/v1/incidents/snapshot. True when the answer ended at the ~4 MB size cap rather than at limit, so fewer records than asked for came back; truncated is then true too (narrow by state or event_type)."
        },
        "truncated": {
          "type": "boolean",
          "description": "/api/v1/incidents/snapshot. True when more records were active at that moment than came back: the answer stopped at `limit` or at the size cap (`size_limited`). Raise limit (500 at most) or narrow by state or event_type."
        },
        "snapshot_datetime": {
          "type": "string",
          "format": "date-time",
          "description": "/api/v1/incidents/snapshot. The moment reconstructed, in UTC."
        },
        "source": {
          "type": "string",
          "enum": [
            "archive",
            "live"
          ],
          "description": "/api/v1/incidents/snapshot. archive: rebuilt from the historical archive (before 2026-04-08); live: from incident history and currently active incidents."
        },
        "generated_at": {
          "type": "string",
          "format": "date-time",
          "description": "/api/v1/incidents/snapshot. When this answer was built."
        },
        "generatedAt": {
          "type": "string",
          "format": "date-time"
        }
      }
    },
    "links": {
      "type": "object",
      "properties": {
        "self": {
          "type": "string"
        },
        "next": {
          "type": "string"
        }
      }
    },
    "data_freshness": {
      "type": "array",
      "description": "How current each state's data is, one entry per state. Present on the GeoJSON and JSON responses; the csv, cap-au and cap-atom formats return before it is built.",
      "items": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string"
          },
          "lastPolled": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The OLDEST successful poll across that state's feeds, so it bounds how current the state's data can be. It is deliberately not the most recent: taking the newest let one fast feed hide slower siblings and made a down feed invisible behind a healthy one. Null when any feed in the state has never succeeded."
          },
          "updateInterval": {
            "type": "string",
            "description": "Shortest polling interval among that state's feeds, or `unknown`."
          },
          "feedCount": {
            "type": "integer",
            "description": "How many feeds this state has."
          },
          "feedsHealthy": {
            "type": "integer",
            "description": "How many of them are currently healthy. Below feedCount means the state is degraded and some sources are not reporting."
          }
        }
      }
    },
    "attribution": {
      "type": "string"
    }
  },
  "$defs": {
    "IncidentFeature": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "const": "Feature"
        },
        "id": {
          "type": "string"
        },
        "geometry": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "enum": [
                "Point",
                "Polygon",
                "MultiPolygon"
              ]
            },
            "coordinates": {
              "oneOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "number"
                  },
                  "description": "[longitude, latitude] for Point"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    }
                  },
                  "description": "Ring arrays for Polygon"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "array",
                        "items": {
                          "type": "number"
                        }
                      }
                    }
                  },
                  "description": "Polygon arrays for MultiPolygon (an incident whose area has several parts)"
                }
              ]
            }
          }
        },
        "properties": {
          "type": "object",
          "properties": {
            "source": {
              "type": "object",
              "properties": {
                "state": {
                  "type": "string"
                },
                "agency": {
                  "type": "string"
                },
                "feedId": {
                  "type": "string"
                }
              }
            },
            "title": {
              "type": "string"
            },
            "eventType": {
              "type": "string",
              "enum": [
                "bushfire",
                "burn_off",
                "fire_ban",
                "structure_fire",
                "vehicle_fire",
                "grass_fire",
                "other_fire",
                "hazmat",
                "rescue",
                "flood",
                "storm",
                "tree_down",
                "cyclone",
                "earthquake",
                "extreme_heat",
                "vehicle_accident",
                "medical",
                "alarm",
                "other"
              ]
            },
            "eventCategory": {
              "type": "string",
              "enum": [
                "fire",
                "weather",
                "rescue",
                "hazmat",
                "medical",
                "alarm",
                "planned",
                "earth",
                "hazard",
                "other"
              ],
              "description": "Groups related event types: the value the category filter matches, and always the /api/v1/schema categoryMapping of eventType. Documented since v1.12.3 but carried on features only from v1.97.0."
            },
            "featureType": {
              "type": "string",
              "enum": [
                "incident",
                "incident_area",
                "warning_area",
                "fire_ban_area",
                "road_disruption",
                "forecast"
              ],
              "description": "Point incident, boundary polygon, road segment or forecast. Defaults to 'incident'; list endpoints return only incidents unless ?featureType= is supplied."
            },
            "status": {
              "type": "string",
              "enum": [
                "active",
                "contained",
                "controlled",
                "safe",
                "completed"
              ]
            },
            "warningLevel": {
              "type": "string",
              "enum": [
                "advice",
                "watch_and_act",
                "emergency_warning",
                "none"
              ],
              "description": "The Australian Warning System level, only as the agency states it. none means no warning level is published for this record: the agency states none, or the feed carries no warning level. DataQuoll never derives a level, and never reports none for a level it cannot confirm: where a feed's stored level could be DataQuoll's rather than the agency's (a level read from CAP severity where the alert names no AWS level, or a default), this property is absent. A Bureau of Meteorology warning on VicEmergency (Gale, Strong Wind, Severe Weather or Severe Thunderstorm Warning) states none: its kind is in title."
            },
            "severity": {
              "type": "string",
              "enum": [
                "Extreme",
                "Severe",
                "Moderate",
                "Minor",
                "Unknown"
              ],
              "description": "CAP-AU severity, only as the agency states it; Unknown where it states none. DataQuoll never derives it from a warning level, resources, status or magnitude, and where a feed's stored value could be ours rather than the agency's, it reads Unknown. Most agencies state no severity, so most records read Unknown; use warningLevel for the agency's own warning."
            },
            "urgency": {
              "type": "string",
              "enum": [
                "Immediate",
                "Expected",
                "Future",
                "Past",
                "Unknown"
              ],
              "description": "CAP-AU urgency, only as the agency states it; Unknown where it states none, as severity is. DataQuoll never derives it from a warning level or status."
            },
            "certainty": {
              "type": "string",
              "enum": [
                "Observed",
                "Likely",
                "Possible",
                "Unlikely",
                "Unknown"
              ],
              "description": "CAP-AU certainty, only as the agency states it; Unknown where it states none, as severity is. A satellite's own confidence in a hotspot is in details.confidence."
            },
            "location": {
              "type": "object",
              "properties": {
                "address": {
                  "type": "string"
                },
                "suburb": {
                  "type": "string"
                },
                "state": {
                  "type": "string"
                },
                "lga": {
                  "type": "string"
                },
                "latitude": {
                  "type": "number",
                  "description": "Centroid latitude (extracted from geometry)"
                },
                "longitude": {
                  "type": "number",
                  "description": "Centroid longitude (extracted from geometry)"
                },
                "precision": {
                  "type": "string",
                  "enum": [
                    "exact",
                    "address",
                    "interpolated",
                    "street",
                    "grid_square",
                    "locality",
                    "postcode",
                    "lga",
                    "state",
                    "unstated"
                  ],
                  "description": "How far the point can be trusted. 'exact' = the agency's own coordinates. 'grid_square' = the centre of the map-book grid square an SA pager message names (confidence grid_ref): a UBD square 125 to 313 m across, an SA Atlas town cell about 250 m; published 'exact' before v1.88.0. 'address', 'interpolated' and 'street' = an SA pager message placed with G-NAF. When the message names a usable map grid square the point is checked against it: at the address or intersection it names, within 50 m of the square ('interpolated' where G-NAF estimates that address along the street), or on the street it names, inside the square (confidence gnaf_address, gnaf_intersection or gnaf_street). When it names none, only an exact G-NAF match to the number, street and suburb it names is used ('address' or 'interpolated'; there is no square to check it against). 'locality' = the centre of the suburb or town the record names, or of a CFS map-book 1 km grid square. 'postcode' or 'lga' = approximate centroid backfilled for historical archive rows that logged no point. 'state' = the record could not be placed and the point only stands for somewhere in the state; radius and bounding-box searches leave these records out. 'unstated' = a historical archive record whose source never said how it was located (the label /api/v1/hazard-history/point gives the same record). Absent when no coordinates are available (geometry is null)."
                },
                "mapRef": {
                  "type": "string",
                  "description": "SA pager records only: the map-book reference the message states, as paged (book, page and grid square, e.g. ADL 60 P14). The square grid_square and the gnaf_* confidences check a point against."
                },
                "confidence": {
                  "type": "string",
                  "enum": [
                    "grid_ref",
                    "gnaf_address",
                    "gnaf_intersection",
                    "gnaf_street",
                    "geocoded",
                    "suburb",
                    "centroid"
                  ],
                  "description": "How the point was placed, on records where the source did not supply it directly: grid_ref = the centre of a map-book grid cell; gnaf_address, gnaf_intersection and gnaf_street = an SA pager message matched in G-NAF (Geoscape's national address file; attribution at /api/v1/attribution) to the address, intersection or street it names, checked against the message's map grid square when it names one (within 50 m; on the street: inside it); with no usable square, only an exact address match is used. geocoded = poller 1.49.0 and later never write it. It remains on archived WA DFES records, where it marks the agency's own coordinates. suburb = the centre of the town or suburb. For SA pager messages since poller 1.49.0 it is the median of that locality's G-NAF addresses, or, with a regional map-book code, the median for that book's town (also when a facility is named). Earlier SA pager records may carry an OpenStreetMap-derived town point (credited at /api/v1/attribution). centroid = on a point, not placed (precision 'state'); on a historical archive area, the importer derived the record's location from the shape's centre, which says nothing about the shape. Set by the SA pager and WA DFES feeds, on any record that fell back to a state point, and on some archive fire and flood areas. Absent when the agency's own coordinates were used."
                },
                "postcode": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Archive records only (meta.source archive): the postcode a QFD historical record names. Most such records logged no point and sit at the postcode's centroid (precision postcode)."
                },
                "region": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Archive records only: the region a VIC fire history record names, as published (often empty)."
                },
                "regions": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Archive records only: the regions an AIDR historical disaster record names, as published."
                },
                "district": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Archive records only: the DBCA district code a WA fire history record states, e.g. SWC."
                },
                "brigade": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Archive records only: the brigade an SA MFS or CFS historical record names, e.g. 20 ADELAIDE."
                },
                "description": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Archive records only: the source's own words for the place, e.g. a Geoscience Australia earthquake's NORSEMAN, 50KM SE (also some QLD and WA flood and historical records)."
                }
              }
            },
            "boundary": {
              "type": [
                "object",
                "null"
              ],
              "description": "The area the agency published for this incident (a fire perimeter, a warning area, a burn area), or null when it published none. geometry keeps the agency's point. Present on /api/v1/incidents/{id}, and on list endpoints only with include_boundary=true. Coordinates are rounded to 6 decimal places, like area geometry (points are rounded to 4).",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": [
                    "Polygon",
                    "MultiPolygon"
                  ]
                },
                "coordinates": {
                  "type": "array",
                  "description": "Ring arrays for Polygon; an array of those for MultiPolygon.",
                  "items": {}
                }
              }
            },
            "details": {
              "type": "object",
              "description": "Agency-specific incident metadata, passed through from the source feed. OPTIONAL and varies by feed AND by incident (it is not standardised across agencies, and some feeds carry none). Render whatever keys are present rather than expecting a fixed set. The properties below are the common ones; the full per-feed breakdown (every field, its type and meaning) is documented at https://dataquoll.io/guides/incident-details-fields. An archive record (a snapshot before 2026-04-08) can also carry date_as_published or end_as_published: the source's own start or end where we replaced or cleared it.",
              "properties": {
                "description": {
                  "type": "string",
                  "description": "Source detail text; structure varies by feed (NSW RFS alert block, a place name, raw pager text, etc.)."
                },
                "resources": {
                  "type": "integer",
                  "description": "Count of resources (crews/appliances/units) assigned or responding."
                },
                "aircraft": {
                  "type": "integer",
                  "description": "Count of aircraft assigned (SA CFS)."
                },
                "region": {
                  "type": "string",
                  "description": "Agency operational region, e.g. the SA CFS region number 1-6. Not a place name."
                },
                "fire_ban_district": {
                  "type": "string",
                  "description": "Fire ban district, e.g. MOUNT LOFTY RANGES (SA CFS/MFS). Agency spelling, not normalised."
                },
                "decoded_units": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Responding unit names we decode from the unit callsigns in the pager message (SA pager feeds; our decode, not stated by the agency). Absent when no callsign decodes unambiguously."
                },
                "alarm_level": {
                  "type": "integer",
                  "description": "The alarm (turn-out) level an SA fire or SES page states. A response level, not a public warning: pager rows carry warningLevel none and severity and urgency Unknown."
                },
                "alarm_level_description": {
                  "type": "string",
                  "description": "The alarm level in words, e.g. \"Alarm level 2\". Rows archived before October 2026 say Advice, Watch and Act or Emergency Warning: our words for the same number, never agency warnings."
                },
                "priority": {
                  "type": "integer",
                  "description": "The dispatch priority code an SA Ambulance page states, e.g. 1."
                },
                "priority_description": {
                  "type": "string",
                  "description": "Our reading of the priority code (SA Ambulance pager)."
                },
                "incident_level": {
                  "type": "integer",
                  "description": "The incident level SA CFS or MFS states (1 to 3, AIIMS): how big and complex the response is, not a public warning."
                },
                "size": {
                  "type": "string",
                  "description": "Incident size label, e.g. Small/Medium/Large (VIC OSOM)."
                },
                "cad_id": {
                  "type": "string",
                  "description": "CAD dispatch identifier (e.g. WA DFES 6-digit number)"
                },
                "source_id": {
                  "type": "string",
                  "description": "Upstream agency record ID"
                },
                "source_url": {
                  "type": "string",
                  "format": "uri",
                  "description": "Deep link to the incident on the source agency website"
                }
              },
              "additionalProperties": true
            },
            "timestamps": {
              "type": "object",
              "properties": {
                "reported": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time",
                  "description": "When the source reported it. An archive record carries its source's stated time where the source gave one (QFD, ACT ESA, earthquakes); one its source dated only to a day or a period carries that day (a period's first day) at the start of the day, UTC. Null when there is none, or where we have not yet carried the source's time over (the archive's CFA incidents and cyclone track points)."
                },
                "updated": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time",
                  "description": "When the source last updated it. On an archive record, only a time its source stated: QFD's control or stop time, ACT ESA's call closing time, DEECA's under-control or record edit date; null elsewhere, where the archive stored an import time or a copy of the start."
                },
                "fetched": {
                  "type": "string",
                  "format": "date-time"
                },
                "timeStated": {
                  "type": "boolean",
                  "enum": [
                    false
                  ],
                  "description": "Present, and false, only when the source gave a date but no time: reported and updated then carry the right date at a time of day that is not the event's. SA CFS records from 17 April to 14 September 2026, whose time was never read. Absent means the times are the source's own."
                }
              }
            },
            "retraction": {
              "type": "object",
              "description": "Present when the record has left its source. /api/v1/incidents shows such a record only with include_retracted=true. On a recent /api/v1/incidents/snapshot it can carry a withdrawal later than the snapshot moment: the record shows the latest state we hold, and upstream_removed is how an incident normally ends.",
              "properties": {
                "retracted": {
                  "type": "boolean"
                },
                "retractedAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "reason": {
                  "type": "string"
                }
              }
            },
            "archive": {
              "type": "object",
              "description": "Historical enrichment. Present only on deep-archive snapshot results (meta.source = archive). Each field appears only when populated.",
              "properties": {
                "eventStartDate": {
                  "type": "string",
                  "format": "date"
                },
                "eventEndDate": {
                  "type": "string",
                  "format": "date"
                },
                "datePrecision": {
                  "type": "string",
                  "enum": [
                    "month",
                    "season",
                    "financial_year",
                    "year",
                    "period"
                  ],
                  "description": "Present only when the source stated less than a day: eventStartDate is then the first day of the stated period and datePeriodEnd its last, never a stated day."
                },
                "datePeriodEnd": {
                  "type": "string",
                  "format": "date",
                  "description": "The last day of the stated period. Present with datePrecision."
                },
                "magnitude": {
                  "type": "number",
                  "description": "The size the source states, in magnitudeScale units: the area burnt in hectares (fires and burns), or an earthquake's magnitude (MLa, mb, Mw and others)."
                },
                "magnitudeScale": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}