diff --git a/src/caosdb/high_level_api.py b/src/caosdb/high_level_api.py
index 21e0b19a26e9a85b464b3defb59f4cf443eb8bd7..365cd1331a7a88d0931ecab8e4d3ec79e28563c2 100644
--- a/src/caosdb/high_level_api.py
+++ b/src/caosdb/high_level_api.py
@@ -31,15 +31,16 @@ This is refactored from apiutils.
 """
 
 from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, FILE, INTEGER,
-                                    REFERENCE, TEXT)
+                                    REFERENCE, TEXT, is_list_datatype, get_list_datatype)
 import caosdb as db
 
 from .apiutils import get_type_of_entity_with
 
-from typing import Any, Optional
+from typing import Any, Optional, List, Union, Dict
 
 from dataclasses import dataclass
 
+
 @dataclass
 class CaosDBPropertyMetaData:
     # name is already the name of the attribute
@@ -79,38 +80,42 @@ class CaosDBPythonEntity(object):
         """
         Initialize a new CaosDBPythonEntity for the high level python api.
 
-        The new entity is initialized with a new negative ID using _get_id (static).
-        """
-        
-        # Save a copy of the dry state
-        # of this object in order to be
-        # able to detect conflicts.
-        self.do_not_expand = False
-        self._parents = []
-        self._id = CaosDBPythonEntity._get_id()
-        self._path = None
-        self._file = None
-        
-        # TODO:
-        # 3.) resolve references up to a specific depth (including infinity)
-        # 4.) resolve parents function -> partially implemented by
-        # get_parent_names
-        
-        self._references = {}
-        self._properties = set()
-        self._datatypes = {}
-        self._forbidden = dir(self)
+        Parents are either unresolved references or CaosDB RecordTypes.
 
-    @staticmethod
-    def _get_id():
+        Properties are stored directly as attributes for the object.
+        Property metadata is maintained in a dctionary _properties_metadata that should
+        never be accessed directly, but only using the get_property_metadata function.
+        If property values are references to other objects, they will be stored as
+        CaosDBPythonUnresolvedReference objects that can be resolved later into
+        CaosDBPythonRecords.
         """
-        Get a new negative ID for a CaosDB Entity.
 
-        The first ID is -1 and decremented with each call of this function.
-        """
-        CaosDBPythonEntity._last_id -= 1
+        # Parents are either unresolved references or CaosDB RecordTypes
+        self._parents: List[Union[
+            CaosDBPythonUnresolvedParent, CaosDBPythonRecordType]] = []
+        # self._id: int = CaosDBPythonEntity._get_new_id()
+        self._id: Optional[int] = None
+        self._name: Optional[str] = None
+        self._description: Optional[str] = None
+        self._version: Optional[str] = None
+
+        # name: name of property, value: property metadata
+        self._properties_metadata: Dict[CaosDBPropertyMetaData] = dict()
 
-        return CaosDBPythonEntity._last_id
+        # Store all current attributes as forbidden attributes
+        # which must not be changed by the set_property function.
+        self._forbidden = dir(self) + ["_forbidden"]
+
+    # @staticmethod
+    # def _get_new_id():
+    #     """
+    #     Get a new negative ID for a CaosDB Entity.
+
+    #     The first ID is -1 and decremented with each call of this function.
+    #     """
+    #     CaosDBPythonEntity._last_id -= 1
+
+    #     return CaosDBPythonEntity._last_id
 
     def _set_property_from_entity(self, ent: db.Entity):
         """
@@ -126,10 +131,37 @@ class CaosDBPythonEntity(object):
         val, reference = self._type_converted_value(val, pr)
         self.set_property(name, val, reference, datatype=pr)
 
+    def get_property_metadata(self, prop_name: str) -> CaosDBPropertyMetaData:
+        """
+        Retrieve the property metadata for the property with name prop_name.
+
+        If the property with the given name does not exist or is forbidden, raise an exception.
+        Else return the metadata associated with this property.
+
+        If no metadata does exist yet for the given property, a new object will be created
+        and returned.
+
+        prop_name: str
+                   Name of the property to retrieve metadata for.
+        """
+        
+        if not self.property_exists(prop_name):
+            raise RuntimeError("The property with name {} does not exist.".format(prop_name))
+
+        if prop_name not in self._properties_metadata:
+            self._properties_metadata[prop_name] = CaosDBPropertyMetaData()
+
+        return self._properties_metadata[prop_name]
+
+    def property_exists(self, prop_name: str):
+        """
+        Check whether a property exists already.
+        """
+        return prop_name not in self._forbidden and prop_name in self.__dict__
+
     def set_property(self,
                      name: str,
                      value: Any,
-                     is_reference: bool = False,
                      overwrite: bool = False,
                      datatype: Optional[str] = None):
         """
@@ -144,8 +176,6 @@ class CaosDBPythonEntity(object):
         
         value: Any
                Value of the property.
-
-        is_reference: bool
         
         overwrite: bool
                    Use this if you definitely only want one property with
@@ -156,8 +186,9 @@ class CaosDBPythonEntity(object):
 
         # Store the datatype (if given) in a hidden field
         # The datatypes will be stored in name, value pairs.
-        # TODO: check whether handling of lists is correct.
-        self._datatypes[name] = datatype
+        # TODO: check whether handling of lists and multi properties is correct.
+        metadata = self.get_property_metadata(name)
+        metadata.datatype = datatype
 
         if isinstance(name, db.Entity):
             # TODO: check, whether this is reasonable?
@@ -169,7 +200,7 @@ class CaosDBPythonEntity(object):
                                "Python representation. Name of property " +
                                name + " is forbidden!")
         
-        already_exists = (name in dir(self))
+        already_exists = self.property_exists(name)
 
         if already_exists and not overwrite:
             # each call to set_property checks first if it already exists
@@ -183,24 +214,12 @@ class CaosDBPythonEntity(object):
             else:
                 old_att = self.__getattribute__(name)
                 self.__setattr__(name, [old_att])
-
-                if is_reference:
-                    self._references[name] = [
-                        self._references[name]]
             att = self.__getattribute__(name)
             att.append(value)
-
-            if is_reference:
-                self._references[name].append(int(value))
         else:
-            if is_reference:
-                self._references[name] = value
             self.__setattr__(name, value)
 
-        if not (already_exists and overwrite):
-            self._properties.add(name)
-
-    add_property = set_property
+    # add_property = set_property
 
     def set_id(self, idx: int):
         """