diff --git a/serializers/jsonapi/CustomTextFieldSerializer.ts b/serializers/jsonapi/CustomTextFieldSerializer.ts
index 93db4e32b23d9ddfb0e5a5e0a1bf390ebf5bcaac..cda282cc3b7250579051220642822efcc2d2a975 100644
--- a/serializers/jsonapi/CustomTextFieldSerializer.ts
+++ b/serializers/jsonapi/CustomTextFieldSerializer.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2020, 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -35,9 +35,7 @@ import {
   IJsonApiNestedElement,
   IJsonApiObject,
   IJsonApiObjectList,
-  IJsonApiTypeId,
-  IJsonApiTypeIdAttributes,
-  IJsonApiTypeIdDataListDict
+  IJsonApiTypeIdAttributes
 } from '@/serializers/jsonapi/JsonApiTypes'
 
 export class CustomTextFieldSerializer {
@@ -92,14 +90,6 @@ export class CustomTextFieldSerializer {
     return jsonApiObjectList.data.map(this.convertJsonApiDataToModel)
   }
 
-  convertModelListToJsonApiRelationshipObject (customFields: ICustomTextField[]): IJsonApiTypeIdDataListDict {
-    return {
-      customfields: {
-        data: this.convertModelListToTupleListWithIdAndType(customFields)
-      }
-    }
-  }
-
   convertModelToJsonApiData (customField: ICustomTextField, deviceId: string): IJsonApiDataWithOptionalId {
     const data: any = {
       type: 'customfield',
@@ -121,17 +111,4 @@ export class CustomTextFieldSerializer {
     }
     return data
   }
-
-  convertModelListToTupleListWithIdAndType (customFields: ICustomTextField[]): IJsonApiTypeId[] {
-    const result: IJsonApiTypeId[] = []
-    for (const field of customFields) {
-      if (field.id !== null) {
-        result.push({
-          id: field.id,
-          type: 'customfield'
-        })
-      }
-    }
-    return result
-  }
 }
diff --git a/serializers/jsonapi/DeviceAttachmentSerializer.ts b/serializers/jsonapi/DeviceAttachmentSerializer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..de70c753ea76784a1eeedf31957101280c479296
--- /dev/null
+++ b/serializers/jsonapi/DeviceAttachmentSerializer.ts
@@ -0,0 +1,78 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2021
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+import { Attachment } from '@/models/Attachment'
+import { IJsonApiObject, IJsonApiDataWithOptionalId, IJsonApiTypeIdAttributes, IJsonApiObjectList } from '@/serializers/jsonapi/JsonApiTypes'
+
+export class DeviceAttachmentSerializer {
+  convertJsonApiObjectToModel (jsonApiObject: IJsonApiObject): Attachment {
+    const data = jsonApiObject.data
+    return this.convertJsonApiDataToModel(data)
+  }
+
+  convertJsonApiObjectListToModelList (jsonApiObjectList: IJsonApiObjectList): Attachment[] {
+    return jsonApiObjectList.data.map(this.convertJsonApiDataToModel)
+  }
+
+  convertJsonApiDataToModel (jsonApiData: IJsonApiTypeIdAttributes): Attachment {
+    const attribues = jsonApiData.attributes
+
+    const newEntry = new Attachment()
+
+    newEntry.id = jsonApiData.id.toString()
+    newEntry.url = attribues.url || ''
+    newEntry.label = attribues.label || ''
+
+    return newEntry
+  }
+
+  convertModelToJsonApiData (attachment: Attachment, deviceId: string): IJsonApiDataWithOptionalId {
+    const data: any = {
+      type: 'device_attachment',
+      attributes: {
+        url: attachment.url,
+        label: attachment.label
+      },
+      relationships: {
+        device: {
+          data: {
+            type: 'device',
+            id: deviceId
+          }
+        }
+      }
+    }
+    if (attachment.id) {
+      data.id = attachment.id
+    }
+    return data
+  }
+}
diff --git a/serializers/jsonapi/DevicePropertySerializer.ts b/serializers/jsonapi/DevicePropertySerializer.ts
index d5171bdfc8c05921e2881b4aee0d1eb30c509efd..67123eec55c25be26812bb9fcccd334a55e0762c 100644
--- a/serializers/jsonapi/DevicePropertySerializer.ts
+++ b/serializers/jsonapi/DevicePropertySerializer.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2020, 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -32,7 +32,7 @@
 import { DeviceProperty } from '@/models/DeviceProperty'
 import { MeasuringRange } from '@/models/MeasuringRange'
 
-import { IJsonApiNestedElement, IJsonApiTypeIdAttributes, IJsonApiObjectList } from '@/serializers/jsonapi/JsonApiTypes'
+import { IJsonApiNestedElement, IJsonApiObject, IJsonApiObjectList, IJsonApiTypeIdAttributes, IJsonApiDataWithOptionalId } from '@/serializers/jsonapi/JsonApiTypes'
 
 export class DevicePropertySerializer {
   convertJsonApiElementToModel (property: IJsonApiNestedElement): DeviceProperty {
@@ -99,35 +99,73 @@ export class DevicePropertySerializer {
     return result
   }
 
-  convertJsonApiDataToModel (jsonApiData: IJsonApiTypeIdAttributes): DeviceProperty {
-    const attributes = jsonApiData.attributes
+  convertJsonApiObjectToModel (jsonApiObject: IJsonApiObject): DeviceProperty {
+    const data = jsonApiObject.data
+    return this.convertJsonApiDataToModel(data)
+  }
 
-    const newEntry = new DeviceProperty()
+  convertJsonApiObjectListToModelList (jsonApiObjectList: IJsonApiObjectList): DeviceProperty[] {
+    return jsonApiObjectList.data.map(this.convertJsonApiDataToModel)
+  }
 
-    newEntry.id = jsonApiData.id.toString()
-    newEntry.measuringRange = new MeasuringRange(
-      attributes.measuring_range_min,
-      attributes.measuring_range_max
+  convertJsonApiDataToModel (jsonApiData: IJsonApiTypeIdAttributes): DeviceProperty {
+    const result = new DeviceProperty()
+    result.id = jsonApiData.id.toString()
+    result.measuringRange = new MeasuringRange(
+      jsonApiData.attributes.measuring_range_min || null,
+      jsonApiData.attributes.measuring_range_max || null
     )
-    newEntry.failureValue = attributes.failure_value
-    newEntry.accuracy = attributes.accuracy
-    newEntry.resolution = attributes.resolution
-    newEntry.label = attributes.label || ''
-    newEntry.unitUri = attributes.unit_uri || ''
-    newEntry.unitName = attributes.unit_name || ''
-    newEntry.compartmentUri = attributes.compartment_uri || ''
-    newEntry.compartmentName = attributes.compartment_name || ''
-    newEntry.propertyUri = attributes.property_uri || ''
-    newEntry.propertyName = attributes.property_name || ''
-    newEntry.samplingMediaUri = attributes.sampling_media_uri || ''
-    newEntry.samplingMediaName = attributes.sampling_media_name || ''
-    newEntry.resolutionUnitUri = attributes.resolution_unit_uri || ''
-    newEntry.resolutionUnitName = attributes.resolution_unit_name || ''
+    result.failureValue = jsonApiData.attributes.failure_value || null
+    result.accuracy = jsonApiData.attributes.accuracy || null
+    result.resolution = jsonApiData.attributes.resolution || null
+    result.label = jsonApiData.attributes.label || ''
+    result.unitUri = jsonApiData.attributes.unit_uri || ''
+    result.unitName = jsonApiData.attributes.unit_name || ''
+    result.compartmentUri = jsonApiData.attributes.compartment_uri || ''
+    result.compartmentName = jsonApiData.attributes.compartment_name || ''
+    result.propertyUri = jsonApiData.attributes.property_uri || ''
+    result.propertyName = jsonApiData.attributes.property_name || ''
+    result.samplingMediaUri = jsonApiData.attributes.sampling_media_uri || ''
+    result.samplingMediaName = jsonApiData.attributes.sampling_media_name || ''
+    result.resolutionUnitUri = jsonApiData.attributes.resolution_unit_uri || ''
+    result.resolutionUnitName = jsonApiData.attributes.resolution_unit_name || ''
 
-    return newEntry
+    return result
   }
 
-  convertJsonApiObjectListToModelList (jsonApiObjectList: IJsonApiObjectList): DeviceProperty[] {
-    return jsonApiObjectList.data.map(this.convertJsonApiDataToModel)
+  convertModelToJsonApiData (deviceProperty: DeviceProperty, deviceId: string): IJsonApiDataWithOptionalId {
+    const data: any = {
+      type: 'device_property',
+      attributes: {
+        measuring_range_min: deviceProperty.measuringRange.min,
+        measuring_range_max: deviceProperty.measuringRange.max,
+        failure_value: deviceProperty.failureValue,
+        accuracy: deviceProperty.accuracy,
+        resolution: deviceProperty.resolution,
+        label: deviceProperty.label,
+        unit_uri: deviceProperty.unitUri,
+        unit_name: deviceProperty.unitName,
+        compartment_uri: deviceProperty.compartmentUri,
+        compartment_name: deviceProperty.compartmentName,
+        property_uri: deviceProperty.propertyUri,
+        property_name: deviceProperty.propertyName,
+        sampling_media_uri: deviceProperty.samplingMediaUri,
+        sampling_media_name: deviceProperty.samplingMediaName,
+        resolution_unit_uri: deviceProperty.resolutionUnitUri,
+        resolution_unit_name: deviceProperty.resolutionUnitName
+      },
+      relationships: {
+        device: {
+          data: {
+            type: 'device',
+            id: deviceId
+          }
+        }
+      }
+    }
+    if (deviceProperty.id) {
+      data.id = deviceProperty.id
+    }
+    return data
   }
 }
diff --git a/serializers/jsonapi/PlatformAttachmentSerializer.ts b/serializers/jsonapi/PlatformAttachmentSerializer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f6d772e00f496018ffffadeb19f0d566cb809cf
--- /dev/null
+++ b/serializers/jsonapi/PlatformAttachmentSerializer.ts
@@ -0,0 +1,79 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2021
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+
+import { Attachment } from '@/models/Attachment'
+import { IJsonApiObject, IJsonApiDataWithOptionalId, IJsonApiTypeIdAttributes, IJsonApiObjectList } from '@/serializers/jsonapi/JsonApiTypes'
+
+export class PlatformAttachmentSerializer {
+  convertJsonApiObjectToModel (jsonApiObject: IJsonApiObject): Attachment {
+    const data = jsonApiObject.data
+    return this.convertJsonApiDataToModel(data)
+  }
+
+  convertJsonApiObjectListToModelList (jsonApiObjectList: IJsonApiObjectList): Attachment[] {
+    return jsonApiObjectList.data.map(this.convertJsonApiDataToModel)
+  }
+
+  convertJsonApiDataToModel (jsonApiData: IJsonApiTypeIdAttributes): Attachment {
+    const attribues = jsonApiData.attributes
+
+    const newEntry = new Attachment()
+
+    newEntry.id = jsonApiData.id.toString()
+    newEntry.url = attribues.url || ''
+    newEntry.label = attribues.label || ''
+
+    return newEntry
+  }
+
+  convertModelToJsonApiData (attachment: Attachment, platformId: string): IJsonApiDataWithOptionalId {
+    const data: any = {
+      type: 'platform_attachment',
+      attributes: {
+        url: attachment.url,
+        label: attachment.label
+      },
+      relationships: {
+        platform: {
+          data: {
+            type: 'platform',
+            id: platformId
+          }
+        }
+      }
+    }
+    if (attachment.id) {
+      data.id = attachment.id
+    }
+    return data
+  }
+}
diff --git a/services/Api.ts b/services/Api.ts
index cea629b700d4a6a46685e61aabf3d942eb470e60..2ba69c48a03819d1c7ca08f1a40b617f498f6860 100644
--- a/services/Api.ts
+++ b/services/Api.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2020, 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -33,10 +33,13 @@ import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
 
 import { ContactApi } from '@/services/sms/ContactApi'
 import { DeviceApi } from '@/services/sms/DeviceApi'
+import { DevicePropertyApi } from '@/services/sms/DevicePropertyApi'
 import { PlatformApi } from '@/services/sms/PlatformApi'
 import { ConfigurationApi } from '@/services/sms/ConfigurationApi'
 import { ConfigurationStatusApi } from '@/services/sms/ConfigurationStatusApi'
 import { CustomfieldsApi } from '@/services/sms/CustomfieldsApi'
+import { DeviceAttachmentApi } from '@/services/sms/DeviceAttachmentApi'
+import { PlatformAttachmentApi } from '@/services/sms/PlatformAttachmentApi'
 
 import { CompartmentApi } from '@/services/cv/CompartmentApi'
 import { DeviceTypeApi } from '@/services/cv/DeviceTypeApi'
@@ -60,6 +63,9 @@ export class Api {
   private readonly _configurationApi: ConfigurationApi
   private readonly _configurationStatesApi: ConfigurationStatusApi
   private readonly _customfieldsApi: CustomfieldsApi
+  private readonly _deviceAttachmentApi: DeviceAttachmentApi
+  private readonly _platformAttachmentApi: PlatformAttachmentApi
+  private readonly _devicePropertyApi: DevicePropertyApi
 
   private readonly _manufacturerApi: ManufacturerApi
   private readonly _platformTypeApi: PlatformTypeApi
@@ -105,6 +111,18 @@ export class Api {
       this.createAxios(smsBaseUrl, '/customfields', smsConfig, getIdToken)
     )
 
+    this._deviceAttachmentApi = new DeviceAttachmentApi(
+      this.createAxios(smsBaseUrl, '/device-attachments', smsConfig, getIdToken)
+    )
+
+    this._platformAttachmentApi = new PlatformAttachmentApi(
+      this.createAxios(smsBaseUrl, '/platform-attachments', smsConfig, getIdToken)
+    )
+
+    this._devicePropertyApi = new DevicePropertyApi(
+      this.createAxios(smsBaseUrl, '/device-properties', smsConfig, getIdToken)
+    )
+
     // and here we can set settings for all the cv api calls
     const cvConfig: AxiosRequestConfig = {
       headers: {
@@ -189,6 +207,18 @@ export class Api {
     return this._customfieldsApi
   }
 
+  get deviceAttachments (): DeviceAttachmentApi {
+    return this._deviceAttachmentApi
+  }
+
+  get platformAttachments (): PlatformAttachmentApi {
+    return this._platformAttachmentApi
+  }
+
+  get deviceProperties (): DevicePropertyApi {
+    return this._devicePropertyApi
+  }
+
   get contacts (): ContactApi {
     return this._contactApi
   }
diff --git a/services/sms/ConfigurationApi.ts b/services/sms/ConfigurationApi.ts
index d55c0669d98d63481226d65efb06b812d1b27df6..535e7c99fae814438305ed47b8e2e02d56a12f49 100644
--- a/services/sms/ConfigurationApi.ts
+++ b/services/sms/ConfigurationApi.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2020, 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -34,6 +34,8 @@
 import { AxiosInstance, Method } from 'axios'
 
 import { Configuration } from '@/models/Configuration'
+import { Contact } from '@/models/Contact'
+import { DynamicLocation } from '@/models/Location'
 import { Project } from '@/models/Project'
 
 // eslint-disable-next-line
@@ -49,7 +51,8 @@ import {
   configurationWithMetaToConfigurationByAddingDummyObjects,
   configurationWithMetaToConfigurationByThrowingErrorOnMissing
 } from '@/serializers/jsonapi/ConfigurationSerializer'
-import { DynamicLocation } from '@/models/Location'
+
+import { ContactSerializer } from '@/serializers/jsonapi/ContactSerializer'
 
 interface IRelationshipData {
   id: string
@@ -174,6 +177,36 @@ export class ConfigurationApi {
   newSearchBuilder (): ConfigurationSearchBuilder {
     return new ConfigurationSearchBuilder(this.axiosApi, this.serializer)
   }
+
+  findRelatedContacts (configurationId: string): Promise<Contact[]> {
+    const url = configurationId + '/contacts'
+    const params = {
+      'page[size]': 10000
+    }
+    return this.axiosApi.get(url, { params }).then((rawServerResponse) => {
+      return new ContactSerializer().convertJsonApiObjectListToModelList(rawServerResponse.data)
+    })
+  }
+
+  removeContact (configurationId: string, contactId: string): Promise<void> {
+    const url = configurationId + '/relationships/contacts'
+    const params = {
+      data: [{
+        type: 'contact',
+        id: contactId
+      }]
+    }
+    return this.axiosApi.delete(url, { data: params })
+  }
+
+  addContact (configurationId: string, contactId: string): Promise<void> {
+    const url = configurationId + '/relationships/contacts'
+    const data = [{
+      type: 'contact',
+      id: contactId
+    }]
+    return this.axiosApi.post(url, { data })
+  }
 }
 
 export class ConfigurationSearchBuilder {
diff --git a/services/sms/CustomfieldsApi.ts b/services/sms/CustomfieldsApi.ts
index 8a2179b52880565d1de08a997434d7a405e55d66..4c27722733f7966845264c374cfa21bd9a17af63 100644
--- a/services/sms/CustomfieldsApi.ts
+++ b/services/sms/CustomfieldsApi.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -59,9 +59,8 @@ export class CustomfieldsApi {
   add (deviceId: string, field: CustomTextField): Promise<CustomTextField> {
     const url = ''
     const data: any = this.serializer.convertModelToJsonApiData(field, deviceId)
-    return this.axiosApi.post(url, { data }).then((rawResponse) => {
-      const rawData = rawResponse.data
-      return this.serializer.convertJsonApiObjectToModel(rawData)
+    return this.axiosApi.post(url, { data }).then((serverResponse) => {
+      return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
     })
   }
 
@@ -74,10 +73,9 @@ export class CustomfieldsApi {
       }
     }).then((fieldId) => {
       const data: any = this.serializer.convertModelToJsonApiData(field, deviceId)
-      return this.axiosApi.patch(fieldId, { data })
-    }).then((rawResponse) => {
-      const rawData = rawResponse.data
-      return this.serializer.convertJsonApiObjectToModel(rawData)
+      return this.axiosApi.patch(fieldId, { data }).then((serverResponse) => {
+        return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
+      })
     })
   }
 }
diff --git a/services/sms/DeviceApi.ts b/services/sms/DeviceApi.ts
index a0225569d92987be7cc6469b384792339e4b981d..e32ac0b8756531da3a14c35706f63464dab3f861 100644
--- a/services/sms/DeviceApi.ts
+++ b/services/sms/DeviceApi.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2020, 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -31,6 +31,7 @@
  */
 import { AxiosInstance, Method } from 'axios'
 
+import { Attachment } from '@/models/Attachment'
 import { Contact } from '@/models/Contact'
 import { CustomTextField } from '@/models/CustomTextField'
 import { Device } from '@/models/Device'
@@ -41,6 +42,7 @@ import { Status } from '@/models/Status'
 
 import { ContactSerializer } from '@/serializers/jsonapi/ContactSerializer'
 import { CustomTextFieldSerializer } from '@/serializers/jsonapi/CustomTextFieldSerializer'
+import { DeviceAttachmentSerializer } from '@/serializers/jsonapi/DeviceAttachmentSerializer'
 import { DevicePropertySerializer } from '@/serializers/jsonapi/DevicePropertySerializer'
 
 import { IFlaskJSONAPIFilter } from '@/utils/JSONApiInterfaces'
@@ -150,6 +152,16 @@ export class DeviceApi {
     })
   }
 
+  findRelatedDeviceAttachments (deviceId: string): Promise<Attachment[]> {
+    const url = deviceId + '/device-attachments'
+    const params = {
+      'page[size]': 10000
+    }
+    return this.axiosApi.get(url, { params }).then((rawServerResponse) => {
+      return new DeviceAttachmentSerializer().convertJsonApiObjectListToModelList(rawServerResponse.data)
+    })
+  }
+
   findRelatedDeviceProperties (deviceId: string): Promise<DeviceProperty[]> {
     const url = deviceId + '/device-properties'
     const params = {
diff --git a/services/sms/DeviceAttachmentApi.ts b/services/sms/DeviceAttachmentApi.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc21052045fadb0103760ee6448475202d1a34c3
--- /dev/null
+++ b/services/sms/DeviceAttachmentApi.ts
@@ -0,0 +1,79 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2021
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+import { AxiosInstance } from 'axios'
+
+import { Attachment } from '@/models/Attachment'
+import { DeviceAttachmentSerializer } from '@/serializers/jsonapi/DeviceAttachmentSerializer'
+
+export class DeviceAttachmentApi {
+  private axiosApi: AxiosInstance
+  private serializer: DeviceAttachmentSerializer
+
+  constructor (axiosInstance: AxiosInstance) {
+    this.axiosApi = axiosInstance
+    this.serializer = new DeviceAttachmentSerializer()
+  }
+
+  findById (id: string): Promise<Attachment> {
+    return this.axiosApi.get(id).then((rawRespmse) => {
+      const rawData = rawRespmse.data
+      return this.serializer.convertJsonApiObjectToModel(rawData)
+    })
+  }
+
+  deleteById (id: string): Promise<void> {
+    return this.axiosApi.delete<string, void>(id)
+  }
+
+  add (deviceId: string, attachment: Attachment): Promise<Attachment> {
+    const url = ''
+    const data = this.serializer.convertModelToJsonApiData(attachment, deviceId)
+    return this.axiosApi.post(url, { data }).then((serverResponse) => {
+      return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
+    })
+  }
+
+  update (deviceId: string, attachment: Attachment): Promise<Attachment> {
+    return new Promise<string>((resolve, reject) => {
+      if (attachment.id) {
+        resolve(attachment.id)
+      } else {
+        reject(new Error('no id for the Attachment'))
+      }
+    }).then((attachmentId) => {
+      const data = this.serializer.convertModelToJsonApiData(attachment, deviceId)
+      return this.axiosApi.patch(attachmentId, { data }).then((serverResponse) => {
+        return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
+      })
+    })
+  }
+}
diff --git a/services/sms/DevicePropertyApi.ts b/services/sms/DevicePropertyApi.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5254beb5f8e6be32596ea61f7d6b508cd8f3d717
--- /dev/null
+++ b/services/sms/DevicePropertyApi.ts
@@ -0,0 +1,79 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2021
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+import { AxiosInstance } from 'axios'
+
+import { DeviceProperty } from '@/models/DeviceProperty'
+import { DevicePropertySerializer } from '@/serializers/jsonapi/DevicePropertySerializer'
+
+export class DevicePropertyApi {
+  private axiosApi: AxiosInstance
+  private serializer: DevicePropertySerializer
+
+  constructor (axiosInstance: AxiosInstance) {
+    this.axiosApi = axiosInstance
+    this.serializer = new DevicePropertySerializer()
+  }
+
+  findById (id: string): Promise<DeviceProperty> {
+    return this.axiosApi.get(id).then((rawRespmse) => {
+      const rawData = rawRespmse.data
+      return this.serializer.convertJsonApiObjectToModel(rawData)
+    })
+  }
+
+  deleteById (id: string): Promise<void> {
+    return this.axiosApi.delete<string, void>(id)
+  }
+
+  add (deviceId: string, deviceProperty: DeviceProperty): Promise<DeviceProperty> {
+    const url = ''
+    const data = this.serializer.convertModelToJsonApiData(deviceProperty, deviceId)
+    return this.axiosApi.post(url, { data }).then((serverResponse) => {
+      return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
+    })
+  }
+
+  update (deviceId: string, deviceProperty: DeviceProperty) : Promise<DeviceProperty> {
+    return new Promise<string>((resolve, reject) => {
+      if (deviceProperty.id) {
+        resolve(deviceProperty.id)
+      } else {
+        reject(new Error('no id for the Attachment'))
+      }
+    }).then((devicePropertyId) => {
+      const data = this.serializer.convertModelToJsonApiData(deviceProperty, deviceId)
+      return this.axiosApi.patch(devicePropertyId, { data }).then((serverResponse) => {
+        return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
+      })
+    })
+  }
+}
diff --git a/services/sms/PlatformApi.ts b/services/sms/PlatformApi.ts
index 27842a866427361076e2f0b7aed89b99385e712c..df258d04822dea4be4cee546f2acb3f7b7d89efc 100644
--- a/services/sms/PlatformApi.ts
+++ b/services/sms/PlatformApi.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2020, 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -31,16 +31,21 @@
  */
 import { AxiosInstance, Method } from 'axios'
 
+import { Attachment } from '@/models/Attachment'
+import { Contact } from '@/models/Contact'
 import { Platform } from '@/models/Platform'
 import { PlatformType } from '@/models/PlatformType'
 import { Manufacturer } from '@/models/Manufacturer'
 import { Status } from '@/models/Status'
 
+import { ContactSerializer } from '@/serializers/jsonapi/ContactSerializer'
+
 import {
   PlatformSerializer,
   platformWithMetaToPlatformByThrowingErrorOnMissing,
   platformWithMetaToPlatformByAddingDummyObjects
 } from '@/serializers/jsonapi/PlatformSerializer'
+import { PlatformAttachmentSerializer } from '@/serializers/jsonapi/PlatformAttachmentSerializer'
 
 import { IFlaskJSONAPIFilter } from '@/utils/JSONApiInterfaces'
 
@@ -102,6 +107,46 @@ export class PlatformApi {
   newSearchBuilder (): PlatformSearchBuilder {
     return new PlatformSearchBuilder(this.axiosApi, this.serializer)
   }
+
+  findRelatedContacts (platformId: string): Promise<Contact[]> {
+    const url = platformId + '/contacts'
+    const params = {
+      'page[size]': 10000
+    }
+    return this.axiosApi.get(url, { params }).then((rawServerResponse) => {
+      return new ContactSerializer().convertJsonApiObjectListToModelList(rawServerResponse.data)
+    })
+  }
+
+  findRelatedDeviceAttachments (deviceId: string): Promise<Attachment[]> {
+    const url = deviceId + '/device-attachments'
+    const params = {
+      'page[size]': 10000
+    }
+    return this.axiosApi.get(url, { params }).then((rawServerResponse) => {
+      return new PlatformAttachmentSerializer().convertJsonApiObjectListToModelList(rawServerResponse.data)
+    })
+  }
+
+  removeContact (platformId: string, contactId: string): Promise<void> {
+    const url = platformId + '/relationships/contacts'
+    const params = {
+      data: [{
+        type: 'contact',
+        id: contactId
+      }]
+    }
+    return this.axiosApi.delete(url, { data: params })
+  }
+
+  addContact (platformId: string, contactId: string): Promise<void> {
+    const url = platformId + '/relationships/contacts'
+    const data = [{
+      type: 'contact',
+      id: contactId
+    }]
+    return this.axiosApi.post(url, { data })
+  }
 }
 
 export class PlatformSearchBuilder {
diff --git a/services/sms/PlatformAttachmentApi.ts b/services/sms/PlatformAttachmentApi.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f0ab0391ef589370a005f6b8f736e062b1867b1
--- /dev/null
+++ b/services/sms/PlatformAttachmentApi.ts
@@ -0,0 +1,79 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2021
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+import { AxiosInstance } from 'axios'
+
+import { Attachment } from '@/models/Attachment'
+import { PlatformAttachmentSerializer } from '@/serializers/jsonapi/PlatformAttachmentSerializer'
+
+export class PlatformAttachmentApi {
+  private axiosApi: AxiosInstance
+  private serializer: PlatformAttachmentSerializer
+
+  constructor (axiosInstance: AxiosInstance) {
+    this.axiosApi = axiosInstance
+    this.serializer = new PlatformAttachmentSerializer()
+  }
+
+  findById (id: string): Promise<Attachment> {
+    return this.axiosApi.get(id).then((rawRespmse) => {
+      const rawData = rawRespmse.data
+      return this.serializer.convertJsonApiObjectToModel(rawData)
+    })
+  }
+
+  deleteById (id: string): Promise<void> {
+    return this.axiosApi.delete<string, void>(id)
+  }
+
+  add (platformId: string, attachment: Attachment) : Promise<Attachment> {
+    const url = ''
+    const data = this.serializer.convertModelToJsonApiData(attachment, platformId)
+    return this.axiosApi.post(url, { data }).then((serverResponse) => {
+      return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
+    })
+  }
+
+  update (platformId: string, attachment: Attachment) : Promise<Attachment> {
+    return new Promise<string>((resolve, reject) => {
+      if (attachment.id) {
+        resolve(attachment.id)
+      } else {
+        reject(new Error('no id for the Attachment'))
+      }
+    }).then((attachmentId) => {
+      const data = this.serializer.convertModelToJsonApiData(attachment, platformId)
+      return this.axiosApi.patch(attachmentId, { data }).then((serverResponse) => {
+        return this.serializer.convertJsonApiObjectToModel(serverResponse.data)
+      })
+    })
+  }
+}
diff --git a/test/serializers/jsonapi/CustomTextFieldSerializer.test.ts b/test/serializers/jsonapi/CustomTextFieldSerializer.test.ts
index 27ced3b6254aaad8606e5a4e1f0423904dd09ebb..80f4221a316c7549a589cabcf25deab628acedc3 100644
--- a/test/serializers/jsonapi/CustomTextFieldSerializer.test.ts
+++ b/test/serializers/jsonapi/CustomTextFieldSerializer.test.ts
@@ -33,6 +33,10 @@ import { CustomTextField } from '@/models/CustomTextField'
 
 import { CustomTextFieldSerializer } from '@/serializers/jsonapi/CustomTextFieldSerializer'
 
+import {
+  IJsonApiObjectList
+} from '@/serializers/jsonapi/JsonApiTypes'
+
 describe('CustomTextFieldSerializer', () => {
   describe('#convertNestedJsonApiToModelList', () => {
     it('should convert a list of entries to models', () => {
@@ -148,5 +152,128 @@ describe('CustomTextFieldSerializer', () => {
       expect(deviceData).toHaveProperty('type')
       expect(deviceData.type).toEqual('device')
     })
+    it('should also be possible if there is no id yet', () => {
+      const customfield = CustomTextField.createFromObject({
+        id: null,
+        key: 'some key',
+        value: 'test test test'
+      })
+      const serializer = new CustomTextFieldSerializer()
+      const deviceId = '456'
+
+      const jsonApiPayload = serializer.convertModelToJsonApiData(customfield, deviceId)
+
+      expect(jsonApiPayload).not.toHaveProperty('id')
+      expect(jsonApiPayload).toHaveProperty('type')
+      expect(jsonApiPayload.type).toEqual('customfield')
+      expect(jsonApiPayload).toHaveProperty('attributes')
+      expect(jsonApiPayload.attributes).toHaveProperty('key')
+      expect(jsonApiPayload.attributes.key).toEqual('some key')
+      expect(jsonApiPayload.attributes).toHaveProperty('value')
+      expect(jsonApiPayload.attributes.value).toEqual('test test test')
+      expect(jsonApiPayload).toHaveProperty('relationships')
+      expect(jsonApiPayload.relationships).toHaveProperty('device')
+      expect(jsonApiPayload.relationships.device).toHaveProperty('data')
+      const deviceData: any = jsonApiPayload.relationships.device.data
+      expect(deviceData).toHaveProperty('id')
+      expect(deviceData.id).toEqual('456')
+      expect(deviceData).toHaveProperty('type')
+      expect(deviceData.type).toEqual('device')
+    })
+  })
+  describe('#convertJsonApiDataToModel', () => {
+    it('should convert an example payload to a model', () => {
+      const data = {
+        id: '123',
+        type: 'customfield',
+        attributes: {
+          key: 'institute',
+          value: 'GFZ'
+        }
+      }
+
+      const serializer = new CustomTextFieldSerializer()
+      const model = serializer.convertJsonApiDataToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.key).toEqual('institute')
+      expect(model.value).toEqual('GFZ')
+    })
+    it('should also convert missing values to empty strings', () => {
+      const data = {
+        id: '123',
+        type: 'customfield',
+        attributes: {
+        }
+      }
+
+      const serializer = new CustomTextFieldSerializer()
+      const model = serializer.convertJsonApiDataToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.key).toEqual('')
+      expect(model.value).toEqual('')
+    })
+  })
+  describe('#convertJsonApiObjectListToModelList', () => {
+    it('should convert two paylods to customTextField models', () => {
+      const data: IJsonApiObjectList = {
+        data: [
+          {
+            id: '123',
+            type: 'customfield',
+            attributes: {
+              key: 'Website GFZ',
+              value: 'www.gfz-potsdam.de'
+            },
+            relationships: {}
+          }, {
+            id: '124',
+            type: 'customfield',
+            attributes: {
+              key: 'Website UFZ',
+              value: 'www.ufz.de'
+            },
+            relationships: {}
+          }
+        ],
+        included: []
+      }
+
+      const serializer = new CustomTextFieldSerializer()
+      const models = serializer.convertJsonApiObjectListToModelList(data)
+
+      expect(models.length).toEqual(2)
+      expect(models[0].id).toEqual('123')
+      expect(models[0].key).toEqual('Website GFZ')
+      expect(models[0].value).toEqual('www.gfz-potsdam.de')
+
+      expect(models[1].id).toEqual('124')
+      expect(models[1].key).toEqual('Website UFZ')
+      expect(models[1].value).toEqual('www.ufz.de')
+    })
+  })
+  describe('#convertJsonApiObjectToModel', () => {
+    it('should convert an example payload to a model', () => {
+      const data = {
+        data: {
+          id: '123',
+          type: 'customfield',
+          attributes: {
+            key: 'institute',
+            value: 'GFZ'
+          },
+          relationships: {}
+        },
+        included: []
+      }
+
+      const serializer = new CustomTextFieldSerializer()
+      const model = serializer.convertJsonApiObjectToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.key).toEqual('institute')
+      expect(model.value).toEqual('GFZ')
+    })
   })
 })
diff --git a/test/serializers/jsonapi/DeviceAttachmentSerializer.test.ts b/test/serializers/jsonapi/DeviceAttachmentSerializer.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e1c2fdc61214e5044af25f13f58105c9ec68bf6f
--- /dev/null
+++ b/test/serializers/jsonapi/DeviceAttachmentSerializer.test.ts
@@ -0,0 +1,171 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2021
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+
+import { Attachment } from '@/models/Attachment'
+import { DeviceAttachmentSerializer } from '@/serializers/jsonapi/DeviceAttachmentSerializer'
+
+describe('DeviceAttachmetnSerializer', () => {
+  describe('#convertJsonApiObjectListToModelList', () => {
+    it('should create a list of attachments', () => {
+      const data = {
+        data: [{
+          id: '123',
+          type: 'device_attachment',
+          attributes: {
+            url: 'https://www.gfz-potsdam.de',
+            label: 'GFZ Homepage'
+          },
+          relationships: {}
+        }, {
+          id: '456',
+          type: 'device_attachment',
+          attributes: {
+            url: 'https://www.ufz.de',
+            label: 'UFZ Homepage'
+          },
+          relationships: {}
+        }],
+        included: []
+      }
+      const serializer = new DeviceAttachmentSerializer()
+      const models = serializer.convertJsonApiObjectListToModelList(data)
+
+      expect(models.length).toEqual(2)
+      expect(models[0].id).toEqual('123')
+      expect(models[0].url).toEqual('https://www.gfz-potsdam.de')
+      expect(models[0].label).toEqual('GFZ Homepage')
+      expect(models[1].id).toEqual('456')
+      expect(models[1].url).toEqual('https://www.ufz.de')
+      expect(models[1].label).toEqual('UFZ Homepage')
+    })
+  })
+  describe('#convertJsonApiObjectToModel', () => {
+    it('should create the model from the json:api object', () => {
+      const data = {
+        data: {
+          id: '123',
+          type: 'device_attachment',
+          attributes: {
+            url: 'https://www.gfz-potsdam.de',
+            label: 'GFZ Homepage'
+          },
+          relationships: {}
+        },
+        included: []
+      }
+
+      const serializer = new DeviceAttachmentSerializer()
+      const model = serializer.convertJsonApiObjectToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.url).toEqual('https://www.gfz-potsdam.de')
+      expect(model.label).toEqual('GFZ Homepage')
+    })
+    it('should also fill the attributes with empty strings if missing', () => {
+      const data = {
+        data: {
+          id: '123',
+          type: 'device_attachment',
+          attributes: {},
+          relationships: {}
+        },
+        included: []
+      }
+
+      const serializer = new DeviceAttachmentSerializer()
+      const model = serializer.convertJsonApiObjectToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.url).toEqual('')
+      expect(model.label).toEqual('')
+    })
+  })
+  describe('#convertModelToJsonApiData', () => {
+    it('should convert an attachment to a json:api payload', () => {
+      const attachment = Attachment.createFromObject({
+        id: '123',
+        url: 'https://www.ufz.de',
+        label: 'UFZ Homepage'
+      })
+      const serializer = new DeviceAttachmentSerializer()
+      const deviceId = '456'
+
+      const jsonApiPayload = serializer.convertModelToJsonApiData(attachment, deviceId)
+
+      expect(jsonApiPayload).toHaveProperty('id')
+      expect(jsonApiPayload.id).toEqual('123')
+      expect(jsonApiPayload).toHaveProperty('type')
+      expect(jsonApiPayload.type).toEqual('device_attachment')
+      expect(jsonApiPayload).toHaveProperty('attributes')
+      expect(jsonApiPayload.attributes).toHaveProperty('url')
+      expect(jsonApiPayload.attributes.url).toEqual('https://www.ufz.de')
+      expect(jsonApiPayload.attributes).toHaveProperty('label')
+      expect(jsonApiPayload.attributes.label).toEqual('UFZ Homepage')
+      expect(jsonApiPayload).toHaveProperty('relationships')
+      expect(jsonApiPayload.relationships).toHaveProperty('device')
+      expect(jsonApiPayload.relationships.device).toHaveProperty('data')
+      const deviceData: any = jsonApiPayload.relationships.device.data
+      expect(deviceData).toHaveProperty('id')
+      expect(deviceData.id).toEqual('456')
+      expect(deviceData).toHaveProperty('type')
+      expect(deviceData.type).toEqual('device')
+    })
+    it('should also work if we don\'t have an id yet', () => {
+      const attachment = Attachment.createFromObject({
+        id: null,
+        url: 'https://www.ufz.de',
+        label: 'UFZ Homepage'
+      })
+      const serializer = new DeviceAttachmentSerializer()
+      const deviceId = '456'
+
+      const jsonApiPayload = serializer.convertModelToJsonApiData(attachment, deviceId)
+
+      expect(jsonApiPayload).not.toHaveProperty('id')
+      expect(jsonApiPayload).toHaveProperty('type')
+      expect(jsonApiPayload.type).toEqual('device_attachment')
+      expect(jsonApiPayload).toHaveProperty('attributes')
+      expect(jsonApiPayload.attributes).toHaveProperty('url')
+      expect(jsonApiPayload.attributes.url).toEqual('https://www.ufz.de')
+      expect(jsonApiPayload.attributes).toHaveProperty('label')
+      expect(jsonApiPayload.attributes.label).toEqual('UFZ Homepage')
+      expect(jsonApiPayload).toHaveProperty('relationships')
+      expect(jsonApiPayload.relationships).toHaveProperty('device')
+      expect(jsonApiPayload.relationships.device).toHaveProperty('data')
+      const deviceData: any = jsonApiPayload.relationships.device.data
+      expect(deviceData).toHaveProperty('id')
+      expect(deviceData.id).toEqual('456')
+      expect(deviceData).toHaveProperty('type')
+      expect(deviceData.type).toEqual('device')
+    })
+  })
+})
diff --git a/test/serializers/jsonapi/DevicePropertySerializer.test.ts b/test/serializers/jsonapi/DevicePropertySerializer.test.ts
index 55410c34b037fa93db43224190b6069dcf59c1a9..f44ddd267720e91ce7884261b0e8f3b04c162735 100644
--- a/test/serializers/jsonapi/DevicePropertySerializer.test.ts
+++ b/test/serializers/jsonapi/DevicePropertySerializer.test.ts
@@ -3,7 +3,7 @@
  * Web client of the Sensor Management System software developed within
  * the Helmholtz DataHub Initiative by GFZ and UFZ.
  *
- * Copyright (C) 2020
+ * Copyright (C) 2020, 2021
  * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
  * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
  * - Helmholtz Centre Potsdam - GFZ German Research Centre for
@@ -270,4 +270,326 @@ describe('DevicePropertySerializer', () => {
       })
     })
   })
+  describe('#convertJsonApiObjectListToModelList', () => {
+    it('should create a list of device properties', () => {
+      const data = {
+        data: [{
+          id: '123',
+          type: 'device_property',
+          attributes: {
+            compartment_name: 'Climate',
+            unit_uri: 'abc',
+            sampling_media_name: 'Other',
+            compartment_uri: 'variabletype/Climate',
+            property_name: 'Water vapor concentration',
+            accuracy: 0.1,
+            measuring_range_min: 10,
+            measuring_range_max: -10,
+            label: 'water vapor',
+            property_uri: 'variablename/Water%20vapor%20concentration',
+            unit_name: '%',
+            failure_value: -234,
+            sampling_media_uri: 'medium/Other',
+            resolution: 0.001,
+            resolution_unit_uri: 'http://foo/unit/1',
+            resolution_unit_name: 'mm'
+          },
+          relationships: {}
+        }, {
+          id: '456',
+          type: 'device_property',
+          attributes: {
+            compartment_name: 'Resources',
+            unit_uri: 'xyz',
+            sampling_media_name: 'ether',
+            compartment_uri: 'variabletype/Resources',
+            property_name: 'abc',
+            accuracy: -2.9,
+            measuring_range_min: 100,
+            measuring_range_max: -100,
+            label: 'abc - prop',
+            property_uri: 'variablename/abc',
+            unit_name: 'n',
+            failure_value: -999,
+            sampling_media_uri: 'medium/ether',
+            resolution: 0.008,
+            resolution_unit_uri: 'http://foo/unit/100',
+            resolution_unit_name: '°C'
+          },
+          relationships: {}
+        }],
+        included: []
+      }
+      const serializer = new DevicePropertySerializer()
+      const models = serializer.convertJsonApiObjectListToModelList(data)
+
+      expect(models.length).toEqual(2)
+      expect(models[0].id).toEqual('123')
+      expect(models[0].compartmentName).toEqual('Climate')
+      expect(models[0].unitUri).toEqual('abc')
+      expect(models[0].samplingMediaName).toEqual('Other')
+      expect(models[0].compartmentUri).toEqual('variabletype/Climate')
+      expect(models[0].propertyName).toEqual('Water vapor concentration')
+      expect(models[0].accuracy).toEqual(0.1)
+      // I know it is the wrong ordering
+      expect(models[0].measuringRange.min).toEqual(10)
+      expect(models[0].measuringRange.max).toEqual(-10)
+      expect(models[0].label).toEqual('water vapor')
+      expect(models[0].propertyUri).toEqual('variablename/Water%20vapor%20concentration')
+      expect(models[0].unitName).toEqual('%')
+      expect(models[0].failureValue).toEqual(-234)
+      expect(models[0].samplingMediaUri).toEqual('medium/Other')
+      expect(models[0].resolution).toEqual(0.001)
+      expect(models[0].resolutionUnitUri).toEqual('http://foo/unit/1')
+      expect(models[0].resolutionUnitName).toEqual('mm')
+
+      expect(models[1].id).toEqual('456')
+      expect(models[1].compartmentName).toEqual('Resources')
+      expect(models[1].unitUri).toEqual('xyz')
+      expect(models[1].samplingMediaName).toEqual('ether')
+      expect(models[1].compartmentUri).toEqual('variabletype/Resources')
+      expect(models[1].propertyName).toEqual('abc')
+      expect(models[1].accuracy).toEqual(-2.9)
+      expect(models[1].measuringRange.min).toEqual(100)
+      expect(models[1].measuringRange.max).toEqual(-100)
+      expect(models[1].label).toEqual('abc - prop')
+      expect(models[1].propertyUri).toEqual('variablename/abc')
+      expect(models[1].unitName).toEqual('n')
+      expect(models[1].failureValue).toEqual(-999)
+      expect(models[1].samplingMediaUri).toEqual('medium/ether')
+      expect(models[1].resolution).toEqual(0.008)
+      expect(models[1].resolutionUnitUri).toEqual('http://foo/unit/100')
+      expect(models[1].resolutionUnitName).toEqual('°C')
+    })
+  })
+  describe('#convertJsonApiObjectToModel', () => {
+    it('should create the model from the json:api object', () => {
+      const data = {
+        data: {
+          id: '123',
+          type: 'device_property',
+          attributes: {
+            compartment_name: 'Climate',
+            unit_uri: 'abc',
+            sampling_media_name: 'Other',
+            compartment_uri: 'variabletype/Climate',
+            property_name: 'Water vapor concentration',
+            accuracy: 0.1,
+            measuring_range_min: 10,
+            measuring_range_max: -10,
+            label: 'water vapor',
+            property_uri: 'variablename/Water%20vapor%20concentration',
+            unit_name: '%',
+            failure_value: -234,
+            sampling_media_uri: 'medium/Other',
+            resolution: 0.001,
+            resolution_unit_uri: 'http://foo/unit/1',
+            resolution_unit_name: 'mm'
+          },
+          relationships: {}
+        },
+        included: []
+      }
+      const serializer = new DevicePropertySerializer()
+      const model = serializer.convertJsonApiObjectToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.compartmentName).toEqual('Climate')
+      expect(model.unitUri).toEqual('abc')
+      expect(model.samplingMediaName).toEqual('Other')
+      expect(model.compartmentUri).toEqual('variabletype/Climate')
+      expect(model.propertyName).toEqual('Water vapor concentration')
+      expect(model.accuracy).toEqual(0.1)
+      expect(model.measuringRange.min).toEqual(10)
+      expect(model.measuringRange.max).toEqual(-10)
+      expect(model.label).toEqual('water vapor')
+      expect(model.propertyUri).toEqual('variablename/Water%20vapor%20concentration')
+      expect(model.unitName).toEqual('%')
+      expect(model.failureValue).toEqual(-234)
+      expect(model.samplingMediaUri).toEqual('medium/Other')
+      expect(model.resolution).toEqual(0.001)
+      expect(model.resolutionUnitUri).toEqual('http://foo/unit/1')
+      expect(model.resolutionUnitName).toEqual('mm')
+    })
+    it('should also fill the attributes with empty strings if missing', () => {
+      const data = {
+        data: {
+          id: '123',
+          type: 'device_property',
+          attributes: {},
+          relationships: {}
+        },
+        included: []
+      }
+      const serializer = new DevicePropertySerializer()
+      const model = serializer.convertJsonApiObjectToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.compartmentName).toEqual('')
+      expect(model.unitUri).toEqual('')
+      expect(model.samplingMediaName).toEqual('')
+      expect(model.compartmentUri).toEqual('')
+      expect(model.propertyName).toEqual('')
+      expect(model.accuracy).toEqual(null)
+      expect(model.measuringRange.min).toEqual(null)
+      expect(model.measuringRange.max).toEqual(null)
+      expect(model.label).toEqual('')
+      expect(model.propertyUri).toEqual('')
+      expect(model.unitName).toEqual('')
+      expect(model.failureValue).toEqual(null)
+      expect(model.samplingMediaUri).toEqual('')
+      expect(model.resolution).toEqual(null)
+      expect(model.resolutionUnitUri).toEqual('')
+      expect(model.resolutionUnitName).toEqual('')
+    })
+  })
+  describe('#convertModelToJsonApiData', () => {
+    it('should convert device prooperty to a json:api payload', () => {
+      const deviceProperty = DeviceProperty.createFromObject({
+        id: '123',
+        compartmentName: 'Climate',
+        unitUri: 'abc',
+        samplingMediaName: 'Other',
+        compartmentUri: 'variabletype/Climate',
+        propertyName: 'Water vapor concentration',
+        accuracy: 0.1,
+        measuringRange: MeasuringRange.createFromObject({
+          min: 10,
+          max: -10
+        }),
+        label: 'water vapor',
+        propertyUri: 'variablename/Water%20vapor%20concentration',
+        unitName: '%',
+        failureValue: -234,
+        samplingMediaUri: 'medium/Other',
+        resolution: 0.001,
+        resolutionUnitUri: 'http://foo/unit/1',
+        resolutionUnitName: 'mm'
+      })
+      const serializer = new DevicePropertySerializer()
+      const deviceId = '456'
+
+      const jsonApiPayload = serializer.convertModelToJsonApiData(deviceProperty, deviceId)
+
+      expect(jsonApiPayload).toHaveProperty('id')
+      expect(jsonApiPayload.id).toEqual('123')
+      expect(jsonApiPayload).toHaveProperty('type')
+      expect(jsonApiPayload.type).toEqual('device_property')
+      expect(jsonApiPayload).toHaveProperty('attributes')
+      expect(jsonApiPayload.attributes).toHaveProperty('compartment_name')
+      expect(jsonApiPayload.attributes.compartment_name).toEqual('Climate')
+      expect(jsonApiPayload.attributes).toHaveProperty('unit_uri')
+      expect(jsonApiPayload.attributes.unit_uri).toEqual('abc')
+      expect(jsonApiPayload.attributes).toHaveProperty('sampling_media_name')
+      expect(jsonApiPayload.attributes.sampling_media_name).toEqual('Other')
+      expect(jsonApiPayload.attributes).toHaveProperty('compartment_uri')
+      expect(jsonApiPayload.attributes.compartment_uri).toEqual('variabletype/Climate')
+      expect(jsonApiPayload.attributes).toHaveProperty('property_name')
+      expect(jsonApiPayload.attributes.property_name).toEqual('Water vapor concentration')
+      expect(jsonApiPayload.attributes).toHaveProperty('accuracy')
+      expect(jsonApiPayload.attributes.accuracy).toEqual(0.1)
+      expect(jsonApiPayload.attributes).toHaveProperty('measuring_range_min')
+      expect(jsonApiPayload.attributes.measuring_range_min).toEqual(10)
+      expect(jsonApiPayload.attributes).toHaveProperty('measuring_range_max')
+      expect(jsonApiPayload.attributes.measuring_range_max).toEqual(-10)
+      expect(jsonApiPayload.attributes).toHaveProperty('label')
+      expect(jsonApiPayload.attributes.label).toEqual('water vapor')
+      expect(jsonApiPayload.attributes).toHaveProperty('property_uri')
+      expect(jsonApiPayload.attributes.property_uri).toEqual('variablename/Water%20vapor%20concentration')
+      expect(jsonApiPayload.attributes).toHaveProperty('unit_name')
+      expect(jsonApiPayload.attributes.unit_name).toEqual('%')
+      expect(jsonApiPayload.attributes).toHaveProperty('failure_value')
+      expect(jsonApiPayload.attributes.failure_value).toEqual(-234)
+      expect(jsonApiPayload.attributes).toHaveProperty('sampling_media_uri')
+      expect(jsonApiPayload.attributes.sampling_media_uri).toEqual('medium/Other')
+      expect(jsonApiPayload.attributes).toHaveProperty('resolution')
+      expect(jsonApiPayload.attributes.resolution).toEqual(0.001)
+      expect(jsonApiPayload.attributes).toHaveProperty('resolution_unit_uri')
+      expect(jsonApiPayload.attributes.resolution_unit_uri).toEqual('http://foo/unit/1')
+      expect(jsonApiPayload.attributes).toHaveProperty('resolution_unit_name')
+      expect(jsonApiPayload.attributes.resolution_unit_name).toEqual('mm')
+
+      expect(jsonApiPayload).toHaveProperty('relationships')
+      expect(jsonApiPayload.relationships).toHaveProperty('device')
+      expect(jsonApiPayload.relationships.device).toHaveProperty('data')
+      const deviceData: any = jsonApiPayload.relationships.device.data
+      expect(deviceData).toHaveProperty('id')
+      expect(deviceData.id).toEqual('456')
+      expect(deviceData).toHaveProperty('type')
+      expect(deviceData.type).toEqual('device')
+    })
+    it('should also work if we don\'t have an id yet', () => {
+      const deviceProperty = DeviceProperty.createFromObject({
+        id: null,
+        compartmentName: 'Climate',
+        unitUri: 'abc',
+        samplingMediaName: 'Other',
+        compartmentUri: 'variabletype/Climate',
+        propertyName: 'Water vapor concentration',
+        accuracy: 0.1,
+        measuringRange: MeasuringRange.createFromObject({
+          min: 10,
+          max: -10
+        }),
+        label: 'water vapor',
+        propertyUri: 'variablename/Water%20vapor%20concentration',
+        unitName: '%',
+        failureValue: -234,
+        samplingMediaUri: 'medium/Other',
+        resolution: 0.001,
+        resolutionUnitUri: 'http://foo/unit/1',
+        resolutionUnitName: 'mm'
+      })
+      const serializer = new DevicePropertySerializer()
+      const deviceId = '456'
+
+      const jsonApiPayload = serializer.convertModelToJsonApiData(deviceProperty, deviceId)
+
+      expect(jsonApiPayload).not.toHaveProperty('id')
+      expect(jsonApiPayload).toHaveProperty('type')
+      expect(jsonApiPayload.type).toEqual('device_property')
+      expect(jsonApiPayload).toHaveProperty('attributes')
+      expect(jsonApiPayload.attributes).toHaveProperty('compartment_name')
+      expect(jsonApiPayload.attributes.compartment_name).toEqual('Climate')
+      expect(jsonApiPayload.attributes).toHaveProperty('unit_uri')
+      expect(jsonApiPayload.attributes.unit_uri).toEqual('abc')
+      expect(jsonApiPayload.attributes).toHaveProperty('sampling_media_name')
+      expect(jsonApiPayload.attributes.sampling_media_name).toEqual('Other')
+      expect(jsonApiPayload.attributes).toHaveProperty('compartment_uri')
+      expect(jsonApiPayload.attributes.compartment_uri).toEqual('variabletype/Climate')
+      expect(jsonApiPayload.attributes).toHaveProperty('property_name')
+      expect(jsonApiPayload.attributes.property_name).toEqual('Water vapor concentration')
+      expect(jsonApiPayload.attributes).toHaveProperty('accuracy')
+      expect(jsonApiPayload.attributes.accuracy).toEqual(0.1)
+      expect(jsonApiPayload.attributes).toHaveProperty('measuring_range_min')
+      expect(jsonApiPayload.attributes.measuring_range_min).toEqual(10)
+      expect(jsonApiPayload.attributes).toHaveProperty('measuring_range_max')
+      expect(jsonApiPayload.attributes.measuring_range_max).toEqual(-10)
+      expect(jsonApiPayload.attributes).toHaveProperty('label')
+      expect(jsonApiPayload.attributes.label).toEqual('water vapor')
+      expect(jsonApiPayload.attributes).toHaveProperty('property_uri')
+      expect(jsonApiPayload.attributes.property_uri).toEqual('variablename/Water%20vapor%20concentration')
+      expect(jsonApiPayload.attributes).toHaveProperty('unit_name')
+      expect(jsonApiPayload.attributes.unit_name).toEqual('%')
+      expect(jsonApiPayload.attributes).toHaveProperty('failure_value')
+      expect(jsonApiPayload.attributes.failure_value).toEqual(-234)
+      expect(jsonApiPayload.attributes).toHaveProperty('sampling_media_uri')
+      expect(jsonApiPayload.attributes.sampling_media_uri).toEqual('medium/Other')
+      expect(jsonApiPayload.attributes).toHaveProperty('resolution')
+      expect(jsonApiPayload.attributes.resolution).toEqual(0.001)
+      expect(jsonApiPayload.attributes).toHaveProperty('resolution_unit_uri')
+      expect(jsonApiPayload.attributes.resolution_unit_uri).toEqual('http://foo/unit/1')
+      expect(jsonApiPayload.attributes).toHaveProperty('resolution_unit_name')
+      expect(jsonApiPayload.attributes.resolution_unit_name).toEqual('mm')
+
+      expect(jsonApiPayload).toHaveProperty('relationships')
+      expect(jsonApiPayload.relationships).toHaveProperty('device')
+      expect(jsonApiPayload.relationships.device).toHaveProperty('data')
+      const deviceData: any = jsonApiPayload.relationships.device.data
+      expect(deviceData).toHaveProperty('id')
+      expect(deviceData.id).toEqual('456')
+      expect(deviceData).toHaveProperty('type')
+      expect(deviceData.type).toEqual('device')
+    })
+  })
 })
