What is a canonical object?
Every entity in Structural is stored as a canonical object - a platform-agnostic representation with:- A typed, prefixed ID generated deterministically from the source system and entity.
Prefix indicates the entity type:
pay_for payments,proj_for projects,cust_for customers,cor_for contracts,inv_for invoices. - An
objectdiscriminator ("payment","project","invoice", etc.) for type safety. - An
external_idsmap preserving each platform’s native identifiers - Procore’s numeric IDs, Dynamics’ GUIDs, and any secondary identifiers needed for lossless round-trip conversion. - An optional
provider_metadatamap storing platform-specific fields that have no canonical equivalent, so nothing is lost in translation.
Support matrix
contract on /transform requires context: {"commitmentType": "WorkOrderContract"} or
context: {"commitmentType": "PurchaseOrderContract"} when source is procore. See the
Transform endpoint for details.Example: Payment #1 - Lusail Boulevard Tower
The same payment as it appears in each system. This is real seed data, not a fabricated example.- Procore
- Dynamics
- Canonical
- Money in minor units. Procore sends
"1700000.00"(a string in dollars). The canonical stores170000000(an integer in cents). This eliminates floating-point rounding across systems. - ISO 4217 currency codes. The canonical
amountobject carries the currency code explicitly. Dynamics’ payment (6188000) is in QAR (Qatari Riyal) - a different currency for the same payment. Structural normalizes to the canonical amount in the source currency. external_idsfor lossless round-trip. Procore’s numeric IDs and Dynamics’ GUIDs are both preserved. The reverse mapper reads these to reconstruct the original platform payload exactly.- Cross-entity references. The canonical payment references its project and invoice
via typed
ObjectReferencefields (omitted from this example for brevity) that link to other canonical objects by their prefixed IDs.