Case study 02

Entity resolution and layered entitlements

At an AI document automation platform for institutional financial services.

Problem

Each extracted document existed in isolation. Analysts had no unified view of an entity across documents and reporting periods, so tracking its state over time meant manual cross-referencing.

What I built

An entity resolution layer that maps document level aliases, such as "Acme Co." and "A.B.C. Ltd.", to a canonical entity record. That record acts as a key linking related documents across types and time. It drives reference data enrichment, entity metadata validation, point in time tracking, and distribution across three channels: the UI, a REST API, and MCP.

Entity resolution and reference pipelineEntitlements modelNoticedocumentDocument templateEntity: Acme Co.Sub-entity: A.B.C. Ltd.Due 01 Mardue datePoint-in-time dataentity trackeralias-to-entity mappingMaster entity recordEntity: Acme CompanySub-entity: ABC Ltd.Externalreference datasetreference data enrichmentMulti-channeldistributiondocument view ยท REST APIMCPextracted datametadata validationsRegion A entityregion-scoped recordentity mappingManageruserRevieweruserRead onlyuserrole-definedactionsRegion A groupRegion B groupHas access todocument templateHas access toRegion A entitiesBoth the action entitlement and thedata-scoped entitlement must applyfor access via the document viewer,a data inbox line entry, the RESTAPI, or an MCP call.

Redrawn from the original with generic labels. Structure unchanged.

The decision that mattered

Two enterprise clients wanted incompatible group structures. One organized by entity family, the other by geography. Rather than build both, I built the mapping layer that resolves either. That architecture later carried into an entirely new vertical with minimal incremental build.

The entitlements half runs on two dimensions that must both apply. Role defined actions determine what you can do. Data scoped entitlements determine which entities you can see. Enforcement lives at the data access layer rather than in any one client, so it applies identically whether the request comes from the UI, the API, or an agent. An agent inherits the calling user's scope by construction. There is no separate agent permission model to drift out of sync.

What I'd revisit

Point in time tracking and entitlements were built as adjacent features on the same entity record, not as one coherent model. A change to an entity's group assignment mid reporting period can create a short window where past and present access don't line up cleanly. I'd want that resolved at the data model level, not patched at the query layer.