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
| Type | What it is | Default TTL |
|---|---|---|
accident | A collision. | 2 h |
stopped_vehicle | An immobilised vehicle — breakdown, on the shoulder or in a lane. | 1 h |
object_on_road | Debris, lost cargo, an animal. | 30 min |
weather_hazard | Black ice, a fog bank, aquaplaning, violent gusts. | 3 h |
congestion | An abnormal slowdown or queue. Usually a segment — send a geometry, and revise it as the tail grows. | 30 min |
wrong_way_vehicle | A vehicle driving against traffic. | 20 min |
road_damage | A pothole, a broken surface, a damaged barrier — the one type here that is not really ephemeral. | 12 h, 30 d ceiling |
people_on_road | A person in or beside the carriageway: a driver out of a broken-down car, a road worker, an officer directing traffic. | 30 min |
police_check | A control in place — a checkpoint, a patrol, officers with a speed gun. | 30 min |
speed_camera | Automatic enforcement, with nobody standing beside it: fixed, mobile, average-speed or red-light. | 1 h, 7 d ceiling |
road_closure | The road, a lane or a slip road is shut. | 2 h |
infrastructure_fault | A dead traffic signal, a sign missing or knocked round, street lighting out. | 6 h, 7 d ceiling |
roadworks | Works 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_atoff the event. Never compute it from the table above; it moves. The defaults themselves, when you need them, arettl_secondsandmax_ttl_secondson/v1/meta/taxonomy. - Drop events at
expires_atyourself. 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.