Developer

Attachments APIs

What are Attachments?

Attachments allow the user to pair together two elements so that they act as a single, interactable object.

In a Bluescape Workspace, a user has the ability to manually drag an element A on top of an element B in order to form a link between the two, to group them. Now whenever the user selects or moves one element, the other will be selected and moved as well. The Attachments APIs allow for a programmatic way to perform these attachment operations.

Coordinates can be specified in order to determine where the element to attach will be placed on the base element. It is important to note that when providing coordinates, these inputs will be relative to the base element they are being attached to. For example, if the user specifies the element to be attached at [0,0] this will place the attachment at the top left corner of the base element, and not at the center of the workspace.

When executing an Attachments API, the user will see the source element be moved onto the base element specified. When deleting an Attachment (or detaching), the user will see the detached element in the same position, but it is not attached to the surface.
NOTE: The object to which an element is attached is referred as the surface in this page.

Please review the page for attachment APIs in v2.

Table of Contents:

Allowed Elements and Surfaces for Attachments
How to Implement Attachments APIs: REST APIs
1. Create: attachment an element to another element, REST
2. Get: list all attachments of an element, REST
3. Detach: remove attachment of an element, REST
How to Implement Attachments APIs: GraphQL APIs
A. Create: attachment an element to another element, GraphQL
B. Get: list all attachments of an element, GraphQL
C. Detach: remove attachment of an element, GraphQL

Allowed Elements and Surfaces for Attachments

The elements to which you can attach other elements are in the following list:

Attachable objects Surfaces (elements to which attachable object can attach to)
  • Text
  • LegacyNote
  • Image
  • Document
  • Browser
  • Video
  • Stroke
  • Shape
  • Line
  • Window
  • Whiteboard
  • LinkedDocument
  • Browser
  • Document
  • Image
  • Note
  • Shape
  • Video
  • Window

How to Implement Attachments APIs: REST APIs

Create: attach one element to another, REST

Endpoint /v3/workspaces/<workspace_uid>/elements/<surface_id>/attachments
Method POST
Comments Specify in the payload the Id of the element you want to attach to, the element indicated with <surface_id>.

Sample Request:

var request = require('request');

const token = '<SET_TOKEN>';
const portal = '';
const workspace_uid = '<SET_WORKSPACE_UID>';
const api_version = 'v3';
var api_endpoint = '/' + api_version + '/workspaces/' + workspace_uid + '/elements/';

function runRequest(portal,api_endpoint,method,data_load) {
    return new Promise((resolve, reject) => {
        var request_values = {
            uri : portal + api_endpoint ,
            method : method ,
            headers : {
                'Authorization': "Bearer " + token,
                'Content-Type' : 'application/json'    
            },
            body : data_load,
            json: true
        };

        request(request_values, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                // Print out the result
                console.log("Successful request. Body response: " + JSON.stringify(body,null,"     ")); 
                } else {
                reject('Invalid status code <' + response.statusCode + '>');
                }
                resolve(body);
        })

    });
}

async function runAPIRequests() {
    api_endpoint = '/v3' + '/workspaces/' + workspace_uid + '/elements/<SET_SURFACE_ID>/attachments'

    data_load = {
        'attachment': "<SET_SOURCE_ID>",
        'transform' : {'x':10, 'y': 10}
    }

    method = 'POST';

    try {
        var uploadResponse = await runRequest(portal,api_endpoint,method,data_load);
    } catch (error) {
        console.error('ERROR  attaching ');
        console.error(error);
    }
}
runAPIRequests();
    
import requests
from requests.exceptions import HTTPError
import pprint

token = '<SET_TOKEN>'
portal = ''

workspace_uid = '<SET_WORKSPACE_UID>'
api_version = '/v3/'

if __name__ == "__main__":

    API_endpoint = API_endpoint = portal + api_version + 'workspaces/' + workspace_uid + '/elements/<SET_SURFACE_ID>/attachments'

    data_load = {
        'attachment': '<SET_SOURCE_ID>',     # Example showing how to use the filter_by field
        'transform' : {
            'x' : 10,
            'y' : 10
        }
    }

    the_request = requests.post(
            API_endpoint,
            headers={"Authorization": "Bearer " + token,
                        "Content-Type": "application/json"
                    },
            json = data_load
        )

    json_response = the_request.json()
    pprint.pprint(json_response)
curl -X POST /v3/workspaces/<SET_WORKSPACE_ID>/elements/<SET_SURFACE_ID>/attachments \
-H 'Authorization: Bearer <SET_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
    "attachment" : "<SET_ID_OF_ELEMENT_TO_ATTACH_TO_SURFACE_ID>",
    "transform" : {
        "x": 10,
        "y": 10
    }

}'


Sample Response Body:

{
    "data": {
        "id": "<ID_OF_ATTACHED_ELEMENT>"
    }
}

Get: list all attachments of an element

Endpoint /v3/workspaces/<workspace_uid>/elements/<surface_id>/attachments
Method GET
Comments

Sample Request:

var request = require('request');

