Component Edit API — Programmatic Property Updates

This guide explains how to read and modify component properties via the HiStruct REST API, simulating the same operations the Aurelia frontend performs.


Overview

There are two approaches to modifying component state:

  1. Direct PVC manipulation — set the raw JSON parameter values (quick, but bypasses triggers and validation)
  2. Property transaction — load the property ViewModel, modify it, and POST it back with a trigger (proper, runs the full constraint pipeline)

The property transaction approach is the correct way to simulate user interaction.


API Endpoints

All endpoints are relative to the HiStruct instance (e.g., https://deve.histruct.com).

Component discovery

GET /api/spaces/{spaceKey}/components/{componentId}?privateKey={pk}

Returns a JSON object with URLs to all component resources:

Field Description
component_pvc_json_url Current PVC as JSON
component_edit_url Edit page (HTML)
component_details_url Component metadata
component_pvc_hash_url Content-addressed PVC hash

Read PVC (direct)

GET /api/spaces/{spaceKey}/components/{componentId}/pvc-json?privateKey={pk}

Returns the current PVC as a JSON object. Example:

{
  "inMapImageUrl": "https://example.com/image.png",
  "reconResponse": {"SOK": true, "timestamp": "2026..."}
}

Evaluate expressions

POST /api/spaces/{spaceKey}/components/{componentId}/expression?privateKey={pk}
Content-Type: application/json

{"Expression": "hasMask"}

Returns: {"Result": true}

Useful for checking computed values without loading the full property UI.


Property Transaction Flow

This is the proper way to simulate a user editing a parameter and clicking an action button.

Step 1: Load properties

POST /api/spaces/{spaceKey}/components/{componentId}/load-properties?privateKey={pk}
Content-Type: application/json

{"contextPath": "inMapImageUrl"}

The contextPath is the FCS variable identifier (e.g., "inMapImageUrl"). Returns a ContextParamValueViewModel:

{
  "ComponentId": "479418",
  "ComponentSubRevision": null,
  "ComponentPrivateKey": null,
  "ComponentPrototypeName": "https://github.com/HiStructClient*fcs-lab-01*...",
  "ContextPath": "inMapImageUrl",
  "ContextPathHash": "32186",
  "RevisionPublished": false,
  "ContextTitle": "Satellite Image URL",
  "ParamValueDto": {
    "Resources": {"ListOptionsEntries": []},
    "Value": {
      "TypeName": "ParamValueStringVM",
      "Value": "https://example.com/image.png",
      "Identifier": "inMapImageUrl",
      "HumanName": "Satellite Image URL",
      "IsEditable": true,
      "FromUser": true
    }
  },
  "StagingEditedPvcB64": null,
  "OriginalPvcB64": "fcc698a8...",
  "DialogOriginalPvcB64": "fcc698a8..."
}

Step 2: Modify the ViewModel (optional)

To change a parameter value, modify ParamValueDto.Value.Value in the returned VM:

{
  "ParamValueDto": {
    "Value": {
      "Value": "https://new-image-url.com/image.png"
    }
  }
}

Step 3: POST update-properties (with trigger)

POST /api/spaces/{spaceKey}/components/{componentId}/update-properties?privateKey={pk}
Content-Type: application/json

{
  "vm": <the ContextParamValueViewModel from step 1>,
  "vms": null,
  "validationTrigger": "detectBuildingsTrigger",
  "observedContexts": null,
  "navigationContexts": null,
  "jobResultKey": null
}

The validationTrigger is the name of the FCS trigger variable to execute (same as ValidationTrigger on Fcs.Parameter.ItemAction).

Response format

The response is chunked JSON with EditResult objects:

["chunk:000000000513", {
  "validationresult": {
    "Exception": null,
    "NotesList": ["Running building detection...", "Downloading mask..."],
    "UpdatedValuesList": [],
    "Ok": false
  },
  "originalPvcB64": "d8dea119...",
  "timeline": {
    "CurrentAction": {"CheckoutToken": "..."},
    "Undo": {"CheckoutToken": null},
    "Redo": {"CheckoutToken": null}
  },
  "ResultType": "ResultUpdateTransaction"
}]
Field Meaning
Exception null on success, error string on failure
NotesList Collected Explanation strings from constraints that fired
Ok Whether the edit was fully committed
originalPvcB64 PVC hash before the transaction
timeline Undo/redo tokens for the edit history

Direct PVC Manipulation

For quick updates without triggers (e.g., pre-filling values at creation time):

Create component with PVC

POST /{spaceKey}/{language}/inquiry/new-configuration
Content-Type: application/json

{
  "FccRef": "SkyNetRecognizer2.fcc",
  "PvcJsonString": "{\"inMapImageUrl\": \"https://example.com/image.png\"}"
}

PVC Store (content-addressed)

The PVC store is a content-addressed key-value store. The Aurelia frontend uses it for the admin palette editor:

POST /api/pvc-store/json
Content-Type: application/json
Body: "{\"key\": \"value\"}"

→ Returns: "hash-string"
GET /api/pvc-store/{hash}/json
→ Returns: the PVC object

Note: PVC store endpoints require Admin role.

Edit PVC directly (MVC form POST)

POST /edit/ComponentPvc
Content-Type: application/x-www-form-urlencoded

ComponentRevision=479418&PvcJsonString={"inMapImageUrl":"..."}

This bypasses triggers entirely and writes the PVC directly.


Complete Example: Programmatic Detection Pipeline

# 1. Create component with pre-filled image URL
$pvc = @{ inMapImageUrl = $imageUrl } | ConvertTo-Json -Compress
$body = @{ FccRef = "SkyNetRecognizer2.fcc"; PvcJsonString = $pvc } | ConvertTo-Json
$r = Invoke-WebRequest -Uri "$base/sky/en/inquiry/new-configuration" `
    -WebSession $session -Method Post -ContentType "application/json" -Body $body
$result = $r.Content | ConvertFrom-Json
$cid = $result.componentId; $pk = $result.privateKey

# 2. Load properties VM
$loadBody = @{ contextPath = "inMapImageUrl" } | ConvertTo-Json
$r = Invoke-WebRequest -Uri "$base/api/spaces/sky/components/$cid/load-properties?privateKey=$pk" `
    -WebSession $session -Method Post -ContentType "application/json" -Body $loadBody
$vm = $r.Content | ConvertFrom-Json

# 3. Trigger detection (runs all constraints in the trigger)
$updateBody = @{
    vm = $vm
    vms = $null
    validationTrigger = "detectBuildingsTrigger"
    observedContexts = $null
    navigationContexts = $null
    jobResultKey = $null
} | ConvertTo-Json -Depth 10

$r = Invoke-WebRequest -Uri "$base/api/spaces/sky/components/$cid/update-properties?privateKey=$pk" `
    -WebSession $session -Method Post -ContentType "application/json" -Body $updateBody

# 4. Verify results
$pvcR = Invoke-WebRequest -Uri "$base/api/spaces/sky/components/$cid/pvc-json?privateKey=$pk" `
    -WebSession $session
$pvc = $pvcR.Content | ConvertFrom-Json
# $pvc.reconResponse.SOK → true
# $pvc.maskBase64 → base64 PNG string

ContextEditContextRequestDto Reference

The update-properties endpoint accepts this DTO:

public record ContextEditContextRequestDto(
    ContextParamValueViewModel vm,       // the loaded property VM
    ContextParamValueViewModel[] vms,    // null for single-property edits
    string validationTrigger,            // trigger name (e.g., "detectBuildingsTrigger")
    string observedContexts,             // null for simple cases
    string[] navigationContexts,         // null for simple cases
    string jobResultKey                  // null unless committing a background job result
);

Source: ContextParamValueViewModel.cs:31-38


Gotchas

  1. Variables must be InputItems — A trigger constraint can only write to variables declared in InputPageGallery_ParameterItemClasses. Hidden variables use VisibilityCondition = "False".

  2. Absolute vs relative URLs — If the FCS code prepends a base URL to an API response field, make sure the response field contains a relative path (e.g., /api/GetMask?ts=...), not an absolute URL.

  3. ContextPath must be a variable identifier — The contextPath in load-properties is the FCS variable name (e.g., "inMapImageUrl"), not a UI page name.

  4. PVC store requires Admin — The GET/POST /api/pvc-store/... endpoints check IsUserAdmin(). The load-properties / update-properties endpoints work for any authorized user.

  5. Chunked response — The update-properties response is a JSON array of ["chunk:NNNNNN", {...}] pairs. Parse accordingly.


Template Upgrade — Updating a component to a newer commit

When the prototype branch has new commits, you can upgrade a component to use the latest code.

Check upgrade availability

GET /api/spaces/{spaceKey}/components/{componentId}/template-upgrade?privateKey={pk}

Returns:

Field Type Description
upgradeAvailable bool Whether a newer commit exists
currentlyUsedPrototypeCommit object Current commit reference with .Version (SHA)
latestCommitForPrototypeUpgrade object Latest commit reference
componentTemplateUpgradePostUrl string Full URL to POST for upgrade (null if not available)

Execute the upgrade

POST /api/spaces/{spaceKey}/components/{componentId}/template-upgrade?componentPrototype={protoString}&privateKey={pk}
Body: {}

The componentPrototype format: https://github.com/{org}*{repo}*{fccFolder}*{commitSha}

Example:

https://github.com/HiStructClient*fcs-lab-01*SkyNetRecognizer2.fcc*b790f7f5da06ad41dee8cc5483fcd7e20f2eafbe

Important: Use the full SHA (40 chars). Short SHAs may not be cached in gitlocal yet, causing SnapshotPath errors.

Returns: {"success": true, "newPrototype": "..."}

List prototype commits

GET /api/spaces/{spaceKey}/components/{componentId}/template-commits?take=N

Returns recent commits for the component's prototype, useful for browsing version history.

Cache considerations

Source: ComponentTemplateController.cs:84-145 (GET/POST upgrade), ComponentTemplateController.cs:151 (commits)