Hierarchy Updated
Published when the org hierarchy changes. Emitted on two different sources with two different payload shapes.
Event Overview
The org hierarchy is the manager chain, flattened onto every person as level_1_id … level_10_id
plus a hierarchy_path. When that chain changes, everything downstream that answers “who reports to
this manager” has to be recalculated. This event is the trigger for that work.
Read this carefully: Hierarchy Updated is emitted twice, on two different sources, with two
different payload shapes. They are not interchangeable.
Variant A — the audit record
| Field | Value |
|---|---|
source | rio.core |
detail-type | Hierarchy Updated |
detail.event_name | rio.core.identity.hierarchy.updated |
| Payload | AuditEventDetail — the standard envelope, see the schema below |
| Emitted at | api/services/hierarchy_service.py:2983-2994 |
| Purpose | Record that the change happened, for the audit trail |
Variant B — the recalculation trigger
| Field | Value |
|---|---|
source | rio.api.hierarchy_change (hierarchy_service.py:2077-2080) |
detail-type | Hierarchy Updated (hierarchy_service.py:2081-2084) |
detail.event_name | rio.user.hierarchy.updated (hierarchy_service.py:2023-2025) |
| Payload | a plain dict, not the audit envelope — event_name, domain: "identity", version: "v1", actor, context, metadata, data |
| Emitted at | api/services/hierarchy_service.py:2050-2101 |
| Purpose | Make the hierarchy Lambda actually do the recalculation |
Variant B’s data block carries hierarchy_revision_id, a list of updates, and
provisioning_source: "MANUAL". Entries are batched under a 240 KB ceiling
(EVENTBRIDGE_ENTRY_SIZE_LIMIT, api/core/constants.py:121), because EventBridge caps entry size.
All three of source, detail-type and detail.event_name are configurable through settings; the
values above are the fallbacks used when nothing is overridden.
Why there are two variants
Variant B is shaped to match the event that rio-ingestion-service publishes when the CRM sync
detects a manager change — see
HierarchyUpdatedByIngestion.
Because both use detail-type Hierarchy Updated and detail.event_name
rio.user.hierarchy.updated, a single rule catches hierarchy changes from either origin:
${DeployPrefix}-hierarchy-updates (infrastructure/lambda/template.yaml:168-179) source: [rio.glue.crm_sync, rio.api.hierarchy_change] detail-type: [Hierarchy Updated] detail.event_name: [rio.user.hierarchy.updated]So a hierarchy change made through the API and one detected by the scheduled CRM sync both land in the same recalculation Lambda.
Who consumes it
- Variant B → this service’s own hierarchy recalculation Lambda, via the rule above.
- Variant A → the Audit Service, via
its catch-all rule matching any
sourcethat begins withrio.
Payload Schema
The schema below documents Variant A, the audit envelope. Variant B’s shape is described in the
table above and is structurally identical to HierarchyUpdatedByIngestion.
JSON Schema
Variant A audit record for an org-hierarchy change. Envelope published on source `rio.core` with detail-type `Hierarchy Updated`. Shape is the AuditEventDetail Pydantic model, rio-identity-service/api/events/models.py:88-120.
Auto-generated identifier, format evt_{12 hex chars}.
rio.core.identity.hierarchy.updatedDotted logical name of this event.
Service that published the event.
Tenant the change belongs to.
EventEntityType, models.py:15-81.
user role hierarchy tenantIdentifier of the record that changed.
EventAction enum.
created updated deleted deactivated assigned removed changedEmitted uppercase here; the audit service lowercases it on ingest.
SUCCESS FAILED REJECTEDUSER SYSTEM ADMIN CRMWho or what triggered the change.
ISO-8601 timestamp.
Auto-generated corr_* value for tracing across services.
Auto-generated req_* value for the originating HTTP request.
Record state before the change.
Record state after the change.