const token = <SET_TOKEN>;
const portal = '';
const workspace_uid = '<SET_WORKSPACE_UID>';
const api_version = 'v3';
var api_endpoint = '/' + api_version + '/workspaces/' + workspace_uid + '/elements/attachments';

function runRequest(portal,api_endpoint,method,data_load) {
    return new Promise((resolve, reject) => {
        var request_values = {
            uri : portal + api_endpoint ,
            method : method ,
            headers : {
                'Authorization': "Bearer " + token,
                'Content-Type' : 'application/json'    
            },
            body : data_load,
            json: true
        };

        request(request_values, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                // Print out the result
                console.log("Successful request. Body response: " + JSON.stringify(body,null,"     ")); 
                } else {
                    reject('Invalid status code <' + response.statusCode + '>'); 
                }
                resolve(body);
        })

    });
}

async function runAPIRequests() {
    api_endpoint = '/v3' + '/workspaces/' + workspace_uid + '/elements/<SET_SURFACE_ID>/attachments'

    method = 'GET';

    try {
        var uploadResponse = await runRequest(portal,api_endpoint,method);
    } catch (error) {
        console.error('ERROR getting attachments ');
        console.error(error);
    }
}
runAPIRequests();  
import requests
from requests.exceptions import HTTPError
import pprint

token = '<SET_TOKEN>'
portal = ''
workspace_uid = '<SET_WORKSPACE_UID>'
api_version = '/v3/'


if __name__ == "__main__":
    API_endpoint = portal + api_version + 'workspaces/' + workspace_uid + '/elements/<SET_SURFACE_ID>/attachments'

    the_request = requests.get(
            API_endpoint,
            headers={"Authorization": "Bearer " + token
                    }
        )

    json_response = the_request.json()
    pprint.pprint(json_response)
curl -X GET /v3/workspaces/<workspace_uid>/elements/<SET_SURFACE_ID>/attachments \
-H 'Authorization: Bearer <SET_TOKEN>' 

Sample Response Body with the list of attachments an object has:
  • This is the list of elements attached to the element <surface_id>:
{
    "data": [
        {
            "id": "<ID_OF_ATTACHED_ELEMENT>",
            "zIndex": 1357,
            "transform": {
                "x": 10,
                "y": 10,
                "scaleX": 1,
                "scaleY": 1
            },
            "pinned": false,
            "comments": [],
            "surface": "<surface_id>",
            "type": "Text",
            "text": "Text to Attach",
            "style": {
                ...
            },
            "blocks": [
                ...
            ]
        }
    ]
}

Detach: remove attachment of an element, REST

To detach an element (remove it from being attached to a surface), you will need to use the following API:

Endpoint /v3/workspaces/<workspace_uid>/elements/<surface_id>/attachments/<element_id>
Method DELETE
Comments Here you will remove the element with Id element_id from the surface with Id surface_id.

Sample Request:

var request = require('request');

const token = <SET_TOKEN>;
const portal = '';
const workspace_uid = '<SET_WORKSPACE_UID>';
const api_version = 'v3';
var api_endpoint = '/' + api_version + '/workspaces/' + workspace_uid + '/elements/attachments';

function runRequest(portal,api_endpoint,method,data_load) {
    return new Promise((resolve, reject) => {
        var request_values = {
            uri : portal + api_endpoint ,
            method : method ,
            headers : {
                'Authorization': "Bearer " + token,
                'Content-Type' : 'application/json'    
            },
            json: true
        };

        request(request_values, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                // Print out the result
                console.log("Successful request. Body response: " + JSON.stringify(body,null,"     ")); 
                } else {
                reject('Invalid status code <' + response.statusCode + '>'); 
                }
                resolve(body);
        })

    });
}
async function runAPIRequests() {
    api_endpoint = '/v3' + '/workspaces/' + workspace_uid + '/elements/<SET_SURFACE_ID>/attachments/<ATTACHMENT_ID>'

    method = 'DELETE';

    try {
        var uploadResponse = await runRequest(portal,api_endpoint,method);
    } catch (error) {
        console.error('ERROR deleting attachment ');
        console.error(error);
    }
}
runAPIRequests();
import requests
from requests.exceptions import HTTPError
import pprint

token = '<SET_TOKEN>'
portal = ''
workspace_uid = '<SET_WORKSPACE_UID>'
api_version = '/v3/'


if __name__ == "__main__":
    API_endpoint = portal + api_version + 'workspaces/' + workspace_uid + '/elements/<SET_SURFACE_ID>/attachments/<ATTACHMENT_ID>'

    the_request = requests.delete(
            API_endpoint,
            headers={"Authorization": "Bearer " + token
                    }
        )

    # Successful response is HTTP 204
    pprint.pprint(the_request.status_code)
    
curl -X DELETE /v3/workspaces/<workspace_uid>/elements/<SET_SURFACE_ID>/attachments/<attachment_id> \
-H 'Authorization: Bearer <SET_TOKEN>'


The Response Body, after deleting the attachments in an element, will return an status HTTP 204: no content. It means that the operation was executed successfully.

{
  "message": "Attachment {source_id} has been removed from the surface {surface_id}"
}  


How to Implement Attachments APIs: GRAPHQL APIs

Create: attach one element to another, GraphQL

