Skip to content

Event types

Thirteen today, eight of them until 2026-08-11. Do not hardcode this list — fetch it:

curl -s "$RELAY/v1/meta/event-types"
["accident","stopped_vehicle","object_on_road","weather_hazard","congestion","wrong_way_vehicle","road_damage","people_on_road","police_check","speed_camera","road_closure","infrastructure_fault","roadworks"]

No key required, and the sentence above is not a formality: this list gained five values in one day, and a client that enumerated it at build time is the one that silently drops them. A type your client does not recognise means the event is opaque to it — skip it, never crash, and never map it onto a type you do know. See Compatibility.

The thirteen

TypeWhat it isDefault TTL
accidentA collision.2 h
stopped_vehicleAn immobilised vehicle — breakdown, on the shoulder or in a lane.1 h
object_on_roadDebris, lost cargo, an animal.30 min
weather_hazardBlack ice, a fog bank, aquaplaning, violent gusts.3 h
congestionAn abnormal slowdown or queue. Usually a segment — send a geometry, and revise it as the tail grows.30 min
wrong_way_vehicleA vehicle driving against traffic.20 min
road_damageA pothole, a broken surface, a damaged barrier — the one type here that is not really ephemeral.12 h, 30 d ceiling
people_on_roadA person in or beside the carriageway: a driver out of a broken-down car, a road worker, an officer directing traffic.30 min
police_checkA control in place — a checkpoint, a patrol, officers with a speed gun.30 min
speed_cameraAutomatic enforcement, with nobody standing beside it: fixed, mobile, average-speed or red-light.1 h, 7 d ceiling
road_closureThe road, a lane or a slip road is shut.2 h
infrastructure_faultA dead traffic signal, a sign missing or knocked round, street lighting out.6 h, 7 d ceiling
roadworksWorks a driver ran into: a mobile crew, an emergency dig.4 h

These are ephemeral events only — the real-time flow. Planned roadworks are not here: they are known weeks ahead, last weeks, and are out of this contract entirely. That is why roadworks names the mobile crew and the emergency dig instead — the works nobody published in advance.

police_check and speed_camera divide on people vs device, because that is the only rule a publisher can apply from a windscreen: officers at the roadside with a hand-held radar are a police_check, an unattended cabinet or a camera car is a speed_camera. Neither is people_on_road, where a person is the hazard rather than the reason to slow down.

subtype — the fine detail under a type

Optional, and designed to grow without notice: GET /v1/meta/taxonomy serves the current fine vocabulary per type. type alone is always enough to act on — subtype refines it, never replaces it, and a client that has never heard of a given subtype still has the type it sits under. Filter on type; refine on subtype; never filter on subtype alone, because unlike type it is expected to gain values between deploys. Same tolerate-the-unknown rule as type itself — see Compatibility.

The shape, so you never guess it:

curl -s "$RELAY/v1/meta/taxonomy"
{
  "schema_version": 2,
  "types": [
    {"id": "accident", "subtypes": [],
     "ttl_seconds": 7200, "max_ttl_seconds": 43200},
    {"id": "road_damage",
     "subtypes": ["pothole", "damaged_surface", "damaged_barrier", "sinkhole"],
     "ttl_seconds": 43200, "max_ttl_seconds": 2592000},
    // … one entry per type in /v1/meta/event-types, same order
  ]
}

No key required, like the flat list. ttl_seconds is the default lifetime and max_ttl_seconds the hard ceiling — this route is the runtime source for the numbers in the table above, which are current values, not contract. It is a second endpoint rather than a richer /v1/meta/event-types because that route returns a flat list[str] and will forever: re-typing a response clients already parse is exactly what the wire contract forbids.

On the publish path subtype is validated against its parent — a pothole filed under weather_hazard is a 422, never silently dropped. See Publishing.

What a fixed camera is doing in an ephemeral contract

A fixed speed camera lives for years, and everything else here lives for hours. It is on this list anyway, and the answer is in the TTL table rather than in the vocabulary: a speed_camera starts with a one-hour life, sized for the camera car that has already driven off, and each confirmation from a new reporter buys two hours, up to a seven-day ceiling. So the fixed one stays live exactly as long as traffic keeps seeing it, and the mobile one dies on the clock of a mobile one. No per-subtype machinery, and no guess about installation dates.

The same shape carries infrastructure_fault (a dead signal lasts until a crew arrives) and road_damage (a pothole lasts until somebody repairs it, 30-day ceiling).

A permanent reference dataset of fixed cameras — they are open data in several countries — would be a better home for that half, and it would sit beside roadworks rather than in this contract. It changes nothing for a client either way: the events are on the same disc, in the same stream, under the same filter.

TTL is not how long the hazard lasts

The TTL is how long Relay serves an event without further evidence.

A real three-hour accident outlives its two-hour TTL because other vehicles confirm it — each first confirmation from a new reporter extends the life to at least 15 minutes from that moment, capped at 12 hours from creation. Three types override both numbers because they are not really ephemeral — road_damage (12 h extension, 30 d ceiling), speed_camera and infrastructure_fault (2 h and 6 h, 7 d each). An unconfirmed report dies on schedule. The network decides longevity, not a constant we guessed up front.

Consequences for your client:

  • Read expires_at off the event. Never compute it from the table above; it moves. The defaults themselves, when you need them, are ttl_seconds and max_ttl_seconds on /v1/meta/taxonomy.
  • Drop events at expires_at yourself. Natural expiry sends no delta. See Streaming.
  • An unknown type falls back to the shortest known TTL — an event we do not understand must never outlive the ones we do.

wrong_way_vehicle is not like the other seven

It is the only mobile hazard in the vocabulary, and the only one whose value decays in seconds rather than minutes. It is also the event that justifies the sub-second latency commitment on its own.

Two things make it different, and both are traps.

1. It moves — roughly 1.5 km per minute. A fixed point is wrong almost immediately. Publish a geometry covering the exposed stretch of road rather than a dot, and re-publish as a new event (a new client_event_id) as your information updates — a replay of the same id does not move an event, it returns the original. See Publishing.

That is also why its TTL is the shortest we have, and deliberately shorter than an accident's. A wrong-way incident resolves within minutes: the driver exits, stops, or crashes. A report that outlives it is not merely useless — it triggers braking on a road that is now clear. Being too short is recoverable here, because a confirmation extends it. Being too long is not.

2. heading is the direction of the drivers at risk, not the wrong-way vehicle's.

The hazard travels opposite to the people who need to know. If a car is heading north on a southbound carriageway, publish heading: 180 — the direction of the traffic in its path. Publish 0 and every consumer filtering by direction of travel raises the alert for drivers going the other way: it reaches the one person who already knows, and nobody who can act on it. Relay does not filter on heading itself, so nothing on our side will catch the inversion for you.

The full statement is in Publishing. It applies to every type; this is only the one where the two readings diverge.

Choosing a type

There is no other and no free-text type, on purpose — a vocabulary with an escape hatch becomes a vocabulary nobody can consume. If nothing fits, the closest accurate match plus a description beats a wrong one; a rockfall published as an accident routes traffic around the wrong kind of hazard (it is an object_on_road, subtype landslide), and the mistake is invisible to whoever made it.

severity (low / medium / high) is your own assessment and is optional and orthogonal — it does not feed the trust score.

type + position + heading must always be enough to understand an event on their own. description is an aid, capped at 280 characters, and never something a consumer should be required to parse.