> ## Documentation Index
> Fetch the complete documentation index at: https://docs.structural.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Entity types

> Canonical object shape, support matrix, and a concrete example

## 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 type        | Key fields                                      | `/connect` + `/objects` | `/propagate` | `/transform` | Procore | Dynamics |
| ------------------ | ----------------------------------------------- | :---------------------: | :----------: | :----------: | :-----: | :------: |
| project            | `name`, `number`, `status`                      |            ✓            |       ✓      |       ✓      |    ✓    |     ✓    |
| customer           | `name`, `currency`, `tax_id`                    |            ✓            |       ✓      |       ✓      |    ✓    |     ✓    |
| contract           | `title`, `number`, `amount`, `status`           |            ✓            |       ✓      |       ✓      |    ✓    |     ✓    |
| invoice            | `number`, `amount`, `status`                    |            ✓            |       ✓      |       ✓      |    ✓    |     ✓    |
| payment            | `amount`, `check_number`, `status`              |            ✓            |       ✓      |       ✓      |    ✓    |     ✓    |
| employee           | `first_name`, `last_name`, `email`, `job_title` |            —            |       —      |       ✓      |    ✓    |     ✓    |
| time\_entry        | `hours`, `date`, `employee`                     |            —            |       —      |       ✓      |    ✓    |     ✓    |
| change\_order      | `title`, `amount`, `status`                     |            —            |       —      |       ✓      |    ✓    |     ✓    |
| budget\_line\_item | `cost_code`, `budgeted_amount`, `name`          |            —            |       —      |       ✓      |    ✓    |     ✓    |

<Note>
  `contract` on `/transform` requires `context: {"commitmentType": "WorkOrderContract"}` or
  `context: {"commitmentType": "PurchaseOrderContract"}` when `source` is `procore`. See the
  [Transform endpoint](/api-reference/transform) for details.
</Note>

## Example: Payment #1 - Lusail Boulevard Tower

The same payment as it appears in each system. This is real seed data, not a fabricated
example.

<Tabs>
  <Tab title="Procore">
    ```json theme={null}
    {
      "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"
    }
    ```
  </Tab>

  <Tab title="Dynamics">
    ```json theme={null}
    {
      "id": "a34e74db-53c7-4368-8fe7-bb87c83c7902",
      "amount": 6188000,
      "postingDate": "2026-02-05",
      "documentNumber": "CR-6101",
      "externalDocumentNumber": "CHK-2026-0101",
      "appliesToInvoiceNumber": "INV-2025-0061",
      "description": "Substructure progress payment #1",
      "dimensions": [
        { "code": "PROJECT", "valueCode": "J-LEX-147" }
      ]
    }
    ```
  </Tab>

  <Tab title="Canonical">
    ```json theme={null}
    {
      "id": "pay_xK9mN2vL8gM3y6P",
      "object": "payment",
      "amount": {
        "amount": 170000000,
        "currency": "USD"
      },
      "status": "pending",
      "payment_date": "2026-02-01",
      "check_number": "CHK-2026-0101",
      "invoice_number": "INV-2025-0061",
      "payment_number": 24,
      "notes": "Substructure progress payment #1",
      "external_ids": {
        "procore": {
          "id": 1823456,
          "contract_id": 1945782,
          "project_id": 341276,
          "requisition_id": 10061
        },
        "dynamics": {
          "id": "a34e74db-53c7-4368-8fe7-bb87c83c7902"
        }
      }
    }
    ```
  </Tab>
</Tabs>

**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.
