event

Hierarchy Updated

Published when the org hierarchy changes. Emitted on two different sources with two different payload shapes.

Event

Event Overview

The org hierarchy is the manager chain, flattened onto every person as level_1_idlevel_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

FieldValue
sourcerio.core
detail-typeHierarchy Updated
detail.event_namerio.core.identity.hierarchy.updated
PayloadAuditEventDetail — the standard envelope, see the schema below
Emitted atapi/services/hierarchy_service.py:2983-2994
PurposeRecord that the change happened, for the audit trail

Variant B — the recalculation trigger

FieldValue
sourcerio.api.hierarchy_change (hierarchy_service.py:2077-2080)
detail-typeHierarchy Updated (hierarchy_service.py:2081-2084)
detail.event_namerio.user.hierarchy.updated (hierarchy_service.py:2023-2025)
Payloada plain dict, not the audit envelope — event_name, domain: "identity", version: "v1", actor, context, metadata, data
Emitted atapi/services/hierarchy_service.py:2050-2101
PurposeMake 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 source that begins with rio.

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.

Event-driven architecture documentation: RIO

JSON Schema

20 properties

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.

event_idstring
required

Auto-generated identifier, format evt_{12 hex chars}.

event_namestringconstant: rio.core.identity.hierarchy.updated
required

Dotted logical name of this event.

source_servicestring
required

Service that published the event.

tenant_idstring
required

Tenant the change belongs to.

domainstring
required
subdomainstring
entity_typestring
required

EventEntityType, models.py:15-81.

Allowed values: user role hierarchy tenant
entity_idstring
required

Identifier of the record that changed.

actionstring
required

EventAction enum.

Allowed values: created updated deleted deactivated assigned removed changed
statusstring
required

Emitted uppercase here; the audit service lowercases it on ingest.

Allowed values: SUCCESS FAILED REJECTED
actor_typestring
required
Allowed values: USER SYSTEM ADMIN CRM
actor_idstring
required

Who or what triggered the change.

occurred_atstring<date-time>
required

ISO-8601 timestamp.

correlation_idstring | null

Auto-generated corr_* value for tracing across services.

request_idstring | null

Auto-generated req_* value for the originating HTTP request.

beforeobject | null

Record state before the change.

afterobject | null

Record state after the change.

error_codestring | null
error_messagestring | null
metadataobject | null