Postman/Curl Samples
The following examples can be run with the curl command line
utility or imported into the Postman application
(File menu, Import, select Raw Text, paste the whole curl command, Continue,
review and replace the variable placeholders with actual values).
Command line setup for curl commands:
export TOKEN=<api token>
export BACKEND_HOST=backend.<your company>.fieldtwin.com
export PROJECT=<project id>
export SUBPROJECT=<subproject id>
export STREAM=<stream id>
The API token can be created by an administrator in FieldTwin Admin from the Account Settings / API section. It must be created for the same account that the project lives in. API tokens are convenient for testing but they are unrestricted by default, so once you are familiar with the API you might (depending on your use case) choose to use a JWT instead.
The IDs of the project, subproject and stream (and objects below) can be found from
the URL in your browser's address bar when the project is open in FieldTwin Design.
They look like this: -MeidQjcOmxpYWFIq5zp.

Create a manifold
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request POST \
--data '{
"name": "New Manifold #1",
"asset": "-K5uq-URmQV-aH0-uBpF",
"tags": [],
"initialState": {
"rotation": 0,
"x": 135277.5,
"y": 6565034.6,
"scale": 1
},
"metaData": []
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/stagedAsset
- Asset ID
-K5uq-URmQV-aH0-uBpFequates to a Manifold in FutureOn's standard asset library- The list of available assets can be found by calling
/API/v1.10/assets
- The list of available assets can be found by calling
- Coordinate values are provided inside the bounds and in the unit of the
Coordinate Reference System
defined for the project
- For example meters for EPSG:23031 or us-feet for EPSG:3452
- For subsea assets the
zvalue is automatically set from the bathymetry if uploaded, or else the seabed level defined for the project zvalues in FieldTwin are0at sea level, negative below sea level and positive above sea level
Create a connection
A draggable connection that follows the bathymetry in FieldTwin
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request POST \
--data '{
"params": {
"type": 2,
"label": "API connection"
},
"from": "-M8RhZWomNyqNyVgDxpy",
"fromSocket": "a",
"to": "-M8RhZWomNyqNyVgDxpm",
"toSocket": "a",
"designType": "None",
"intermediaryPoints": [
{ "x": 650, "y": 160, "z": -100 },
{ "x": 795, "y": 20, "z": -100 }
]
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection
params.typeof2is the connection type ID for Oil ProductionfromandfromSocketare the ID of an existing staged asset and the name of the socket to start the connection attoandtoSocketindicate the staged asset and location to end the connection at- The available socket names can be found from a staged asset's
sockets2dattribute
- The available socket names can be found from a staged asset's
designTypeofNoneindicates that the connection has no special geometry (as opposed to a lazy wave riser or a spool for example)intermediaryPointsdefines 2 midpoints that the connection will pass through- These will be shown as drag handles in FieldTwin that can be moved to adjust the connection
- If
designTypeis not specified it defaults toImported, which creates a different kind of connection (see next example)
A connection based on survey data
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request POST \
--data '{
"params": {
"type": 2,
"label": "API connection"
},
"from": "-M8RhZWomNyqNyVgDxpy",
"fromSocket": "a",
"to": "-M8RhZWomNyqNyVgDxpm",
"toSocket": "a",
"designType": "Imported",
"intermediaryPoints": [
{ "x": 782, "y": 303, "z": -20 },
{ "x": 830, "y": 240, "z": -100 },
{ "x": 920, "y": 170, "z": -50 }
],
"noHeightSampling": true,
"bendable": false,
"isLocked": true
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection
- The first few parameters are the same as described above
designTypeofImportedindicates that theintermediaryPointsare to be treated as the complete 3D profile of the connection- In reality the
intermediaryPointsarray will be much larger, with smaller changes between points - The
xyzpoints must be in the same Coordinate Reference System as the project - FieldTwin does not show drag handles for
Importedpoints
- In reality the
noHeightSamplinginstructs that the givenzpoints should be preserved instead of fitting the connection to the bathymetry in FieldTwinisLockedastrueprevents a user from accidentally adjusting the connection
Delete a staged asset
export STAGEDASSET=<staged asset id>
curl -H "token: ${TOKEN}" \
--request DELETE \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/stagedAsset/${STAGEDASSET}
Get a 3D well bore profile
export WELL=<well id>
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/well/${WELL}
- The bore profile is returned in the
wellBores[0 .. n].pathattribute - The bore path is empty by default, it can be set or imported from FieldTwin Design, or generated with the Well Assist integration
Get a generated seabed profile for a connection
export SAMPLE_RESOLUTION=5
export CONNECTION=<connection id>
curl -H "token: ${TOKEN}" \
-H "sample-every: ${SAMPLE_RESOLUTION}" \
-H "simplify: true" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection/${CONNECTION}
- The value of sample resolution can be
1or more - Providing
simplify: trueremoves points that fall in a straight line to reduce the data size - The connection profile is returned in the
sampledattribute - The profile points are given in the direction
fromtoto - You can also use the profile-tools package to extract connection profiles
Get the profile of an imported connection
export CONNECTION=<connection id>
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection/${CONNECTION}
- Imported connections (including those created with the API when no other
designTypeis given) generally have adesignTypeofImported - The imported points are made up from the
fromCoordinateplus theintermediaryPointsplus thetoCoordinate - When
noHeightSamplingis true, the pointz(depth) values are as they were at import time - When
noHeightSamplingis false, the pointz(depth) values are sampled from the bathymetry layer or else the project seabed depth setting - You can also use the profile-tools package to extract connection profiles
Note: for connections manually created (not imported) in FieldTwin Design, the intermediaryPoints
consist of auto-generated points along the connection (when the point has attribute added: true)
and/or the locations of the connection's midpoints - shown as circular drag handles in FieldTwin.
Get and set metadata values
Metadata values are stored in the metaData array attribute on objects in the field.
You can see the general data structure by requesting a single connection / staged asset / well / layer / shape:
export CONNECTION=<connection id>
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection/${CONNECTION}
-->
{
"visible": true,
"fromSocket": "b",
"toSocket": "a",
"params": {
"type": 4,
"label": "Water Injection #1",
"width": 1
},
"from": { ... },
"to": { ... },
...
"metaData": [
{
"id": "-NTruDmpGaWmFq5dY1UL",
"metaDatumId": "-MX1hgyA5rMYnffSIDqS",
"metaDatumLinkId": "-NTruDmpGaWmFq5dY1UL",
"definitionId": "IntegrationsTeam:CustomString[string]",
"name": "Custom String",
"type": "string",
"tags": [],
"value": "my string",
"cost": 0,
"costPerLength": false,
"vendorAttributes": {}
},
{
"id": "-NTruFFZ1QdcHPX7DsWg",
"metaDatumId": "-MKeJ9PrkJXvPLg9vYzB",
"metaDatumLinkId": "-NTruFFZ1QdcHPX7DsWg",
"definitionId": "IntegrationsTeam:KeyPoints[table]",
"name": "Key Points",
"type": "table",
"tags": [],
"value": [
{
"-MKeJHF9Q0jyb4AIwicf": "1",
"-MKeJIq3E-RffPBFbJdG": "100"
},
{
"-MKeJHF9Q0jyb4AIwicf": "2",
"-MKeJIq3E-RffPBFbJdG": "200"
}
],
"cost": 0,
"costPerLength": false,
"vendorAttributes": {}
}
]
}
At the time of writing (FieldTwin 7.x), requesting a whole subproject will return
metadata values for the connections, staged assets and shapes but not for wells and layers -
metadata for these must be fetched with separate API calls.
To add or change the metadata values for an object, PATCH the object by sending the
metaData array with only the metadata attributes to update. The minimum attributes to
send are the metaDatumId (identifying the relevant metadatum definition) and value.
The following example updates the 2 metadata values on the connection shown above.
export CONNECTION=<connection id>
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{
"metaData": [
{
"metaDatumId": "-MX1hgyA5rMYnffSIDqS",
"value": "Flexible"
},
{
"metaDatumId": "-MKeJ9PrkJXvPLg9vYzB",
"value": []
}
]
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection/${CONNECTION}
When a metadatum value has been cleared or has not been set, the
value attribute
will be missing from the metadatum object. In your code be sure to check that value exists
before reading from it. The data type and content of value varies depending on the
metadatum's type. The following types are supported:
string
To set a string, provide a string in value:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MHm9G9TaN4W_4jMYPHl",
"value": "value as a string"
}]}
numerical and slider
To set a numerical, provide an integer or a floating point number in value.
To set the unit (if applicable), specify the unit in the option attribute
(for example mm, ft3, C, sm3/m3, W/m2/C):
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MHm9G9TaN4W_4jMYPHl",
"value": 150.1,
"option": "m"
}]}
boolean
To set a boolean, provide the string true or false in value:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MHm9G9TaN4W_4jMYPHl",
"value": "true"
}]}
Depending on your use case, if the value attribute is missing (meaning that no
value has been saved) you may want to treat this as false.
choices - single selection
Use Request all metadata definitions
or Request one metadata definition
with the metaDatumId to fetch the metadatum definition and its list of possible choices.
Each choice is its own separate object with attributes for: id, name, and optionally
a customValue. The name is what is displayed in the user interface, and the custom value
(if present) is the value to use for the choice instead of its name.
When reading metadata, the value will be the selected choice object. For example:
GET object
-->
"metaData": [
{
"id": "-NTsQ5XGQctE7dDV5OrH",
"metaDatumId": "-MX1hgyA5rMYnffSIDqS",
"metaDatumLinkId": "-NTsQ5XGQctE7dDV5OrH",
"definitionId": "MyAccount:NumberOfSlots[choices]",
"name": "Number of slots",
"type": "choices",
"tags": [],
"value": {
"id": "-NTt33VQYMf-LD6VKZzR",
"name": "6 Slot",
"customValue": "6",
"filterValue": []
},
"cost": 0,
"costPerLength": false,
"vendorAttributes": {}
}
]
So the final metadata value to use here is 6, or it would be 6 Slot if there was
no customValue.
To set a choice, provide one of the choice objects in value.
You can provide just the id of the choice object if you prefer:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MX1hgyA5rMYnffSIDqS",
"value": { "id": "-NTt33VQYMf-LD6VKZzR" }
}]}
choices - multiple selection
This case is similar to choices - single selection, but the value is now an array
of choice objects. To clear all choices, apply a zero length array:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MX1hgyA5rMYnffSIDqS",
"value": []
}]}
Or to select 2 choices:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MX1hgyA5rMYnffSIDqS",
"value": [
{ "id": "-NTt33VQYMf-LD6VKZzR" },
{ "id": "-NTt34S9xlSnOcx-FAhy" }
]
}]}
asset
A type of asset allows for the selection of an asset from the asset library or a
virtual asset defined in FieldTwin Admin. All metadata values associated with the
selected asset will then be presented in FieldTwin and nested inside this metadatum
object in a new subValue attribute.
The value to store in the metadatum object is the ID of the asset definition
or the virtual asset. Request the list of allowable IDs using
Request the list of asset definitions.
To set an asset and create the nested subValue containing that asset's metadata,
provide the asset ID as a string in value:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MHm9G9TaN4W_4jMYPHl",
"value": "-M3LRYZGl2h0K902juxA"
}]}
connection
A type of connection allows for the selection of a connection type. All metadata
values associated with the selected connection type will then be presented in FieldTwin
and nested inside this metadatum object in a new subValue attribute.
The value to store in the metadatum object is the ID of the connection type.
Request the list of allowable IDs using
Request the list of connection definitions.
To set a connection type and create the nested subValue containing that connection's metadata,
provide the definition ID in value:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MHm9G9TaN4W_4jMYPHl",
"value": 4
}]}
At the time of writing (FieldTwin 7.x) the pre-installed connection types
have a numeric ID while custom connection types have a string ID.
table
Use Request all metadata definitions
or Request one metadata definition
with the metaDatumId to fetch the metadatum definition and its list of table headers.
Each table header / column in the definition has an id and name.
The metadatum value is an array of rows, where each row is a key/value object with
the column ID as its key.
To set a 2x2 table, provide the following array in value:
PATCH object
<--
{ "metaData": [{
"metaDatumId": "-MHm9G9TaN4W_4jMYPHl",
"value": [
{
"id-of-column-1": "col A row 1",
"id-of-column-2": "col B row 1"
},
{
"id-of-column-1": "col A row 2",
"id-of-column-2": "col B row 2"
}
]
}]}
button
Button metadata shows a button on the user interface that launches a web page when clicked.
It does not have a value and setting one has no effect.
Visualise custom data for a staged asset
Custom results are names and values displayed in a box next to the staged asset in FieldTwin Design. They can be used for displaying simulation results or operational data.
export STAGEDASSET=<staged asset id>
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{
"customResults": {
"Current": [
{"name": "Service", "value": "Production", "type": "string"},
{"name": "Valve 1 cycles", "value": 384, "type": "decimal"},
{"name": "Valve 2 cycles", "value": 410, "type": "decimal"},
{"name": "Current Pressure", "value": 5113, "type": "decimal"},
{"name": "Current Temp", "value": 60, "type": "decimal"}
]
},
"showCustomResults": true
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/stagedAsset/${STAGEDASSET}

To hide the custom results in FieldTwin Design:
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{ "showCustomResults": false }' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/stagedAsset/${STAGEDASSET}
Visualise custom data along a connection
Connection visualisation data is displayed at key points on or alongside a connection in FieldTwin Design. This can be used for displaying simulation results, survey results or operational data.
export CONNECTION=<connection id>
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{
"visualisationMaps": [{
"id": "connection-1-custom-temp",
"name": "Temperature Survey",
"normalizedKp": false,
"rendering": "bar",
"data": [
{"kp": 100, "v": 68},
{"kp": 200, "v": 67},
{"kp": 300, "v": 66},
{"kp": 400, "v": 65},
{"kp": 500, "v": 64}
]
}],
"visibleVisualisationMapId": "connection-1-custom-temp"
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection/${CONNECTION}
- The visualisation data is provided as an array of key points
kpas a distance along the connection and a corresponding numeric valuev - When
normalizedKpis true, the key pointkpis a value between0and1instead of a value in feet/meters (so0.5would be half way along the connection) - A rendering of:
bardisplays the visualisation alongside the connectionon-connectiondisplays the visualisation inside the connection- See the documentation for other possible values
- A palette for the visualisation can also be provided, but a default palette is generated by default


To hide the visualisation in FieldTwin Design:
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{ "visibleVisualisationMapId": null }' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection/${CONNECTION}
Store custom data at project level
docs link (write)
docs link (read)
FieldTwin provides the vendorAttributes attribute on many objects including account,
project, subproject, staged asset, connection, and document. This can be used to attach
custom data to the object. There is no built-in visualisation of this data.
Because vendorAttributes can be used by more than one integration or for more than one
purpose, we use a convention of storing data objects inside a child key to keep the different
uses separate. The API will merge the provided top-level key(s) into the vendorAttributes
without overwriting other existing top-level keys.
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{ "vendorAttributes": { "myIntegration": { "foo": "bar" } } }' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}
Example of data separation using keys:
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{ "vendorAttributes": { "drillPro": { "settings": { "name": "Drill Pro v1" } } } }' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{ "vendorAttributes": { "heatCalc": { "scenario": "Base Case", "autoCalc": true, "pipes": ["c-1", "c-2", "c-3"] } } }' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/vendorAttributes
-->
{
"drillPro": {
"settings": {
"name": "Drill Pro v1"
}
},
"heatCalc": {
"scenario": "Base Case",
"autoCalc": true,
"pipes": ["c-1", "c-2", "c-3"]
}
}
- Only one level of keys is merged into
vendorAttributes- In the above example, to add something new into
drillProyou have to PATCH the wholedrillProobject a second time
- In the above example, to add something new into
- To delete one set of
vendorAttributes, send PATCH data of the format:{ "vendorAttributes": { "myIntegration": null } }
Request linked (parent/child) subprojects
By default, requesting a child subproject returns the sum of objects from both the parent
subproject(s) and the child. Objects that are automatically merged from a parent subproject
have a different subProject value and are given additional attributes in the JSON:
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}
-->
{
"id": "-MjF6TYxJ-mkAzWnnsfa",
"name": "Child Subproject",
"stagedAssets": {
"-MkaNgEfqAYhqyhQHNsz": {
"name": "Local FPSO #1",
"visible": true,
"subProject": "-MjF6TYxJ-mkAzWnnsfa"
},
"-MjF4vtXQ574Bid3Zbs2": {
"name": "XMT from parent backdrop",
"visible": true,
"subProject": "-MjF3wciX_dTQzLL4aOu",
"project": "-MjF3L9vJdSXSOiYZiMQ",
"isForeign": true,
"getFromSubProject": "https://example.fieldtwin.com/API/v1.10/-MjF3L9vJdSXSOiYZiMQ/subProject/-MjF3wciX_dTQzLL4aOu",
"getFrom": "https://example.fieldtwin.com/API/v1.10/-MjF3L9vJdSXSOiYZiMQ/subProject/-MjF3wciX_dTQzLL4aOu/stagedAsset/-MjF4vtXQ574Bid3Zbs2"
}
}
}
If you wish to request a child subproject without merging the items from the parent(s),
set the merge-foreign request header to false. The parent objects are then returned
in a separate list of foreignSubProjects.
curl -H "token: ${TOKEN}" \
-H "merge-foreign: false" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}
-->
{
"id": "-MjF6TYxJ-mkAzWnnsfa",
"name": "Child Subproject",
"stagedAssets": {
"-MkaNgEfqAYhqyhQHNsz": {
"name": "Local FPSO #1",
"visible": true,
"subProject": "-MjF6TYxJ-mkAzWnnsfa"
}
},
"foreignSubProjects": [
{
"id": "-MjF3wciX_dTQzLL4aOu",
"name": "Parent Project 1",
"stagedAssets": {
"-MjF4vtXQ574Bid3Zbs2": {
"name": "XMT from parent backdrop",
"visible": true,
"subProject": "-MjF3wciX_dTQzLL4aOu",
"project": "-MjF3L9vJdSXSOiYZiMQ",
"isForeign": true
}
}
}
]
}
Requesting a parent subproject returns only the objects that live in the parent.
Use Smart Models
Smart 3D models consist of special 3D assets that can be combined together as components to create a customized staged asset. The customizations are controlled by setting special metadata attributes.
To use smart models, ask FutureOn to deploy the required smart assets and associated metadata definitions into your Asset Library.
When a staged asset is created from a smart model in FieldTwin, its "docking slots" will
be made available as metadata values with "type": "asset".
Wind turbine smart models (2024 onwards)
Configurable Wind smart models are included in FutureOn's standard asset library as of 2024. The workflow for creation of a turbine is:
- Create a WindTool Construction Base staged asset
- Set its metadata value for turbine to one of the allowed turbine assets
- Set its metadata value for foundation to one of the allowed foundation assets
Following step 1 the resulting data can be retrieved as follows:
export STAGEDASSET=<staged asset id>
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/stagedAsset/${STAGEDASSET}
-->
{
"id": "-NtWdCF0msKLrkbwhSgN",
"name": "WindTool Construction Base #1",
"visible": true,
"virtual": false,
...
"metaData": [
{
"metaDatumId": "-NtWDOfryzi8OEwdm7I9",
"definitionId": "WindPOC:5MWWT[asset]",
"name": "5MW WT",
"type": "asset",
"tags": [],
"cost": 0,
"metaDatumLinkId": "-NtWDrplqhtzDTrqM7Z4",
"id": "-NtWDrplqhtzDTrqM7Z4",
"subValue": []
},
{
"metaDatumId": "-Nky37U2lazdWHzMXQAs",
"definitionId": "WindPOC:5MWFoundations[asset]",
"name": "5MW Foundations",
"type": "asset",
"tags": [],
"cost": 0,
"metaDatumLinkId": "-NtWP2c3UY_EwEOP-Ymq",
"id": "-NtWP2c3UY_EwEOP-Ymq",
"subValue": []
}
],
"sockets2d": []
}
After fetching the newly created Construction Base, two metadata objects are returned:
one for the wind turbine (WT) and the other for the turbine foundation. Neither has a value
attribute in the metaData object - the docking slots are empty. To set them we need to set
the value to the ID of an allowable asset from the asset library (note: use the ID of an asset
definition, not a staged asset in the project):
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{
"metaData": [{
"metaDatumId": "-NtWDOfryzi8OEwdm7I9",
"value": "-N4R5TPAOGih17IYVZm3"
}, {
"metaDatumId": "-Nky37U2lazdWHzMXQAs",
"value": "-N4RytdCUlxy8oJSdDIv"
}]
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/stagedAsset/${STAGEDASSET}
The above call will set both a turbine body and a foundation in the base's docking slots.
To find the allowable asset IDs for a docking slot, first request the metadata definition
using the metadata definition ID in metaDatumId:
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/metadatadefinitions/-Nky37U2lazdWHzMXQAs
-->
{
"id": "-Nky37U2lazdWHzMXQAs",
"name": "5MW Foundations",
"definitionId": "WindPOC:5MWFoundations[asset]",
"cost": 0,
"order": 200000,
"global": true,
"public": false,
"options": {
"unit": {},
"filter": {
"assetTypes": ["vessel"],
"assetSubTypes": ["WindTurbine"],
"assetCategories": ["Wind"],
"assetSubCategories": ["5MW Floater", "5MW Jacket", "5MW MonoPile"]
},
"docking": {}
},
"standard": true,
"clonedFroms": ["-Nky37U2lazdWHzMXQAs"],
"displayIfConditions": [],
"type": "asset"
}
Then use the filters given in options.filter to find asset definitions in the asset
library that match the filters:
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/assets
-->
{
...
"-N4R5mVs3w46MbHGqByC": {
"id": "-N4R5mVs3w46MbHGqByC",
"name": "5MW Floater",
"params": {...},
"shared": true,
"type": "vessel", // match on filter.assetTypes
"subType": "WindTurbine", // match on filter.assetSubTypes
"category": "Wind", // match on filter.assetCategories
"subCategory": "5MW Floater", // match on filter.assetSubCategories
"imageUrl": "...",
"sockets2d": [...],
"model3dUrl": "...",
"dockingMales": [],
"dockingFemale": {...},
"filename2D": "5MW_Floater.png - Tue, 20 Feb 2024 12:03:19 GMT",
"filename3D": "5MW_Floater180.glb - Tue, 20 Feb 2024 11:50:32 GMT",
"filenameSockets": "5MW_Floater180.sockets - Tue, 20 Feb 2024 11:55:09 GMT",
"hideInAssetLibrary": true
},
"-N4RytdCUlxy8oJSdDIv": {
"id": "-N4RytdCUlxy8oJSdDIv",
"name": "5MW Jacket",
"params": {...},
"shared": true,
"type": "vessel", // match on filter.assetTypes
"subType": "WindTurbine", // match on filter.assetSubTypes
"category": "Wind", // match on filter.assetCategories
"subCategory": "5MW Jacket", // match on filter.assetSubCategories
"imageUrl": "...",
"sockets2d": [],
"model3dUrl": "...",
"dockingMales": [],
"dockingFemale": {...},
"filename2D": "5MW_Jacket.png - Mon, 13 Jun 2022 12:10:47 GMT",
"filename3D": "5MW_Jacket.glb - Mon, 13 Jun 2022 12:01:57 GMT",
"filenameSockets": "5MW_Jacket.sockets - Mon, 13 Jun 2022 12:11:05 GMT",
"hideInAssetLibrary": true
},
"-N4Rz5E2U88Qoq02TLF2": {
"id": "-N4Rz5E2U88Qoq02TLF2",
"name": "5MW MonoPile",
"params": {...},
"shared": true,
"type": "vessel", // match on filter.assetTypes
"subType": "WindTurbine", // match on filter.assetSubTypes
"category": "Wind", // match on filter.assetCategories
"subCategory": "5MW MonoPile", // match on filter.assetSubCategories
"imageUrl": "...",
"sockets2d": [],
"model3dUrl": "...",
"dockingMales": [],
"dockingFemale": {...},
"filename2D": "5MW_MonoPile.png - Mon, 13 Jun 2022 12:09:44 GMT",
"filename3D": "5MW_MonoPile.glb - Wed, 22 Jun 2022 11:24:37 GMT",
"filenameSockets": "5MW_MonoPile.sockets - Wed, 22 Jun 2022 11:28:23 GMT",
"hideInAssetLibrary": true
},
...
}
In this example the allowable values for the 5MW Foundations docking slot in the
parent staged asset are:
-N4R5mVs3w46MbHGqByCto create a5MW Floaterfoundation in the slot, or-N4RytdCUlxy8oJSdDIvto create a5MW Jacketfoundation, or-N4Rz5E2U88Qoq02TLF2to create a5MW MonoPilefoundation, ornullto set the docking slot as empty
Connecting a connection to a docking slot
Most assets in FutureOn's asset library define one or more "sockets" that allow a connection to be connected to it. Assets that make up the swappable components of a smart model may also define sockets.
In the example of how to create a connection you can see that a connection is attached to a regular staged asset by settings these attributes:
{
"to": "<id of a staged asset>",
"toSocket": "<name of a socket in stagedAsset.sockets2d>"
}
However the item in a docking slot of a smart model is not a staged asset, it exists
only as a metadata value. To connect a connection to an item in a docking slot we have
to set the connection's to and toSocket differently.
In the previous smart model example, after creating the Construction Base and assigning a wind turbine body in its docking slot, the staged asset is the Construction Base and its data is as follows:
export STAGEDASSET=<staged asset id>
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/stagedAsset/${STAGEDASSET}
-->
{
"id": "-NtWdCF0msKLrkbwhSgN",
"name": "WindTool Construction Base #1",
"visible": true,
"virtual": false,
...
"metaData": [
{
"metaDatumId": "-NtWDOfryzi8OEwdm7I9",
"definitionId": "WindPOC:5MWWT[asset]",
"name": "5MW WT",
"type": "asset",
"tags": [],
"cost": 0,
"value": "-N4R5TPAOGih17IYVZm3",
"metaDatumLinkId": "-NtWDrplqhtzDTrqM7Z4",
"id": "-NtWDrplqhtzDTrqM7Z4",
"subType": "WindTurbine",
"category": "Wind",
"subCategory": "5MW WT",
"subValue": [
... nested metadata for the wind turbine ...
]
},
{
... metadata that sets the foundation ...
}
],
"sockets2d": [
{
"x": 1.958685174628093,
"y": 1.1513813644766508,
"z": -3.017566015066542,
"name": "-NuZOI2drjETshKe6zQw:001",
"generated": true,
"assetId": "-N4R5TPAOGih17IYVZm3",
"radius": 0.25,
"metaData": []
},
{
"x": -1.974785545371426,
"y": 1.1513827399763774,
"z": -3.017567390566267,
"name": "-NuZOI2drjETshKe6zQw:002",
"generated": true,
"assetId": "-N4R5TPAOGih17IYVZm3",
"radius": 0.25,
"metaData": []
}
]
}
Since 5MW WT was assigned in the metaData the base staged asset has gained 2 entries
in its sockets2d attribute, both marked as "generated": true. These 2 sockets are defined
on the turbine body asset (indicated by "assetId": "-N4R5TPAOGih17IYVZm3") and they have
now been inherited by the parent staged asset and given a generated name.
To connect an existing mooring line to the turbine body in the docking slot, update the
mooring line, setting to as the base staged asset's ID and toSocket as one of the
generated socket names.
export CONNECTION=<mooring line id>
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request PATCH \
--data '{
"to": "-NtWdCF0msKLrkbwhSgN",
"toSocket": "-NuZOI2drjETshKe6zQw:001"
}' \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/connection/${CONNECTION}
To connect the connection in the opposite direction, set from and fromSocket.
Create a metadata definition with variable choices
Sometimes a metadata attribute defined as a 'list of choices' needs to present different options depending on context:
- Choice A (always relevant)
- Choice B (relevant only for some use cases)
- Choice C (relevant only for different use cases)
- ...
One way to achieve this is to create 2 or more definitions, each with a different list of choices. Metadata conditions can then be defined to show or hide the appropriate definition so that only one is visible at a time. This is appropriate if it is correct for the definition ID and selected choice to be separate for each use case.
If what is needed is a single definition, common to all use cases, we instead need to vary the list of allowed choices at runtime. This can be set up in FieldTwin Admin as long as the context can be determined from another metadata attribute.
Firstly a metadata definition is needed to provide the context or use case. It needs to be of type
choices. This example will determine whether a pipe is Rigid or Flexible:

