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

# Concepts

> Canonical objects, entity reconciliation, and the two write paths

This page covers the mental model behind the API. Read it before diving into the endpoint
reference.

## Canonical objects

Every entity in Structural is stored as a **canonical object** - a platform-agnostic shape
with:

* A **typed, prefixed ID** (e.g., `pay_xK9mN2vL8gM3y6P` for a payment)
* An **`object` discriminator** (`"payment"`, `"project"`, `"invoice"`, etc.)
* An **`external_ids`** map preserving each platform's native identifiers for lossless
  round-trip conversion

When you read objects via `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

Call `POST /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** whose `mappings` 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.
