REST Integration Patterns

This guide shows the common ways FCS components call external services, especially long-running inference APIs such as SkyNet.

When to use which pattern


Fcs.Web.RestClient

Fcs.Web.RestClient is the simplest synchronous pattern for calling a JSON API from FCS.

skynetClient := Fcs.Web.RestClient{
   BaseAddress := "https://skynet-roof-recon-v1.azurewebsites.net/api/"
}
response = skynetClient.GetResponseObject("HelloWorld?name=test")
# response.payload contains the JSON response as FCS object

How it works

  1. Create a client with a stable BaseAddress.
  2. Call GetResponseObject(...) with the relative path.
  3. Read response.payload as a normal FCS object.

Best use cases

Practical tips


InitByGet — async REST calls via constraint triggers

For user-driven actions, FCS commonly performs REST calls through a ValidationTrigger. This is the normal pattern for buttons in the parameter UI.

recognitionTrigger = {
   Constraints = [
      {
         Variable = "reconResponse",       # FCS variable to populate
         Condition = False,                 # always trigger (False = unconditional)
         InitByGet = {
            Url = url,                      # full URL to call
            MediaFormat = "object"          # "object" = JSON→FCS object, "base64" = binary→base64 string
         },
         Explanation = "Recognition done!"  # shown to user in UI
      }
   ],
}

Why this pattern matters

InitByGet lets a button click populate a variable without rebuilding the whole page workflow manually. The response becomes part of the component state and can be consumed by later constraints or rendered in the UI.

Key fields

Typical usage on buttons

Use this inside a variable referenced by Fcs.Parameter.ItemAction.ValidationTrigger.


Chaining triggers (pipeline pattern)

A common FCS integration pattern is to split work into multiple small REST steps and execute them in order.

processMapImageTrigger = {
   Constraints = [
      recognitionTrigger.Constraints,         # first: run recognition
      downloadNormalsTrigger.Constraints       # then: download results
   ],
}

Why chain triggers?

This keeps each step focused:

This is usually easier to debug than one very large trigger.

Good pipeline shape

  1. Build input URL
  2. Run recognition
  3. Extract output URLs or IDs from response
  4. Download images or JSON artifacts
  5. Render previews and 3D overlays

ItemAction buttons

Buttons in the parameter panel are the normal entry point for REST-triggered workflows.

Fcs.Parameter.ItemAction{
   HumanName = "Run Recognition",
   ValidationTrigger = "recognitionTrigger",
   IsQuick = True,          # no page reload
   IsAuthPost = False,      # GET not POST
   CssStyleClass = "fcs-parameterItem-inMenuButton"
}

Important flags

Rule of thumb


URL encoding in FCS

FCS does not provide a built-in URL encoder, so URL-safe strings are typically created with chained Replace(...) calls.

urlEncoder = url => url.Replace("?", "%3F").Replace("/", "%2F").Replace(":", "%3A").Replace("&", "%26").Replace("=", "%3D")

When this is needed

You usually need manual encoding when:

Practical advice

Example:

encodedRgbUrl = urlEncoder(inputRgbUrl)
url = apiBase + "RunRecognition?input_rgb=" + encodedRgbUrl

Embedding images in parameter UI

A fast way to show API results is to emit HTML and render it through an ItemComment.

mapImageHtml = "<img src='" + imageUrl + "' width='256' style='padding: 5px;' />"
# Use Fcs.Parameter.ItemComment{Identifier="mapImageHtml"} to render HTML in form

Typical pattern

mapImageHtml = "<img src='" + imageUrl + "' width='256' style='padding: 5px;' />"

Fcs.Parameter.ItemComment{
   HumanName = "Map Preview",
   Identifier = "mapImageHtml"
}

Best use cases

Caution

Build HTML from trusted URLs only. This pattern is convenient, but it should still be treated as UI rendering logic.


Environment ground textures (map overlay in 3D view)

A downloaded image can also be placed into the 3D scene as a textured ground plane.