diff --git a/test/serializers/jsonapi/PlatformAttachmentSerializer.test.ts b/test/serializers/jsonapi/PlatformAttachmentSerializer.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e41f5f3e487c6f69eeae8273428762837e6d6032
--- /dev/null
+++ b/test/serializers/jsonapi/PlatformAttachmentSerializer.test.ts
@@ -0,0 +1,171 @@
+/**
+ * @license
+ * Web client of the Sensor Management System software developed within
+ * the Helmholtz DataHub Initiative by GFZ and UFZ.
+ *
+ * Copyright (C) 2021
+ * - Nils Brinckmann (GFZ, nils.brinckmann@gfz-potsdam.de)
+ * - Marc Hanisch (GFZ, marc.hanisch@gfz-potsdam.de)
+ * - Helmholtz Centre Potsdam - GFZ German Research Centre for
+ *   Geosciences (GFZ, https://www.gfz-potsdam.de)
+ *
+ * Parts of this program were developed within the context of the
+ * following publicly funded projects or measures:
+ * - Helmholtz Earth and Environment DataHub
+ *   (https://www.helmholtz.de/en/research/earth_and_environment/initiatives/#h51095)
+ *
+ * Licensed under the HEESIL, Version 1.0 or - as soon they will be
+ * approved by the "Community" - subsequent versions of the HEESIL
+ * (the "Licence").
+ *
+ * You may not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://gitext.gfz-potsdam.de/software/heesil
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the Licence for the specific language governing
+ * permissions and limitations under the Licence.
+ */
+
+import { Attachment } from '@/models/Attachment'
+import { PlatformAttachmentSerializer } from '@/serializers/jsonapi/PlatformAttachmentSerializer'
+
+describe('PlatformAttachmentSerializer', () => {
+  describe('#convertJsonApiObjectListToModelList', () => {
+    it('should create a list of attachments', () => {
+      const data = {
+        data: [{
+          id: '123',
+          type: 'platform_attachment',
+          attributes: {
+            url: 'https://www.gfz-potsdam.de',
+            label: 'GFZ Homepage'
+          },
+          relationships: {}
+        }, {
+          id: '456',
+          type: 'platform_attachment',
+          attributes: {
+            url: 'https://www.ufz.de',
+            label: 'UFZ Homepage'
+          },
+          relationships: {}
+        }],
+        included: []
+      }
+      const serializer = new PlatformAttachmentSerializer()
+      const models = serializer.convertJsonApiObjectListToModelList(data)
+
+      expect(models.length).toEqual(2)
+      expect(models[0].id).toEqual('123')
+      expect(models[0].url).toEqual('https://www.gfz-potsdam.de')
+      expect(models[0].label).toEqual('GFZ Homepage')
+      expect(models[1].id).toEqual('456')
+      expect(models[1].url).toEqual('https://www.ufz.de')
+      expect(models[1].label).toEqual('UFZ Homepage')
+    })
+  })
+  describe('#convertJsonApiObjectToModel', () => {
+    it('should create the model from the json:api object', () => {
+      const data = {
+        data: {
+          id: '123',
+          type: 'platform_attachment',
+          attributes: {
+            url: 'https://www.gfz-potsdam.de',
+            label: 'GFZ Homepage'
+          },
+          relationships: {}
+        },
+        included: []
+      }
+
+      const serializer = new PlatformAttachmentSerializer()
+      const model = serializer.convertJsonApiObjectToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.url).toEqual('https://www.gfz-potsdam.de')
+      expect(model.label).toEqual('GFZ Homepage')
+    })
+    it('should also fill the attributes with empty strings if missing', () => {
+      const data = {
+        data: {
+          id: '123',
+          type: 'device_attachment',
+          attributes: {},
+          relationships: {}
+        },
+        included: []
+      }
+
+      const serializer = new PlatformAttachmentSerializer()
+      const model = serializer.convertJsonApiObjectToModel(data)
+
+      expect(model.id).toEqual('123')
+      expect(model.url).toEqual('')
+      expect(model.label).toEqual('')
+    })
+  })
+  describe('#convertModelToJsonApiData', () => {
+    it('should convert an attachment to a json:api payload', () => {
+      const attachment = Attachment.createFromObject({
+        id: '123',
+        url: 'https://www.ufz.de',
+        label: 'UFZ Homepage'
+      })
+      const serializer = new PlatformAttachmentSerializer()
+      const platformId = '456'
+
+      const jsonApiPayload = serializer.convertModelToJsonApiData(attachment, platformId)
+
+      expect(jsonApiPayload).toHaveProperty('id')
+      expect(jsonApiPayload.id).toEqual('123')
+      expect(jsonApiPayload).toHaveProperty('type')
+      expect(jsonApiPayload.type).toEqual('platform_attachment')
+      expect(jsonApiPayload).toHaveProperty('attributes')
+      expect(jsonApiPayload.attributes).toHaveProperty('url')
+      expect(jsonApiPayload.attributes.url).toEqual('https://www.ufz.de')
+      expect(jsonApiPayload.attributes).toHaveProperty('label')
+      expect(jsonApiPayload.attributes.label).toEqual('UFZ Homepage')
+      expect(jsonApiPayload).toHaveProperty('relationships')
+      expect(jsonApiPayload.relationships).toHaveProperty('platform')
+      expect(jsonApiPayload.relationships.platform).toHaveProperty('data')
+      const platformData: any = jsonApiPayload.relationships.platform.data
+      expect(platformData).toHaveProperty('id')
+      expect(platformData.id).toEqual('456')
+      expect(platformData).toHaveProperty('type')
+      expect(platformData.type).toEqual('platform')
+    })
+    it('should also work if we don\'t have an id yet', () => {
+      const attachment = Attachment.createFromObject({
+        id: null,
+        url: 'https://www.ufz.de',
+        label: 'UFZ Homepage'
+      })
+      const serializer = new PlatformAttachmentSerializer()
+      const platformId = '456'
+
+      const jsonApiPayload = serializer.convertModelToJsonApiData(attachment, platformId)
+
+      expect(jsonApiPayload).not.toHaveProperty('id')
+      expect(jsonApiPayload).toHaveProperty('type')
+      expect(jsonApiPayload.type).toEqual('platform_attachment')
+      expect(jsonApiPayload).toHaveProperty('attributes')
+      expect(jsonApiPayload.attributes).toHaveProperty('url')
+      expect(jsonApiPayload.attributes.url).toEqual('https://www.ufz.de')
+      expect(jsonApiPayload.attributes).toHaveProperty('label')
+      expect(jsonApiPayload.attributes.label).toEqual('UFZ Homepage')
+      expect(jsonApiPayload).toHaveProperty('relationships')
+      expect(jsonApiPayload.relationships).toHaveProperty('platform')
+      expect(jsonApiPayload.relationships.platform).toHaveProperty('data')
+      const platformData: any = jsonApiPayload.relationships.platform.data
+      expect(platformData).toHaveProperty('id')
+      expect(platformData.id).toEqual('456')
+      expect(platformData).toHaveProperty('type')
+      expect(platformData.type).toEqual('platform')
+    })
+  })
+})