Canonical objects
Every entity in Structural is stored as a canonical object - a platform-agnostic shape with:- A typed, prefixed ID (e.g.,
pay_xK9mN2vL8gM3y6Pfor a payment) - An
objectdiscriminator ("payment","project","invoice", etc.) - An
external_idsmap preserving each platform’s native identifiers for lossless round-trip conversion
GET /api/v1/objects, the data field contains the canonical
view. The platform_data field contains the raw platform payload. The mappings array
is a service-layer join showing which platforms the entity is linked to - it is not a stored
field on the canonical object itself.
Entity types
Structural supports different entity types depending on the endpoint:| Surface | Entity types |
|---|---|
/connect + /objects | project, customer, contract, invoice, payment |
/propagate | project, customer, contract, invoice, payment |
/transform | project, customer, contract, invoice, payment, employee, time_entry, change_order, budget_line_item |
/transform accepts 9 entity types because it is a stateless translation primitive - it
does not require entity data to be seeded via /connect.
Platform connections
CallPOST /api/v1/connect with {"platform": "procore"} to seed that platform’s data.
Check the result with GET /api/v1/connections. Each customer can connect both Procore and
Dynamics; the order does not matter.
Entity reconciliation
When both platforms are connected, Structural automatically matches entities across systems using business keys (check numbers, invoice numbers, contract numbers). The same real-world entity appears as one canonical object whosemappings array contains both platforms’
native IDs.
You do not need to pair entities manually. The mappings array on each
GET /api/v1/objects response tells you which platform IDs refer to the same entity.
Two write paths
Structural offers two ways to write data:/transform - stateless preview
Pass in platform data, get back the canonical form and the target platform’s representation.
Nothing is persisted. Use this when you want to:
- Preview what a cross-platform translation looks like
- Validate your data shape before committing
- Get Structural’s canonical representation without side effects
/propagate - stateful write-through
Write a change through the canonical layer and fan it out to every connected platform.
This persists data. Use this when you want the change to actually land in connected
platforms.
Rule of thumb: use /transform to preview, use /propagate to commit.