env = {
   ground = { type = "plain", level = -1, groundColor = {R=240,G=240,B=240} },
   otherGrounds = {
      map = {
         type = "plain",
         size = { x = mapSize, y = mapSize },
         position = { x = -mapSize/2, y = -mapSize/2 },
         texture = { size = {x=mapSize, y=mapSize}, url = imageUrl }
      }
   }
}

Why this pattern is useful

It lets the user see modeled geometry against the original source imagery in the 3D view.

Common inputs

Practical checks


Real-world example: SkyNetRecognizer.fcc

A typical SkyNet-style workflow is not a single API call. It is a small pipeline:

  1. Construct the inference URL
  2. Trigger recognition from a button
  3. Read the JSON response into reconResponse
  4. Build follow-up URLs from the response
  5. Download result images such as normals or masks
  6. Display results in both the parameter UI and the 3D environment

Step 1: build the recognition URL

urlEncoder = x => x.Replace("?", "%3F").Replace("/", "%2F").Replace(":", "%3A").Replace("&", "%26").Replace("=", "%3D")

encodedRgb = urlEncoder(inputRgbUrl)
encodedBbox = urlEncoder(inputBboxUrl)
apiBase = "https://skynet-roof-recon-v1.azurewebsites.net/api/"
recognitionUrl = apiBase + "RunRecognition?input_rgb=" + encodedRgb + "&input_bbox=" + encodedBbox

The important point is that the remote service receives full source URLs, so those values must be encoded manually before concatenation.

Step 2: recognition trigger

recognitionTrigger = {
   Constraints = [
      {
         Variable = "reconResponse",
         Condition = False,
         InitByGet = {
            Url = recognitionUrl,
            MediaFormat = "object"
         },
         Explanation = "Recognition done!"
      }
   ]
}

At this point reconResponse becomes a structured FCS object containing fields returned by the inference API, such as timestamps, IDs, or artifact URLs.

Step 3: follow-up download trigger

The next trigger typically reads fields from reconResponse.payload and downloads one or more artifacts.

downloadNormalsUrl = apiBase
   + "GetRecognitionNormals?timestamp=" + reconResponse.payload.timestamp
   + "&id=" + reconResponse.payload.id

downloadNormalsTrigger = {
   Constraints = [
      {
         Variable = "normalsBase64",
         Condition = False,
         InitByGet = {
            Url = downloadNormalsUrl,
            MediaFormat = "base64"
         },
         Explanation = "Normals downloaded!"
      }
   ]
}

This second step is where the pipeline pattern shines: the first call gives identifiers, the second call fetches actual result media.

Step 4: chain the workflow

processMapImageTrigger = {
   Constraints = [
      recognitionTrigger.Constraints,
      downloadNormalsTrigger.Constraints
   ]
}

A single button can now execute the whole flow in order.

Step 5: expose the pipeline to the user

Fcs.Parameter.ItemAction{
   HumanName = "Run Recognition",
   ValidationTrigger = "processMapImageTrigger",
   IsQuick = True,
   IsAuthPost = False,
   CssStyleClass = "fcs-parameterItem-inMenuButton"
}

Step 6: display the results

Once the variables are populated, the component can render them in multiple ways.

In the parameter UI

normalsImageUrl = "data:image/png;base64," + normalsBase64
normalsHtml = "<img src='" + normalsImageUrl + "' width='256' style='padding: 5px;' />"

Fcs.Parameter.ItemComment{
   HumanName = "Normals Preview",
   Identifier = "normalsHtml"
}

In the 3D environment

env = {
   ground = { type = "plain", level = -1, groundColor = {R=240,G=240,B=240} },
   otherGrounds = {
      recognitionMap = {
         type = "plain",
         size = { x = mapSize, y = mapSize },
         position = { x = -mapSize/2, y = -mapSize/2 },
         texture = { size = {x=mapSize, y=mapSize}, url = normalsImageUrl }
      }
   }
}

Full flow summary

input URLs -> manual URL encoding -> RunRecognition trigger -> JSON response object -> result download trigger -> HTML preview + textured 3D ground

That is the core external-integration recipe used by SkyNet-style components.


Recommended integration checklist