The Events Timeline is the unified event stream that connects every Axiom product. It contains ~1.7M events from 16+ source normalizers covering permits, council votes, AIS dark events, port calls, regulatory filings, POI lifecycle changes, crop health alerts, and more. Every other Codex dataset references an event_id from this timeline, making it the graph spine across the catalog.
Records are ingested continuously and published as monthly immutable snapshots. Every record inherits the full APRS envelope and carries the join keys documented below.
Dataset-specific fields
| Field | Type | Nullable | Description |
|---|
event_type | dotted string | no | Hierarchical event type. See event taxonomy. |
event_category | enum | no | High-level grouping: maritime, civic, regulatory, poi, environmental, economic, risk. |
entity_id | string | no | Canonical key for the affected entity. Shape depends on entity_type. |
entity_type | enum | no | Type of the affected entity. See entity types. |
entity_name | string | yes | Human-readable name (display only). |
location | PostGIS Point | yes | WGS84 geometry. Use h3_index for joins. |
location_name | string | yes | Human-readable location description. |
h3_index | string | yes | H3 resolution-8 cell derived from location. |
started_at | timestamptz | no | When the real-world event began. |
resolved_at | timestamptz | yes | When the event closed. Null if still open or point-in-time. |
magnitude | float | yes | Event-specific magnitude. Unit varies by event_type. See magnitude units. |
confidence | float [0,1] | yes | Pipeline confidence that the event is real. |
source | string | no | Originating pipeline (aisstream, socrata, granicus, etc.). |
source_ref | string | yes | Source-native reference for re-fetch. |
product | enum | no | Generating product: locus, overwatch, or codex. |
downstream_outcome | string | yes | Post-hoc outcome annotation from cascade detectors. |
outcome_lag_days | integer | yes | Days between this event and its downstream outcome. |
metadata | JSONB | yes | Event-specific overflow fields. See metadata keys. |
jurisdiction_slug | string | yes | Populated for civic and regulatory events. |
Event taxonomy
Event types use a {domain}.{verb} convention. You can filter at the family level (event_type LIKE 'port.%') or by specific action.
Maritime events
| Type | Description |
|---|
port.entered | Vessel crossed port polygon inbound |
port.anchored | Vessel reported anchored status inside port |
port.berthed | Vessel matched to a specific berth |
port.departed | Vessel crossed port polygon outbound |
visit.in_progress | Open vessel visit, no departure yet |
visit.completed | Paired entry and departure. magnitude = visit duration in hours |
dark_event | AIS gap plus kinematic violation |
satellite.{alert_type} | Per-alert-type subtype (e.g., vessel_cluster, dark_emission) |
Civic events
| Type | Description |
|---|
council.approved | Council vote in favor |
council.denied | Council vote against |
council.tabled | Motion tabled or deferred |
council.mentioned | Topic mentioned, no decision |
permit.filed | New permit application |
permit.approved | Permit approved |
permit.denied | Permit denied |
permit.expired | Permit aged out without completion |
permit.completed | Work finaled |
permit.cancelled | Withdrawn by applicant |
permit.revoked | Revoked by authority |
noise_complaint | 311 noise complaint |
business_license | New license issued |
Regulatory events
| Type | Description |
|---|
regulatory.filing | SEC 8-K, 10-K, etc. |
regulatory.enforcement | FDA, EPA, or OSHA action |
regulatory.complaint | Consumer or worker complaint filing |
regulatory.notice | Authority notice |
regulatory.rule | Final rule publication |
POI events
| Type | Description |
|---|
poi.opened | First observation of a new POI |
poi.active | Ongoing POI observation |
poi.temporarily_closed | Temporary closure |
poi.closed | Permanently closed |
Environmental events
| Type | Description |
|---|
environmental.disaster | FEMA/NWS disaster declaration |
crop.ndvi_drop | Sentinel-2 NDVI decline. magnitude = percent change |
crop.ndvi_recovery | NDVI recovery |
river.{status} | Gauge status subtype (flood, action, normal) |
Economic events
| Type | Description |
|---|
jobs.snapshot | Periodic snapshot of active postings per cell. magnitude = listing count |
migration.inflow | Net positive migration. magnitude = net persons |
migration.outflow | Net negative migration |
trade.{flow_type} | Customs/CBP trade flow subtype |
Risk events
| Type | Description |
|---|
risk.critical | Risk score >= 85 |
risk.elevated | Risk score >= 60 |
risk.moderate | Risk score >= 30 |
risk.low | Risk score < 30 |
Entity types
The shape of entity_id depends on entity_type:
| Entity type | entity_id shape | Example |
|---|
vessel | IMO number (preferred) or MMSI | 9432871 |
parcel | {jurisdiction_slug}:{parcel-number} | philadelphia-pa:12-345-678 |
permit | permit:{jurisdiction}:{permit-id} | permit:sf-ca:2024-0412 |
council_matter | legistar:{jurisdiction}:{matter-id} | legistar:phila-pa:24-0031 |
poi | urn:aprs:record:poi:{source}:{id} | urn:aprs:record:poi:fsq:abc123 |
organization | urn:aprs:entity:company:{id} | urn:aprs:entity:company:acme-llc |
person | urn:aprs:entity:person:{id} | urn:aprs:entity:person:j-doe |
h3_cell | H3 resolution-8 string | 88283082b9fffff |
jurisdiction | jurisdiction_slug | philadelphia-pa |
Magnitude units
magnitude is polymorphic — its unit depends on event_type. Always check event_type before comparing magnitude values.
| Event type pattern | Unit |
|---|
visit.completed | Hours (visit duration) |
dark_event | Hours (AIS gap) |
crop.ndvi_* | Percent change vs. 5-year baseline |
jobs.snapshot | Count of active postings |
migration.* | Net persons |
trade.* | USD value (notional) |
risk.* | Risk score 0–100 |
permit.* (with metadata.declared_value) | USD value |
The metadata JSONB column holds type-specific fields. Keys are version-bound — a key present in one event type is not guaranteed in another.
| Key | Event types | Description |
|---|
kinematic_meta | dark_event | Implied speed, Froude limit, OU max reach, actual distance, failed tests |
psc_detention_history | Maritime events | Port State Control detentions linked to this event |
declared_value | permit.* | USD value declared on the permit |
voting_tally | council.* | {yes, no, abstain, absent} |
alert_type_raw | satellite.* | Upstream alert code |
related_event_ids | Any | Array of event_id UUIDs this event is linked to. See event chains |
mmsi | Maritime | MMSI when entity_type='vessel' |
imo | Maritime | IMO number when known |
parcel_id | Civic (permits/zoning) | Parcel identifier |
Event chains
Events are often cascading effects of earlier events. The metadata.related_event_ids field links events into chains:
- Maritime dark event chain:
port.departed → dark_event → port.entered (different port)
- Permit cascade:
council.approved → permit.filed → permit.approved → permit.completed
- Risk cascade:
regulatory.enforcement → risk.critical
Traversal example
Find every downstream civic event following a dark event within 12 months:
WITH RECURSIVE chain AS (
SELECT id, event_type, started_at, metadata
FROM codex.events_timeline
WHERE id = 'c5a8e4f2-9b7d-4c1e-b3a5-1f2d8c0e7a93'
UNION ALL
SELECT e.id, e.event_type, e.started_at, e.metadata
FROM codex.events_timeline e
JOIN chain c
ON c.id::text = ANY(
ARRAY(SELECT jsonb_array_elements_text(e.metadata->'related_event_ids'))
)
WHERE e.started_at - c.started_at BETWEEN interval '0' AND interval '365 days'
)
SELECT * FROM chain
WHERE event_category = 'civic';
Confidence semantics
The confidence field represents pipeline confidence that the event is real, not a downstream prediction score:
- Maritime — AIS signal quality × kinematic model posterior
- Civic — LLM extraction confidence × source reliability score
- Regulatory — 1.0 for primary-source filings; degraded for secondary mirrors
- Environmental — satellite cloud cover × model confidence
Events with confidence < 0.4 are excluded from Research and Commercial tier exports by default.
Join keys
| Key | Presence | Notes |
|---|
event_id (= id) | always | Primary key — the spine across all Codex datasets |
record_id | always | APRS URN: urn:aprs:record:event:{product}:{id} |
chunk_id | always | Deterministic from record_id |
h3_index | often | Null for events without geometry (e.g., regulatory filings) |
jurisdiction_slug | often | Always for civic and regulatory; sometimes for others |
mmsi | maritime only | In metadata.mmsi |
imo | maritime only | In metadata.imo (preferred over MMSI) |
parcel_id | civic only | In metadata.parcel_id |
entity_urn | often | Present for person, organization, and POI entities |
Example queries
Permit cascades after approved rezonings
SELECT
rez.id AS rezoning_event_id,
rez.jurisdiction_slug,
rez.started_at AS rezoning_date,
permit.event_type AS permit_stage,
permit.started_at AS permit_date,
permit.started_at - rez.started_at AS lag
FROM codex.events_timeline rez
JOIN codex.events_timeline permit
ON permit.metadata->'related_event_ids' ? rez.id::text
WHERE rez.event_type = 'council.approved'
AND rez.started_at >= now() - interval '1 year'
AND permit.event_type LIKE 'permit.%'
ORDER BY rez.jurisdiction_slug, rez.started_at;
All dark events longer than 48 hours
SELECT
entity_name,
started_at,
resolved_at,
magnitude AS gap_hours,
metadata->'kinematic_meta'->>'implied_speed_kn' AS implied_speed
FROM read_parquet('events-timeline-2026-04.parquet')
WHERE event_type = 'dark_event'
AND magnitude > 48
ORDER BY magnitude DESC;
Known limitations
entity_id shape is polymorphic — you must branch on entity_type before joining.
magnitude units vary by event type. See magnitude units before comparing.
related_event_ids coverage is uneven. Currently populated by maritime and permit normalizers; civic cascade linking is in progress.
downstream_outcome is post-hoc and sparse — only populated once cascade detectors run, which can take 30–90 days.