In graphQL, you perform the attachment of an element to another element (surface) by setting the ID of the surface element in the surface field when creating or updating an element.
The example below shows how to attach a Text element to another element on creation, using the createText mutation, or later by updating it with the updateText mutation.

mutation createText or updateText
Method POST
Comments
  • Specify in the payload the Id of the element you want to attach to, the element indicated with <surface_id>.
  • The positioning of attachment is relative to top-left corner of the surface

Here is the example for an attachment when creating a Text element:

mutation createTextExample($workspaceId: String!, $input: CreateTextInput!){
    createText(workspaceId: $workspaceId, input: $input) {
        id
    }
}    
using these variables:
{
    "workspaceId" : "<SET_WORKSPACE_UID>",
    "input" : {       
        "transform" : { "x": 10, "y":10 },
        "text" : "This text is attached",
        "surface": "<SET_SURFACE_ID>"
    }
}

Sample Request:

const axios = require('axios');

const url = ""
const token = "<SET_TOKEN>"
const workspaceId = "<SET_WORKSPACEID>"

const textMutation = 
    `mutation createTextExample($workspaceId: String!, $input: CreateTextInput!){
            createText(workspaceId: $workspaceId, input: $input) {
            __typename
            id
            text
            transform { x y }
            }
        }`
const variables = {   
    "workspaceId": workspaceId,
    "input": {
        "text": "This text is attached",
        "surface": "<SET_SURFACE_ID>",
        "transform": {
            "x": 10,
            "y": 10
        }
    }
}

const attachText = async () => {
    try {
        const response = await axios.post(url,
            {
                query: textMutation,
                variables: variables
            }, 
            {headers: {"Authorization": "Bearer " + token}})
        
        console.log(response.data);
        return response
    }
    catch(error) {
        console.error(error)
    }
}

attachText();
    
import requests
from requests.exceptions import HTTPError
import pprint

token = '<SET_TOKEN>'
portal = ''

workspaceId = '<SET_WORKSPACE_UID>'

if __name__ == "__main__":

    API_endpoint = portal

    textMutation = """
    mutation createTextExample($workspaceId: String!, $input: CreateTextInput!){
        createText(workspaceId: $workspaceId, input: $input) {
            __typename
            id
            text
            transform { x y }
        }
    }
    """

    graphqlVariables = {
        "workspaceId": workspaceId,
        "input": {
            "text": "This text is attached",
            "surface": "<SET_SURFACE_ID>",
            "transform": {
                "x": 10,
                "y": 10
            }
        }
    }

    the_request = requests.post(
            API_endpoint,
            headers={"Authorization": "Bearer " + token,
                        "Content-Type": "application/json"
                    },
            json={
                "query": textMutation,
                "variables": graphqlVariables
            }
        )

    json_response = the_request.json()
    pprint.pprint(json_response)
curl --location --request POST '' \
--header 'Authorization: Bearer <SET_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"mutation createTextExample($workspaceId: String!, $input: CreateTextInput!){\n        createText(workspaceId: $workspaceId, input: $input) {\n            __typename\n            id\n            text\n            transform { x y }\n        }\n    }","variables":{"workspaceId":"<SET_WORKSPACE_UID>","input":{"text":"This text is attached","surface":"<SET_SURFACE_ID>","transform":{"x":10,"y":10}}}}'

Sample Response Body:

{
    "data": {
        "id": "<ID_OF_ATTACHED_ELEMENT>"
    }
}

Get: list all attachments of an element, GraphQL

query elements
Method POST
Comments

Here is the example a query to get the details of the attachments for an element. For this example, we added the settings to list the attachment details for an Image, but you can extend it to any specific element you want:

query getDetailsOfAttachments($workspaceId: String! $elementId: String!){
    elements(workspaceId: $workspaceId  id: $elementId) {
        type: __typename        
        id
        transform {
            x
            y
        }

        ... on Image {
          attachments {
            id
            type: __typename
            transform {x y}
            ... on Text {
              text
            }
          }
        }
    }
}

using these variables:

{
    "workspaceId" : "<SET_WORKSPACE_UID>",
    "elementId" : "<SET_SURFACE_ID>"
}

Detach: remove an element's attachments, GraphQL

In graphQL, to detach or remove an element's attachment, you need to set the surface field to the value of the Id of the workspace where you are working. This will need to be done using an update mutation.
The example below shows how to detach a Text element from the surface it is attached to. We will use the updateText mutation.

mutation updateText
Method POST
Comments You will need to know the Id of the Text object to detach

Here is the example a query to get the details of the attachments for an element. For this example, we added the settings to list the attachment details for an Image, but you can extend it to any specific element you want:

mutation detachTextElement($workspaceId: String!  $textElementId: String!) {
  updateText(workspaceId: $workspaceId , id: $textElementId, input: {
    surface: $workspaceId
  })
  {
    id
  }
  
}

using these variables:

{
    "workspaceId" : "<SET_WORKSPACE_UID>",
    "textElementId" : "<SET_TEXT_ELEMENT_ID_TO_DETACH>"
}

If you have any questions or comments, please contact us in the Bluescape Community site.