# Attachments APIs for v3

**URL:** <https://community.bluescape.com/t/attachments-apis-for-v3/2139>\
**Category:** Developer\
**Tags:** api\
**Created:** [July 26, 2022, 6:50pm UTC](https://community.bluescape.com/t/attachments-apis-for-v3/2139 "2022-07-26T18:50:05Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tom\_Garrett](https://avatars.discourse-cdn.com/v4/letter/t/258eb7/32.png) [@Tom\_Garrett](https://community.bluescape.com/u/Tom_Garrett)\
**Post date:** [July 26, 2022, 6:50pm UTC](https://community.bluescape.com/t/attachments-apis-for-v3/2139/1 "2022-07-26T18:50:05Z")

</div>

## 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.

Review the page for [Attachments APIs in v2](https://community.bluescape.com/t/attachments-apis-for-v2/2140).

## Table of Contents:

[Allowed Elements and Surfaces for Attachments](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#allowed-elements-and-surfaces-for-attachments-3)  
[How to Implement Attachments APIs: REST APIs](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#how-to-implement-attachments-apis-rest-apis-4)

[1. Create: attachment an element to another element, REST](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#create-attach-one-element-to-another-rest-5)  
[2. Get: list all attachments of an element, REST](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#get-list-all-attachments-of-an-element-6)  
[3. Detach: remove attachment of an element, REST](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#detach-remove-attachment-of-an-element-rest-7)

[How to Implement Attachments APIs: GraphQL APIs](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#how-to-implement-attachments-apis-graphql-apis-8)  
[A. Create: attachment an element to another element, GraphQL](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#create-attach-one-element-to-another-graphql-9)  
[B. Get: list all attachments of an element, GraphQL](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#get-list-all-attachments-of-an-element-graphql-10)  
[C. Detach: remove attachment of an element, GraphQL](https://community.bluescape.com/t/wip-attachments-apis-for-v3/2139#detach-remove-an-elements-attachments-graphql-11)

### Allowed Elements and Surfaces for Attachments

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

**Attachable objects**

- Text
- LegacyNote
- Image
- Document
- Browser
- Video
- Stroke
- Shape
- Line
- Window
- Whiteboard
- LinkedDocument

**Surfaces (elements to which attachable object can attach to)**

- 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:

> **cURL**
>
> ```auto
> curl -X POST https://api.apps.us.bluescape.com/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
> }
> 
> }'
> 
> ```

> **Node.js**
>
> ```auto
> var request = require('request');
> 
> const token = '<SET_TOKEN>';
> const portal = 'https://api.apps.us.bluescape.com';
> 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();
> 
> ```

> **Python**
>
> ```auto
> import requests
> from requests.exceptions import HTTPError
> import pprint
> 
> token = '<SET_TOKEN>'
> portal = 'https://api.apps.us.bluescape.com'
> 
> 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)
> 
> ```

Sample Response Body:

```auto
{
    "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:

> **cURL**
>
> ```auto
> curl -X GET https://api.apps.us.bluescape.com/v3/workspaces/<workspace_uid>/elements/<SET_SURFACE_ID>/attachments \
> -H 'Authorization: Bearer <SET_TOKEN>' 
> 
> ```

> **Node.js**
>
> ```auto
> var request = require('request');
> 
> const token = <SET_TOKEN>;
> const portal = 'https://api.apps.us.bluescape.com';
> 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(); 
> 
> ```

> **Python**
>
> ```auto
> import requests
> from requests.exceptions import HTTPError
> import pprint
> 
> token = '<SET_TOKEN>'
> portal = 'https://api.apps.us.bluescape.com'
> 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)
> 
> ```

Sample Response Body with the list of attachments an object has:

- This is the list of elements attached to the element \<surface\_id\>:

```auto
{
    "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:

> **cURL**
>
> ```auto
> curl -X DELETE https://api.apps.us.bluescape.com/v3/workspaces/<workspace_uid>/elements/<SET_SURFACE_ID>/attachments/<attachment_id> \
> -H 'Authorization: Bearer <SET_TOKEN>'
> 
> ```

> **Node.js**
>
> ```auto
> var request = require('request');
> 
> const token = <SET_TOKEN>;
> const portal = 'https://api.apps.us.bluescape.com';
> 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();
> 
> ```

> **Python**
>
> ```auto
> import requests
> from requests.exceptions import HTTPError
> import pprint
> 
> token = '<SET_TOKEN>'
> portal = 'https://api.apps.us.bluescape.com'
> 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)
> 
> ```

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.

```auto
{
  "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:

```auto
mutation createTextExample($workspaceId: String!, $input: CreateTextInput!){
    createText(workspaceId: $workspaceId, input: $input) {
        id
    }
}  

```

using these variables:

```auto
{
    "workspaceId" : "<SET_WORKSPACE_UID>",
    "input" : {       
        "transform" : { "x": 10, "y":10 },
        "text" : "This text is attached",
        "surface": "<SET_SURFACE_ID>"
    }
}

```

Sample Request:

> **cURL**
>
> ```auto
> curl --location --request POST 'https://api.apps.us.bluescape.com/v3/graphql' \
> --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}}}}'
> 
> ```

> **Node.js**
>
> ```auto
> const axios = require('axios');
> 
> const url = "https://api.apps.us.bluescape.com/v3/graphql"
> 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();
> 
> ```

> **Python**
>
> ```auto
> import requests
> from requests.exceptions import HTTPError
> import pprint
> 
> token = '<SET_TOKEN>'
> portal = 'https://api.apps.us.bluescape.com/v3/graphql'
> 
> 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)
> 
> ```

Sample Response Body:

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

```

### Get: list all attachments of an element, GraphQL

| query | elements |
| --- | --- |
| Method | POST |

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:

```auto
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:

```auto
{
    "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:

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

```

using these variables:

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

```

 

> # Where to Next?
> 
> - [Attachments APIs for v2](https://community.bluescape.com/t/attachments-apis-for-v2/2140)
> - [Bluescape API Guides](https://community.bluescape.com/t/bluescape-api-guides-landing-page/2020)
> - [Bluescape API Overview](https://community.bluescape.com/t/wip-bluescape-api-overview/2021/2)
> - [Latest Developer topics](https://community.bluescape.com/c/developer/14)
> 
> **Not what you were looking for? Reply below or [Search](https://community.bluescape.com/search) the community and discover more Bluescape.**