Then when defining a list of choices elsewhere we can use that context to determine which choices are made available to the user:

The same configuration can be created with the API.
From the "context" definition the data required are its id and the id of its choices
(found in options.items.list[].id).
export CONTEXT_METADATA_DEFINITION_ID=<metadatum id>
curl -H "token: ${TOKEN}" \
https://${BACKEND_HOST}/API/v1.10/metadatadefinitions/${CONTEXT_METADATA_DEFINITION_ID}
-->
{
"id": "-MESBTJyZFpm2VLXdK-z",
"account": "-MIyPbG_cjUjO7E3ZgzZ",
"name": "Pipe Type",
"type": "choices",
"tags": [],
"global": false,
"public": false,
"shouldFilterChoices": false,
"options": {
"items": {
"list": [
{ "id": "-NwxOrTr5fTh95O5vQl5", "name": "Flexible", "filterValue": [] },
{ "id": "-NwxOtAL47e_Q52uFnWU", "name": "Rigid", "filterValue": [] }
],
"default": { "id": "-NwxOrTr5fTh95O5vQl5", "name": "Flexible", "filterValue": [] }
}
},
"vendorId": "pipe.flex",
"definitionId": "IntegrationsTeam:Pipe.Flex[choices]"
}
The variable list of choices is created using the API as follows:
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request POST \
--data '{
"name": "Pipe Insulation",
"type": "choices",
"global": false,
"public": false,
"vendorId": "pipe.insulation",
"shouldFilterChoices": true,
"filterIf": "-MESBTJyZFpm2VLXdK-z",
"options": {
"items": {
"list": [
{
"name": "None",
"filterValue": []
},
{
"name": "Common insulation",
"filterValue": []
},
{
"name": "Flexible insulation",
"filterValue": [{ "id": "-NwxOrTr5fTh95O5vQl5" }]
},
{
"name": "Rigid insulation 1",
"filterValue": [{ "id": "-NwxOtAL47e_Q52uFnWU" }]
},
{
"name": "Rigid insulation 2",
"filterValue": [{ "id": "-NwxOtAL47e_Q52uFnWU" }]
}
]
}
}
}' \
https://${BACKEND_HOST}/API/v1.10/metadatadefinitions
shouldFilterChoicesis set astrue- The ID of the "context" definition is set in
filterIf - Each choice has a filterValue that takes zero or more objects
- The object provides the
idof one of the choices to match from the "context" definition- For example,
-NwxOtAL47e_Q52uFnWUis the ID ofRigid
- For example,
- The object provides the
- An empty
filterValuearray means that choice is always allowed (it is not filtered) - With one value in
filterValue, that choice will only be allowed for the one matching context - With multiple values in
filterValue, that choice will be allowed for any of the matching contexts
The result in FieldTwin Design:

