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
- Use
Fcs.Web.RestClientwhen you want to call an API directly inside FCS evaluation code and immediately work with the response. - Use
InitByGetorInitByPostinside aValidationTriggerwhen the call should be initiated from the parameter UI, typically from anItemActionbutton. - Chain multiple triggers when one API call prepares data for the next step.
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
- Create a client with a stable
BaseAddress. - Call
GetResponseObject(...)with the relative path. - Read
response.payloadas a normal FCS object.
Best use cases
- Health checks such as
HelloWorld - Short metadata calls
- Small JSON lookups that do not need a UI button
Practical tips
- Keep the
BaseAddressending with/to avoid accidental URL concatenation bugs. - Use this pattern for object/JSON responses, not large binary payloads.
- If the call depends on user interaction, prefer a trigger-based flow so the user gets explicit feedback.
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
Variable— destination FCS variable nameCondition = False— unconditional trigger; run every time the action is pressedUrl— full URL, usually built from parametersMediaFormat = "object"— parse JSON into an FCS objectMediaFormat = "base64"— fetch binary/image content as base64 textExplanation— short status message shown to the user
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:
- Trigger 1 starts inference and stores the response object.
- Trigger 2 uses fields from that response to fetch derived artifacts.
- The final UI reads the already-populated variables.
This is usually easier to debug than one very large trigger.
Good pipeline shape
- Build input URL
- Run recognition
- Extract output URLs or IDs from response
- Download images or JSON artifacts
- 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
ValidationTriggerpoints to the variable containing the trigger definition.IsQuick = Truekeeps the interaction lightweight.IsAuthPost = Falseis appropriate for GET-based APIs.CssStyleClassis often used to keep the action visually consistent with menu buttons.
Rule of thumb
- GET endpoint ->
IsAuthPost = False - POST endpoint -> use
InitByPostand set the button accordingly
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:
- an API URL is itself passed as a query parameter
- image URLs contain
?,&, or= - you are composing callback or proxy endpoints
Practical advice
- Encode the value part, not the whole request template, when possible.
- Keep one reusable
urlEncoderlambda near the integration logic. - Be explicit about which characters your downstream service cares about.
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
- previewing downloaded segmentation masks
- showing map or aerial tiles
- comparing original image vs processed output
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
mapSizederived from site width/height or a square bounding sizeimageUrltaken from a REST response or static asset endpoint
Practical checks
- Ensure the texture URL is reachable by the client.
- Keep the plane origin/position aligned with your component coordinate system.
- Use a neutral base
groundColorso the overlay remains readable.
Real-world example: SkyNetRecognizer.fcc
A typical SkyNet-style workflow is not a single API call. It is a small pipeline:
- Construct the inference URL
- Trigger recognition from a button
- Read the JSON response into
reconResponse - Build follow-up URLs from the response
- Download result images such as normals or masks
- 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
- Build external URLs in one place.
- Encode nested URL parameters manually.
- Store JSON responses with
MediaFormat = "object". - Store image/binary responses with
MediaFormat = "base64". - Split long workflows into chained triggers.
- Use
ItemActionfor user-triggered execution. - Render quick previews with
ItemCommentHTML. - Reuse downloaded imagery as scene textures when useful.