Skip to main content

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 object discriminator ("payment", "project", "invoice", etc.) for type safety.
  • An external_ids map 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_metadata map storing platform-specific fields that have no canonical equivalent, so nothing is lost in translation.

Support matrix

Entity typeKey fields/connect + /objects/propagate/transformProcoreDynamics
projectname, number, status
customername, currency, tax_id
contracttitle, number, amount, status
invoicenumber, amount, status
paymentamount, check_number, status
employeefirst_name, last_name, email, job_title
time_entryhours, date, employee
change_ordertitle, amount, status
budget_line_itemcost_code, budgeted_amount, name
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.
{
  "id": 1823456,
  "amount": "1700000.00",
  "status": "pending",
  "check_number": "CHK-2026-0101",
  "contract_id": 1945782,
  "project_id": 341276,
  "requisition_id": 10061,
  "invoice_number": "INV-2025-0061",
  "date": "2026-02-01",
  "payment_number": 24,
  "notes": "Substructure progress payment #1"
}
What to notice:
  • Money in minor units. Procore sends "1700000.00" (a string in dollars). The canonical stores 170000000 (an integer in cents). This eliminates floating-point rounding across systems.
  • ISO 4217 currency codes. The canonical amount object 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_ids for 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 ObjectReference fields (omitted from this example for brevity) that link to other canonical objects by their prefixed IDs.