Upload a document and link it to multiple objects
Typically when uploading a document it will be attached to an object in a subproject by providing a
value for relateToType and relateToId. It is possible to attach one document to multiple objects
in the subproject by making a series of API calls and providing the same documentGroupId. This
requires the use of the lower level POST documents endpoint that deals with separately uploaded files.
export CREATOR=you@yourcompany.com
export FILENAME=somefile.pdf
export FILETYPE=application/pdf
export STAGEDASSET1=<staged asset id 1>
export STAGEDASSET2=<staged asset id 2>
export GROUPID=group1
curl -H "token: ${TOKEN}" \
--request PUT \
--form creator=${CREATOR} \
--form file=@${FILENAME} \
--form relateToType=stagedAssets \
--form relateToId=${STAGEDASSET1} \
--form documentGroupId=${GROUPID} \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/documents
-->
{"urls":{"somefile.pdf":"https://storage.googleapis.com/ft-bucket/documents/generic/04f58292e449ff/somefile.pdf?GoogleAccessId=..."},
"documentIds":{"somefile.pdf":"-OIpYO4Du1dclRwVl_gF"},"revisionIds":{"somefile.pdf":"-OIpYO53hP_8vJCSMRBM"}}
FILEURL="https://storage.googleapis.com/ft-bucket/documents/generic/04f58292e449ff/somefile.pdf?GoogleAccessId=..."
POSTDATA=$(cat <<EOF
{
"creator": "${CREATOR}",
"fileName": "${FILENAME}",
"fileType": "${FILETYPE}",
"url": "${FILEURL}",
"relateToType": "stagedAssets",
"relateToId": "${STAGEDASSET2}",
"documentGroupId": "${GROUPID}"
}
EOF
)
curl -H "token: ${TOKEN}" \
-H "content-type: application/json" \
--request POST \
--data "${POSTDATA}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/documents
-->
{"urls":{"somefile.pdf":"https://storage.googleapis.com/ft-bucket/documents/generic/04f58292e449ff/somefile.pdf?GoogleAccessId=..."},
"documentIds":{"somefile.pdf":"-OIps0wZM3a0xLeFF_ci"},"revisionIds":{"somefile.pdf":"-OIps0wZM3a0xLeFF_cj"}}
- The initial file upload is a PUT request containing form data that must be sent with
multipart/form-dataencoding and includes the file name and file content - The second API call is a POST request with a regular JSON payload that creates a new document record
for the existing file
- This can be repeated to attach the same file to further objects
- The
documentGroupIdremains the same
- It is possible to query all the documents in a document group (for example to delete one or all of them)
via the API call
GET /API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}/documents?documentGroupId=${GROUPID}
Provide a JWT instead of an API token
A JWT is passed to an integration in the loaded window message
and optionally in the request parameters of an integration's URL.
Unlike an API token, a JWT is restricted to the current user's permissions for
the open project, it does not allow account administration, and it expires after
1 hour (by default). Listen out for the tokenRefresh window message to receive
a new JWT before the old one expires.
You should replace the Token header with an Authorization header; do not provide both.
export JWT=<jwt value>
curl -H "Authorization: Bearer ${JWT}" \
https://${BACKEND_HOST}/API/v1.10/${PROJECT}/subProject/${SUBPROJECT}:${STREAM}
- For integration user interfaces and settings pages you should use the provided JWT whenever possible
- API tokens are suitable for back-end tasks when there is no user session or when administration permission is required
Upgrading from previous versions of FieldTwin
7.x to 8.0
In the 8.0 release we introduced a new backend architecture that allows projects to be branched
and merged using git style workflows. In order to support multiple branches of one project
we have introduced a new ID named streamId in the API and in window messages and the JWT.
To keep the window messages and API v1.9 compatible with this change, the subproject ID in 8.0 is
now composed of 2 parts: subProjectId:streamId, however the API will continue to accept the older
form of subProjectId as well.
Content of JWT in 7.1:
{
"userEmail": "matt@futureon.com",
"accountId": "-MIyPbG_cjUjO7E3ZgzZ",
"projectId": "-M-HHqMifhz6qskW2goc",
"subProjectId": "-NlT9gazCfieinkJYsOv",
}
Content of JWT in 8.0:
{
"userEmail": "matt@futureon.com",
"accountId": "-MIyPbG_cjUjO7E3ZgzZ",
"projectId": "-M-HHqMifhz6qskW2goc",
"subProjectId": "-NlT9gazCfieinkJYsOv:-NlT9gbQQVEiFVxlL2_U",
"streamId": "-NlT9gbQQVEiFVxlL2_U",
}
Since the API accepts either subProjectId or subProjectId:streamId, existing code that calls the
API using jwt.subProjectId or window message message.subProject should continue to work unchanged.
Compatibility issues
- If you use the subproject ID as a key to find other records you will need to make this change to convert the ID to the legacy value:
subProjectId = subProjectId.split(':')[0]
- In FieldTwin 8.1 and beyond, if it is appropriate to store your data at branch level,
use the full
subProjectId:streamIdor incorporate the newstreamIdseparately - API v1.8 and below has been removed
- The JWT
userRightsnow includescanViewDocumentsandcanEditDocumentsand the API now enforces them - Invalid layers (e.g. unsupported files) in a subproject used to be ignored. To avoid ambiguous
data, failure to load a layer now causes all API calls for that subproject to return an error.
- To fix this: open the subproject in Design, view the Layers in the Project View tab, find the layers that have a red warning triangle and delete them