Skip to main content
POST
/
propagate
Propagate a change
curl --request POST \
  --url https://api.structural.app/api/v1/propagate \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "entity_type": "payment",
  "data": {},
  "format": "canonical",
  "canonical_id": "<string>",
  "platform": "procore",
  "platform_id": "<string>"
}
'
{
  "data": {
    "canonical": {
      "id": "pay_xK9mN2vL8gM3y6P",
      "entity_type": "payment",
      "updated_fields": [
        "amount",
        "notes"
      ],
      "data": {}
    },
    "propagated": {},
    "references": {
      "resolved": [
        {
          "field": "<string>",
          "canonical_id": "<string>",
          "platform_id": "<string>",
          "injected": true,
          "reason": "<string>"
        }
      ],
      "unresolved": [
        {
          "field": "<string>",
          "canonical_id": "<string>",
          "reason": "<string>"
        }
      ]
    }
  }
}
Writes a change through the canonical layer and fans it out to all connected platforms. This is the stateful write path - data is persisted. For a stateless preview, use POST /transform instead.

Two input modes

The request body accepts two modes, controlled by the format field:

Canonical mode

Set format: "canonical" and provide canonical_id. Use this when you already have the canonical ID from a previous GET /objects call:
{
  "format": "canonical",
  "canonical_id": "pay_xK9mN2vL8gM3y6P",
  "entity_type": "payment",
  "data": {
    "amount": { "amount": 175000000, "currency": "USD" },
    "notes": "Updated payment amount"
  }
}

Platform mode

Set format: "platform" and provide platform + platform_id. Use this when you only have the platform-native ID (e.g., from a Procore webhook or scrape):
{
  "format": "platform",
  "platform": "procore",
  "platform_id": "1823456",
  "entity_type": "payment",
  "data": {
    "amount": "1750000.00",
    "notes": "Updated payment amount"
  }
}
Structural resolves the platform ID to the canonical entity and patches from there. Rule of thumb: if you read via /objects, use canonical mode. If you got the ID from a platform webhook or external system, use platform mode.

Response

The response includes the updated canonical object, which fields changed, and the propagated output per platform. The updated_fields array tells you exactly which canonical fields were modified.
Writes are not transactional across platforms. The write order is: (1) canonical update, (2) source platform mirror, (3) fan-out to each other connected platform sequentially. If step 3 fails for one platform, earlier writes are already persisted.Only platforms that succeeded appear in the propagated map. Missing platforms were not propagated - retry the /propagate call. Do not assume atomicity.

Authorizations

Authorization
string
header
required

API key passed as a Bearer token. Keys are prefixed: sk_demo_* (sandbox), sk_live_* (production), sk_test_* (staging).

Body

application/json
entity_type
enum<string>
required
Available options:
payment,
project,
customer,
invoice,
contract
data
object
required

The fields to update

format
enum<string>
default:canonical

Input mode. canonical — you have a canonical ID from a previous /objects call. platform — you only have the platform-native ID.

Available options:
canonical,
platform
canonical_id
string

Required when format is canonical

platform
enum<string>

Required when format is platform

Available options:
procore,
dynamics
platform_id
string

Required when format is platform

Minimum string length: 1

Response

Change propagated

data
object