diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h
index 6b96c907f1e24eae4894bc652173ffc8e52ee387..de6d4928771af83edca68d748d73b58f690a35f7 100644
--- a/include/caosdb/entity.h
+++ b/include/caosdb/entity.h
@@ -515,7 +515,7 @@ public:
    * Set the value of this property.
    */
   auto SetValue(const Value &value) -> StatusCode;
-  // auto SetValue(const AbstractValue &value) -> StatusCode;
+  auto SetValue(const AbstractValue &value) -> StatusCode;
   auto SetValue(const std::string &value) -> StatusCode;
   auto SetValue(const char *value) -> StatusCode;
   auto SetValue(const double value) -> StatusCode;
@@ -688,7 +688,7 @@ public:
    */
   auto SetDescription(const std::string &description) -> void;
 
-  // auto SetValue(const AbstractValue &value) -> StatusCode;
+  auto SetValue(const AbstractValue &value) -> StatusCode;
   auto SetValue(const Value &value) -> StatusCode;
   auto SetValue(const std::string &value) -> StatusCode;
   auto SetValue(const char *value) -> StatusCode;
diff --git a/include/caosdb/value.h b/include/caosdb/value.h
index f6de040dcf02fb1600f170f2e18817ed4e2db883..b7da9903eece341955bb1d9a93e3e89d72f866eb 100644
--- a/include/caosdb/value.h
+++ b/include/caosdb/value.h
@@ -158,6 +158,7 @@ public:
    * The return value is undefined if IsVector is false.
    */
   [[nodiscard]] virtual auto GetAsVector() const noexcept -> const std::vector<ScalarValue> & = 0;
+  [[nodiscard]] virtual auto ToString() const noexcept -> const std::string = 0;
   friend class Value;
 
 protected:
@@ -246,6 +247,11 @@ public:
     static const std::vector<ScalarValue> empty_collection;
     return empty_collection;
   }
+  inline auto ToString() const noexcept -> const std::string {
+    CAOSDB_DEBUG_MESSAGE_STRING(*wrapped, out)
+    return out;
+  }
+
   friend class Value;
 
 protected:
@@ -282,7 +288,9 @@ public:
   explicit inline Value(const ScalarValue &value) : Value() {
     this->wrapped->mutable_scalar_value()->CopyFrom(*value.wrapped);
   }
-  explicit inline Value(const AbstractValue &value) : Value(value.GetProtoValue()) {}
+  explicit inline Value(const AbstractValue &value) : Value() {
+    this->wrapped->CopyFrom(*value.GetProtoValue());
+  }
   explicit inline Value(ProtoValue *wrapped) : ProtoMessageWrapper<ProtoValue>(wrapped) {}
   explicit inline Value(const std::string &value) : ProtoMessageWrapper<ProtoValue>() {
     this->wrapped->mutable_scalar_value()->set_string_value(value);
diff --git a/include/ccaosdb.h b/include/ccaosdb.h
index 61b86817532b77ccfd24ccb791b18cc31afc6faa..b9296349b6f9592bed9e60362ae4848faf95a649 100644
--- a/include/ccaosdb.h
+++ b/include/ccaosdb.h
@@ -264,8 +264,6 @@ int caosdb_connection_connection_manager_get_connection(caosdb_connection_connec
  * ENTITY STUFF AND TRANSACTIONS
  ****************************************************************************/
 
-// TODO(fspreck) implementations needed, and probably these declarations are
-// not sufficient yet.
 typedef struct caosdb_transaction_transaction {
   void *wrapped_transaction;
   bool _deletable = false;
@@ -379,10 +377,8 @@ int caosdb_entity_entity_get_role(caosdb_entity_entity *entity, char **out);
 int caosdb_entity_entity_get_name(caosdb_entity_entity *entity, char **out);
 int caosdb_entity_entity_get_description(caosdb_entity_entity *entity, char **out);
 int caosdb_entity_entity_get_local_path(caosdb_entity_entity *entity, char **out);
-// TODO(fspreck) implementation
 int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, caosdb_entity_datatype *out);
 int caosdb_entity_entity_get_unit(caosdb_entity_entity *entity, char **out);
-// TODO(fspreck) implementation
 int caosdb_entity_entity_get_value(caosdb_entity_entity *entity, caosdb_entity_value *out);
 int caosdb_entity_entity_get_version_id(caosdb_entity_entity *entity, char **out);
 int caosdb_entity_entity_get_errors_size(caosdb_entity_entity *entity, int *out);
@@ -405,11 +401,9 @@ int caosdb_entity_property_get_id(caosdb_entity_property *property, char **out);
 int caosdb_entity_property_get_name(caosdb_entity_property *property, char **out);
 int caosdb_entity_property_get_description(caosdb_entity_property *property, char **out);
 int caosdb_entity_property_get_importance(caosdb_entity_property *property, char **out);
-// TODO(fspreck) implementation
 int caosdb_entity_property_get_datatype(caosdb_entity_property *property,
                                         caosdb_entity_datatype *out);
 int caosdb_entity_property_get_unit(caosdb_entity_property *property, char **out);
-// TODO(fspreck) implementation
 int caosdb_entity_property_get_value(caosdb_entity_property *property, caosdb_entity_value *out);
 int caosdb_entity_parent_get_id(caosdb_entity_parent *parent, char **out);
 int caosdb_entity_parent_get_name(caosdb_entity_parent *parent, char **out);
@@ -418,7 +412,12 @@ int caosdb_entity_parent_get_description(caosdb_entity_parent *parent, char **ou
 int caosdb_entity_message_get_code(caosdb_entity_message *message, int *out);
 int caosdb_entity_message_get_description(caosdb_entity_message *message, char **out);
 
-// TODO(fspreck) getters for value and datatypes
+int caosdb_entity_datatype_is_atomic(caosdb_entity_datatype *datatype, bool *out);
+int caosdb_entity_datatype_is_reference(caosdb_entity_datatype *datatype, bool *out);
+int caosdb_entity_datatype_is_list_of_atomic(caosdb_entity_datatype *datatype, bool *out);
+int caosdb_entity_datatype_is_list_of_reference(caosdb_entity_datatype *datatype, bool *out);
+int caosdb_entity_datatype_get_datatype_name(caosdb_entity_datatype *datatype, char **out);
+
 int caosdb_entity_value_is_null(caosdb_entity_value *value, bool *out);
 int caosdb_entity_value_is_string(caosdb_entity_value *value, bool *out);
 int caosdb_entity_value_is_double(caosdb_entity_value *value, bool *out);
@@ -440,9 +439,14 @@ int caosdb_entity_create_property(caosdb_entity_property *out);
 int caosdb_entity_delete_property(caosdb_entity_property *out);
 int caosdb_entity_create_parent(caosdb_entity_parent *out);
 int caosdb_entity_delete_parent(caosdb_entity_parent *out);
-// TODO(fspreck) implementations, also list_datatypes, list_values...
-int caosdb_entity_create_datatype(caosdb_entity_datatype *out);
+
+// DATATYPE CONSTRUCTORS for atomic and reference datatypes and lists thereof
+int caosdb_entity_create_atomic_datatype(caosdb_entity_datatype *out, const char *name);
+int caosdb_entity_create_reference_datatype(caosdb_entity_datatype *out, const char *name);
+int caosdb_entity_create_atomic_list_datatype(caosdb_entity_datatype *out, const char *name);
+int caosdb_entity_create_reference_list_datatype(caosdb_entity_datatype *out, const char *name);
 int caosdb_entity_delete_datatype(caosdb_entity_datatype *out);
+
 // VALUE CONSTRUCTORS (resolve overloaded constructors)
 int caosdb_entity_create_int_value(caosdb_entity_value *out, const int64_t value);
 int caosdb_entity_create_string_value(caosdb_entity_value *out, const char *value);
@@ -464,11 +468,9 @@ int caosdb_entity_entity_set_name(caosdb_entity_entity *entity, const char *name
 int caosdb_entity_entity_set_description(caosdb_entity_entity *entity, const char *description);
 int caosdb_entity_entity_set_local_path(caosdb_entity_entity *entity, const char *name);
 int caosdb_entity_entity_set_file_path(caosdb_entity_entity *entity, const char *name);
-// TODO(fspreck) implementation
 int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity,
                                       caosdb_entity_datatype *datatype);
 int caosdb_entity_entity_set_unit(caosdb_entity_entity *entity, const char *unit);
-// TODO(fspreck) implementation
 int caosdb_entity_entity_set_value(caosdb_entity_entity *entity, caosdb_entity_value *value);
 
 int caosdb_entity_entity_append_parent(caosdb_entity_entity *entity, caosdb_entity_parent *parent);
@@ -479,20 +481,15 @@ int caosdb_entity_entity_remove_property(caosdb_entity_entity *entity, int index
 
 int caosdb_entity_property_set_id(caosdb_entity_property *property, const char *id);
 int caosdb_entity_property_set_name(caosdb_entity_property *property, const char *name);
-// TODO(fspreck) implementation
 int caosdb_entity_property_set_datatype(caosdb_entity_property *property,
                                         caosdb_entity_datatype *datatype);
 int caosdb_entity_property_set_importance(caosdb_entity_property *property, const char *importance);
 int caosdb_entity_property_set_unit(caosdb_entity_property *property, const char *unit);
-
-// TODO(fspreck) implementation
 int caosdb_entity_property_set_value(caosdb_entity_property *property, caosdb_entity_value *value);
 
 int caosdb_entity_parent_set_id(caosdb_entity_parent *parent, const char *id);
 int caosdb_entity_parent_set_name(caosdb_entity_parent *parent, const char *name);
 
-// TODO(fspreck) setters for datatype
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp
index 8f737602bbda62bbae8e8dcf8f287e95fd82aa09..c409a46e8ad37aee93b67b2a36109b1fe8f8c640 100644
--- a/src/caosdb/entity.cpp
+++ b/src/caosdb/entity.cpp
@@ -108,8 +108,7 @@ auto Property::SetImportance(Importance importance) -> void {
 
 auto Property::SetValue(const Value &value) -> StatusCode { return this->value.CopyFrom(value); }
 
-// auto Property::SetValue(const AbstractValue &value) -> StatusCode { return
-// SetValue(Value(value)); }
+auto Property::SetValue(const AbstractValue &value) -> StatusCode { return SetValue(Value(value)); }
 
 auto Property::SetValue(const std::string &value) -> StatusCode { return SetValue(Value(value)); }
 
@@ -205,8 +204,7 @@ auto Entity::SetValue(const Value &value) -> StatusCode {
   return this->value.CopyFrom(value);
 }
 
-// auto Entity::SetValue(const AbstractValue &value) -> StatusCode { return SetValue(Value(value));
-// }
+auto Entity::SetValue(const AbstractValue &value) -> StatusCode { return SetValue(Value(value)); }
 
 auto Entity::SetValue(const std::string &value) -> StatusCode { return SetValue(Value(value)); }
 
diff --git a/src/ccaosdb.cpp b/src/ccaosdb.cpp
index 8a7ae4131f513d257e3c0c1b69f975fde595f9dc..a18c56d90a2b72449ef7fcb60c0dd3e79603dafc 100644
--- a/src/ccaosdb.cpp
+++ b/src/ccaosdb.cpp
@@ -48,6 +48,8 @@ extern "C" {
 
 #define WRAPPED_MESSAGE_CAST(name) static_cast<caosdb::entity::Message *>(name->wrapped_message)
 
+#define WRAPPED_DATATYPE_CAST(name) static_cast<caosdb::entity::DataType *>(name->wrapped_datatype)
+
 #define WRAPPED_VALUE_CAST(name) static_cast<caosdb::entity::AbstractValue *>(name->wrapped_value)
 
 #define ENUM_NAME_FROM_VALUE(arg, etype)                                                           \
@@ -56,6 +58,11 @@ extern "C" {
 #define ENUM_VALUE_FROM_NAME(arg, etype)                                                           \
   caosdb::utility::getEnumValueFromName<caosdb::entity::etype>(arg)
 
+#define RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(name)                                                \
+  if (name->_deletable) {                                                                          \
+    return caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR;                                          \
+  }
+
 /*
  * Macro for wrapping every function into a try-catch clause. If an exception
  * occurs, the given StatusCode is being returned.
@@ -156,9 +163,7 @@ extern "C" {
 #define CREATE_VALUE(fname, arg)                                                                   \
   ERROR_RETURN_CODE(GENERIC_ERROR,                                                                 \
                     int caosdb_entity_create_##fname(caosdb_entity_value *out, arg), {             \
-                      if (out->_deletable) {                                                       \
-                        return caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR;                      \
-                      }                                                                            \
+                      RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)                                   \
                       out->wrapped_value = new caosdb::entity::Value(value);                       \
                       out->_deletable = true;                                                      \
                       return 0;                                                                    \
@@ -683,6 +688,65 @@ ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_delete_parent(caosdb_entity_p
   return 0;
 })
 
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_create_atomic_datatype(caosdb_entity_datatype *out,
+                                                           const char *name),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    try {
+                      auto enum_value = ENUM_VALUE_FROM_NAME(std::string(name), AtomicDataType);
+                      out->wrapped_datatype = new caosdb::entity::DataType(enum_value);
+                      out->_deletable = true;
+                      return 0;
+                    } catch (const std::out_of_range &exc) {
+                      caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what());
+                      return caosdb::StatusCode::ENUM_MAPPING_ERROR;
+                    }
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_create_reference_datatype(caosdb_entity_datatype *out,
+                                                              const char *name),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    out->wrapped_datatype = new caosdb::entity::DataType(std::string(name));
+                    out->_deletable = true;
+                    return 0;
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_create_atomic_list_datatype(caosdb_entity_datatype *out,
+                                                                const char *name),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    try {
+                      auto enum_value = ENUM_VALUE_FROM_NAME(std::string(name), AtomicDataType);
+                      out->wrapped_datatype =
+                        new caosdb::entity::DataType(caosdb::entity::DataType::ListOf(enum_value));
+                      out->_deletable = true;
+                      return 0;
+                    } catch (const std::out_of_range &exc) {
+                      caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what());
+                      return caosdb::StatusCode::ENUM_MAPPING_ERROR;
+                    }
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_create_reference_list_datatype(caosdb_entity_datatype *out,
+                                                                   const char *name),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    out->wrapped_datatype = new caosdb::entity::DataType(
+                      caosdb::entity::DataType::ListOf(std::string(name)));
+                    out->_deletable = true;
+                    return 0;
+                  })
+
+ERROR_RETURN_CODE(GENERIC_ERROR, int caosdb_entity_delete_datatype(caosdb_entity_datatype *out), {
+  if (out->_deletable) {
+    delete WRAPPED_DATATYPE_CAST(out);
+  }
+  out->_deletable = false;
+  return 0;
+})
+
 CREATE_VALUE(int_value, const int64_t value)
 CREATE_VALUE(string_value, const char *value)
 CREATE_VALUE(double_value, const double value)
@@ -725,41 +789,26 @@ ERROR_RETURN_CODE(GENERIC_ERROR,
                   })
 // CAOSDB_ENTITY_GET(file_path, GetFilePath()) TODO(henrik)
 CAOSDB_ENTITY_GET(description, GetDescription())
-// TODO(fspreck)
-// ERROR_RETURN_CODE(GENERIC_ERROR,
-//                   int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity, char
-//                   **name,
-//                                                         bool *is_ref, bool *is_list),
-//                   {
-//                     auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity);
-//                     const auto &datatype = wrapped_entity->GetDataType();
-//                     *is_list = datatype.IsList();
-//                     std::string datatype_name;
-//                     if (*is_list) {
-//                       const auto &list_datatype = datatype.AsList();
-//                       *is_ref = list_datatype.IsListOfReference();
-//                       if (*is_ref) {
-//                         datatype_name = list_datatype.GetReferenceDataType().GetName();
-//                       } else {
-//                         datatype_name =
-//                           ENUM_NAME_FROM_VALUE(list_datatype.GetAtomicDataType(),
-//                           AtomicDataType);
-//                       }
-//                     } else {
-//                       *is_ref = datatype.IsReference();
-//                       if (*is_ref) {
-//                         datatype_name = datatype.AsReference().GetName();
-//                       } else {
-//                         datatype_name = ENUM_NAME_FROM_VALUE(datatype.AsAtomic(),
-//                         AtomicDataType);
-//                       }
-//                     }
-//                     char *tmp = (char *)malloc(sizeof(char) * datatype_name.length() + 1);
-//                     strcpy(tmp, datatype_name.c_str());
-//                     delete[] * name;
-//                     *name = tmp;
-//                     return 0;
-//                   })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_entity_get_datatype(caosdb_entity_entity *entity,
+                                                        caosdb_entity_datatype *out),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity);
+                    out->wrapped_datatype = (void *)(&(wrapped_entity->GetDataType()));
+                    out->_deletable = false;
+                    return 0;
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_entity_get_value(caosdb_entity_entity *entity,
+                                                     caosdb_entity_value *out),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity);
+                    out->wrapped_value = (void *)(&(wrapped_entity->GetValue()));
+                    out->_deletable = false;
+                    return 0;
+                  })
 
 CAOSDB_ENTITY_GET(unit, GetUnit())
 CAOSDB_ENTITY_GET(version_id, GetVersionId())
@@ -901,43 +950,26 @@ ERROR_RETURN_CODE(GENERIC_ERROR,
                     *out = tmp;
                     return 0;
                   })
-
-// TODO(fspreck)
-// ERROR_RETURN_CODE(GENERIC_ERROR,
-//                   int caosdb_entity_property_get_datatype(caosdb_entity_property *property,
-//                                                           char **name, bool *is_ref, bool
-//                                                           *is_list),
-//                   {
-//                     auto *wrapped_property = WRAPPED_PROPERTY_CAST(property);
-//                     const auto &datatype = wrapped_property->GetDataType();
-//                     *is_list = datatype.IsList();
-//                     std::string datatype_name;
-//                     if (*is_list) {
-//                       const auto &list_datatype = datatype.AsList();
-//                       *is_ref = list_datatype.IsListOfReference();
-//                       if (*is_ref) {
-//                         datatype_name = list_datatype.GetReferenceDataType().GetName();
-//                       } else {
-//                         datatype_name =
-//                           ENUM_NAME_FROM_VALUE(list_datatype.GetAtomicDataType(),
-//                           AtomicDataType);
-//                       }
-//                     } else {
-//                       *is_ref = datatype.IsReference();
-//                       if (*is_ref) {
-//                         datatype_name = datatype.AsReference().GetName();
-//                       } else {
-//                         datatype_name = ENUM_NAME_FROM_VALUE(datatype.AsAtomic(),
-//                         AtomicDataType);
-//                       }
-//                     }
-//                     char *tmp = (char *)malloc(sizeof(char) * datatype_name.length() + 1);
-//                     strcpy(tmp, datatype_name.c_str());
-//                     delete[] * name;
-//                     *name = tmp;
-//                     return 0;
-//                   })
-
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_property_get_datatype(caosdb_entity_property *property,
+                                                          caosdb_entity_datatype *out),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    auto *wrapped_property = WRAPPED_PROPERTY_CAST(property);
+                    out->wrapped_datatype = (void *)(&(wrapped_property->GetDataType()));
+                    out->_deletable = false;
+                    return 0;
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_property_get_value(caosdb_entity_property *property,
+                                                       caosdb_entity_value *out),
+                  {
+                    RETURN_ASSIGNEMENT_ERROR_IF_DELETABLE(out)
+                    auto *wrapped_property = WRAPPED_PROPERTY_CAST(property);
+                    out->wrapped_value = (void *)(&(wrapped_property->GetValue()));
+                    out->_deletable = false;
+                    return 0;
+                  })
 CAOSDB_PROPERTY_GET(unit, GetUnit())
 
 ERROR_RETURN_CODE(GENERIC_ERROR,
@@ -959,6 +991,73 @@ ERROR_RETURN_CODE(
     return 0;
   })
 
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_datatype_is_atomic(caosdb_entity_datatype *datatype, bool *out),
+                  {
+                    auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype);
+                    *out = wrapped_datatype->IsAtomic();
+                    return 0;
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_datatype_is_reference(caosdb_entity_datatype *datatype,
+                                                          bool *out),
+                  {
+                    auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype);
+                    *out = wrapped_datatype->IsReference();
+                    return 0;
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_datatype_is_list_of_atomic(caosdb_entity_datatype *datatype,
+                                                               bool *out),
+                  {
+                    auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype);
+                    if (wrapped_datatype->IsList()) {
+                      const auto &list_datatype = wrapped_datatype->GetAsList();
+                      *out = list_datatype.IsListOfAtomic();
+                    } else {
+                      *out = false;
+                    }
+                    return 0;
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_datatype_is_list_of_reference(caosdb_entity_datatype *datatype,
+                                                                  bool *out),
+                  {
+                    auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype);
+                    if (wrapped_datatype->IsList()) {
+                      const auto &list_datatype = wrapped_datatype->GetAsList();
+                      *out = list_datatype.IsListOfReference();
+                    } else {
+                      *out = false;
+                    }
+                    return 0;
+                  })
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_entity_datatype_get_datatype_name(caosdb_entity_datatype *datatype, char **out), {
+    auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype);
+    std::string datatype_name;
+    if (wrapped_datatype->IsList()) {
+      const auto &list_datatype = wrapped_datatype->GetAsList();
+      if (list_datatype.IsListOfAtomic()) {
+        datatype_name = ENUM_NAME_FROM_VALUE(list_datatype.GetAtomicDataType(), AtomicDataType);
+      } else {
+        datatype_name = list_datatype.GetReferenceDataType().GetName();
+      }
+    } else {
+      if (wrapped_datatype->IsAtomic()) {
+        datatype_name = ENUM_NAME_FROM_VALUE(wrapped_datatype->GetAsAtomic(), AtomicDataType);
+      } else {
+        datatype_name = wrapped_datatype->GetAsReference().GetName();
+      }
+    }
+    char *tmp = (char *)malloc(sizeof(char) * datatype_name.length() + 1);
+    strcpy(tmp, datatype_name.c_str());
+    delete[] * out;
+    *out = tmp;
+    return 0;
+  })
+
 VALUE_IS(null, IsNull)
 VALUE_IS(string, IsString)
 VALUE_IS(double, IsDouble)
@@ -1012,31 +1111,25 @@ CAOSDB_ENTITY_SET(local_path, local_path,
 CAOSDB_ENTITY_SET(file_path, file_path, wrapped_entity->SetFilePath(std::string(file_path));)
 CAOSDB_ENTITY_SET(description, description,
                   wrapped_entity->SetDescription(std::string(description));)
-// TODO(fspreck)
-// ERROR_RETURN_CODE(GENERIC_ERROR,
-//                   int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity,
-//                                                         const char *datatype, const bool is_ref,
-//                                                         const bool is_list),
-//                   {
-//                     auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity);
-//                     if (is_ref) {
-//                       // Refernce datatype with name of reference
-//                       wrapped_entity->SetDataType(std::string(datatype), is_list);
-//                       return 0;
-//                     } else {
-//                       // Atomic datatype so get from enum
-//                       try {
-//                         auto enum_value =
-//                           ENUM_VALUE_FROM_NAME(std::string(datatype), AtomicDataType);
-//                         wrapped_entity->SetDataType(enum_value, is_list);
-//                         return 0;
-//                       } catch (const std::out_of_range &exc) {
-//                         caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what());
-//                         return caosdb::StatusCode::ENUM_MAPPING_ERROR;
-//                       }
-//                     }
-//                   })
 CAOSDB_ENTITY_SET(unit, unit, wrapped_entity->SetUnit(std::string(unit));)
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_entity_set_datatype(caosdb_entity_entity *entity,
+                                                        caosdb_entity_datatype *datatype),
+                  {
+                    auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity);
+                    auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype);
+
+                    return wrapped_entity->SetDataType(*wrapped_datatype);
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_entity_set_value(caosdb_entity_entity *entity,
+                                                     caosdb_entity_value *value),
+                  {
+                    auto *wrapped_entity = WRAPPED_ENTITY_CAST(entity);
+                    auto *wrapped_value = WRAPPED_VALUE_CAST(value);
+
+                    return wrapped_entity->SetValue(*wrapped_value);
+                  })
 
 ERROR_RETURN_CODE(GENERIC_ERROR,
                   int caosdb_entity_entity_append_parent(caosdb_entity_entity *entity,
@@ -1083,31 +1176,24 @@ CAOSDB_PARENT_SET(name, name, wrapped_parent->SetName(std::string(name));)
 
 CAOSDB_PROPERTY_SET(name, name, wrapped_property->SetName(std::string(name));)
 CAOSDB_PROPERTY_SET(id, id, wrapped_property->SetId(std::string(id));)
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_property_set_datatype(caosdb_entity_property *property,
+                                                          caosdb_entity_datatype *datatype),
+                  {
+                    auto *wrapped_property = WRAPPED_PROPERTY_CAST(property);
+                    auto *wrapped_datatype = WRAPPED_DATATYPE_CAST(datatype);
 
-// TODO(fspreck)
-// ERROR_RETURN_CODE(GENERIC_ERROR,
-//                   int caosdb_entity_property_set_datatype(caosdb_entity_property *property,
-//                                                           const char *datatype, const bool
-//                                                           is_ref, const bool is_list),
-//                   {
-//                     auto *wrapped_property = WRAPPED_PROPERTY_CAST(property);
-//                     if (is_ref) {
-//                       // Refernce datatype with name of reference
-//                       wrapped_property->SetDataType(std::string(datatype), is_list);
-//                       return 0;
-//                     } else {
-//                       // Atomic datatype so get from enum
-//                       try {
-//                         auto enum_value =
-//                           ENUM_VALUE_FROM_NAME(std::string(datatype), AtomicDataType);
-//                         wrapped_property->SetDataType(enum_value, is_list);
-//                         return 0;
-//                       } catch (const std::out_of_range &exc) {
-//                         caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what());
-//                         return caosdb::StatusCode::ENUM_MAPPING_ERROR;
-//                       }
-//                     }
-//                   })
+                    return wrapped_property->SetDataType(*wrapped_datatype);
+                  })
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_entity_property_set_value(caosdb_entity_property *property,
+                                                       caosdb_entity_value *value),
+                  {
+                    auto *wrapped_property = WRAPPED_PROPERTY_CAST(property);
+                    auto *wrapped_value = WRAPPED_VALUE_CAST(value);
+
+                    return wrapped_property->SetValue(*wrapped_value);
+                  })
 
 ERROR_RETURN_CODE(GENERIC_ERROR,
                   int caosdb_entity_property_set_importance(caosdb_entity_property *property,
diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp
index 4322c135db0024ca90aca52ed783523ba88dc104..361b0918060e46cf71b435e519b3a7ec20b1ee46 100644
--- a/test/test_ccaosdb.cpp
+++ b/test/test_ccaosdb.cpp
@@ -127,6 +127,108 @@ TEST_F(test_ccaosdb, test_query) {
   EXPECT_EQ(return_code, 0);
 }
 
+TEST_F(test_ccaosdb, test_datatype) {
+
+  caosdb_entity_datatype atomic;
+  // check that this fails
+  int return_code(caosdb_entity_create_atomic_datatype(&atomic, "some type"));
+  EXPECT_EQ(return_code, caosdb::StatusCode::ENUM_MAPPING_ERROR);
+
+  return_code = caosdb_entity_create_atomic_datatype(&atomic, "INTEGER");
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype reference;
+  return_code = caosdb_entity_create_reference_datatype(&reference, "MyType");
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype list_of_atomics;
+  return_code = caosdb_entity_create_atomic_list_datatype(&list_of_atomics, "DATETIME");
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype list_of_references;
+  return_code = caosdb_entity_create_reference_list_datatype(&list_of_references, "MyType");
+  EXPECT_EQ(return_code, 0);
+
+  bool is_a(false);
+  return_code = caosdb_entity_datatype_is_atomic(&atomic, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_TRUE(is_a);
+  return_code = caosdb_entity_datatype_is_reference(&atomic, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_atomic(&atomic, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_reference(&atomic, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+
+  return_code = caosdb_entity_datatype_is_atomic(&reference, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_reference(&reference, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_TRUE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_atomic(&reference, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_reference(&reference, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+
+  return_code = caosdb_entity_datatype_is_atomic(&list_of_atomics, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_reference(&list_of_atomics, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_atomic(&list_of_atomics, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_TRUE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_reference(&list_of_atomics, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+
+  return_code = caosdb_entity_datatype_is_atomic(&list_of_references, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_reference(&list_of_references, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_atomic(&list_of_references, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_reference(&list_of_references, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_TRUE(is_a);
+
+  char *name = nullptr; // NOLINT
+  return_code = caosdb_entity_datatype_get_datatype_name(&atomic, &name);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_STREQ(name, "INTEGER");
+
+  return_code = caosdb_entity_datatype_get_datatype_name(&reference, &name);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_STREQ(name, "MyType");
+
+  return_code = caosdb_entity_datatype_get_datatype_name(&list_of_atomics, &name);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_STREQ(name, "DATETIME");
+
+  return_code = caosdb_entity_datatype_get_datatype_name(&list_of_references, &name);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_STREQ(name, "MyType");
+
+  return_code = caosdb_entity_delete_datatype(&atomic);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&reference);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&list_of_atomics);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&list_of_references);
+  EXPECT_EQ(return_code, 0);
+}
+
 TEST_F(test_ccaosdb, test_value) {
 
   caosdb_entity_value string_value;
@@ -207,7 +309,6 @@ TEST_F(test_ccaosdb, test_value) {
   caosdb_entity_value_is_vector(&bool_vector_value, &is_a);
   EXPECT_TRUE(is_a);
 
-  // TODO(fspreck) Test as... functions
   char *out_string = nullptr; // NOLINT
   return_code = caosdb_entity_value_get_as_string(&string_value, &out_string);
   EXPECT_EQ(return_code, 0);
@@ -322,35 +423,95 @@ TEST_F(test_ccaosdb, test_entity) {
   caosdb_entity_entity_get_description(&entity, &out);
   EXPECT_EQ(strcmp(out, "The length of an object"), 0);
 
-  // TODO(fspreck)
-  // caosdb_entity_entity_set_datatype(&entity, "DOUBLE", false, false);
-  // bool is_list[] = {false}; // NOLINT
-  // bool is_ref[] = {false};  // NOLINT
-  // caosdb_entity_entity_get_datatype(&entity, &out, is_ref, is_list);
-  // EXPECT_EQ(strcmp(out, "DOUBLE"), 0);
-  // EXPECT_FALSE(*is_list);
-  // EXPECT_FALSE(*is_ref);
-
-  // caosdb_entity_entity_set_datatype(&entity, "Person", true, true);
-  // caosdb_entity_entity_get_datatype(&entity, &out, is_ref, is_list);
-  // EXPECT_EQ(strcmp(out, "Person"), 0);
-  // EXPECT_TRUE(*is_list);
-  // EXPECT_TRUE(*is_ref);
+  caosdb_entity_datatype in_type;
+  caosdb_entity_create_atomic_datatype(&in_type, "DOUBLE");
+  caosdb_entity_entity_set_datatype(&entity, &in_type);
+
+  // verify that this doesn't work ...
+  return_code = caosdb_entity_entity_get_datatype(&entity, &in_type);
+  EXPECT_EQ(return_code, caosdb::StatusCode::EXTERN_C_ASSIGNMENT_ERROR);
+  caosdb_entity_datatype out_type;
+  // ... but does with a clean property
+  return_code = caosdb_entity_entity_get_datatype(&entity, &out_type);
+  EXPECT_EQ(return_code, 0);
+  bool is_a(false);
+  return_code = caosdb_entity_datatype_is_atomic(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_TRUE(is_a);
+  return_code = caosdb_entity_datatype_is_reference(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+
+  caosdb_entity_datatype_get_datatype_name(&out_type, &out);
+  EXPECT_STREQ(out, "DOUBLE");
+
+  caosdb_entity_value in_value;
+  return_code = caosdb_entity_create_double_value(&in_value, 5.0);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_entity_set_value(&entity, &in_value);
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_value out_value;
+  return_code = caosdb_entity_entity_get_value(&entity, &out_value);
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_value_is_double(&out_value, &is_a);
+  EXPECT_TRUE(is_a);
+  caosdb_entity_value_is_null(&out_value, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_value_is_string(&out_value, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_value_is_bool(&out_value, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_value_is_integer(&out_value, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_value_is_vector(&out_value, &is_a);
+  EXPECT_FALSE(is_a);
+
+  double out_double(0);
+  caosdb_entity_value_get_as_double(&out_value, &out_double);
+  EXPECT_EQ(out_double, 5.0);
+
+  // clear to re-use
+  return_code = caosdb_entity_delete_datatype(&in_type);
+  EXPECT_EQ(return_code, 0);
+  caosdb_entity_create_reference_list_datatype(&in_type, "Person");
+  caosdb_entity_entity_set_datatype(&entity, &in_type);
+
+  // works without clearing since datatype is managed by the owning entity
+  caosdb_entity_entity_get_datatype(&entity, &out_type);
+  return_code = caosdb_entity_datatype_is_atomic(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_reference(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_FALSE(is_a);
+  return_code = caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a);
+  EXPECT_EQ(return_code, 0);
+  EXPECT_TRUE(is_a);
+
+  caosdb_entity_datatype_get_datatype_name(&out_type, &out);
+  EXPECT_STREQ(out, "Person");
 
   caosdb_entity_entity_set_unit(&entity, "m");
   caosdb_entity_entity_get_unit(&entity, &out);
   EXPECT_EQ(strcmp(out, "m"), 0);
 
-  // TODO(fspreck)
-  // return_code = caosdb_entity_entity_set_double_value(&entity, 5.0);
-  // EXPECT_EQ(return_code, 0);
-  // double value[] = {0.0}; // NOLINT
-  // return_code = caosdb_entity_entity_get_double_value(&entity, value);
-  // EXPECT_EQ(return_code, 0);
-  // EXPECT_EQ(*value, 5.0);
-
   return_code = caosdb_entity_delete_entity(&entity);
   EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&in_type);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_value(&in_value);
+  EXPECT_EQ(return_code, 0);
 }
 
 TEST_F(test_ccaosdb, test_parent) {
@@ -382,12 +543,14 @@ TEST_F(test_ccaosdb, test_property) {
   caosdb_entity_property_set_id(&property, "some_id");
   caosdb_entity_property_set_name(&property, "some_name");
 
-  // TODO(fspreck)
-  // caosdb_entity_property_set_datatype(&property, "TEXT", false, false);
+  caosdb_entity_datatype in_type;
+  caosdb_entity_create_atomic_datatype(&in_type, "TEXT");
+  caosdb_entity_property_set_datatype(&property, &in_type);
   caosdb_entity_property_set_importance(&property, "FIX");
   caosdb_entity_property_set_unit(&property, "some_unit");
-  // TODO(fspreck)
-  // caosdb_entity_property_set_string_value(&property, "some_value");
+  caosdb_entity_value in_value;
+  caosdb_entity_create_string_value(&in_value, "some_value");
+  caosdb_entity_property_set_value(&property, &in_value);
 
   char *out = nullptr; // NOLINT
   caosdb_entity_property_get_id(&property, &out);
@@ -396,13 +559,22 @@ TEST_F(test_ccaosdb, test_property) {
   caosdb_entity_property_get_name(&property, &out);
   EXPECT_EQ(strcmp(out, "some_name"), 0);
 
-  // TODO(fspreck)
-  // bool is_ref[] = {false};  // NOLINT
-  // bool is_list[] = {false}; // NOLINT
-  // caosdb_entity_property_get_datatype(&property, &out, is_ref, is_list);
-  // EXPECT_EQ(strcmp(out, "TEXT"), 0);
-  // EXPECT_FALSE(*is_ref);
-  // EXPECT_FALSE(*is_list);
+  caosdb_entity_datatype out_type;
+  return_code = caosdb_entity_property_get_datatype(&property, &out_type);
+  EXPECT_EQ(return_code, 0);
+  bool is_a(false);
+
+  caosdb_entity_datatype_is_atomic(&out_type, &is_a);
+  EXPECT_TRUE(is_a);
+  caosdb_entity_datatype_is_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+
+  caosdb_entity_datatype_get_datatype_name(&out_type, &out);
+  EXPECT_STREQ(out, "TEXT");
 
   caosdb_entity_property_get_importance(&property, &out);
   EXPECT_EQ(strcmp(out, "FIX"), 0);
@@ -410,127 +582,202 @@ TEST_F(test_ccaosdb, test_property) {
   caosdb_entity_property_get_unit(&property, &out);
   EXPECT_EQ(strcmp(out, "some_unit"), 0);
 
-  // TODO(fspreck)
-  // caosdb_entity_property_get_string_value(&property, &out);
-  // EXPECT_EQ(strcmp(out, "some_value"), 0);
+  caosdb_entity_value out_value;
+  return_code = caosdb_entity_property_get_value(&property, &out_value);
+  EXPECT_EQ(return_code, 0);
+  caosdb_entity_value_is_string(&out_value, &is_a);
+  EXPECT_TRUE(is_a);
+  caosdb_entity_value_get_as_string(&out_value, &out);
+  EXPECT_STREQ(out, "some_value");
 
   return_code = caosdb_entity_delete_property(&property);
   EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&in_type);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_value(&in_value);
+  EXPECT_EQ(return_code, 0);
 }
 
-// TODO(fspreck)
-// TEST_F(test_ccaosdb, test_string_list_property) {
-
-//   caosdb_entity_property property;
-//   int return_code(caosdb_entity_create_property(&property));
-//   EXPECT_EQ(return_code, 0);
-
-//   return_code = caosdb_entity_property_set_datatype(&property, "TEXT", false, true);
-//   EXPECT_EQ(return_code, 0);
-
-//   const char *value_list[] = {"val0", "val1", "val2"}; // NOLINT
-//   return_code = caosdb_entity_property_set_string_list_value(&property, value_list, 3);
-//   EXPECT_EQ(return_code, 0);
-
-//   char *out = nullptr;      // NOLINT
-//   bool is_ref[] = {false};  // NOLINT
-//   bool is_list[] = {false}; // NOLINT
-//   return_code = caosdb_entity_property_get_datatype(&property, &out, is_ref, is_list);
-//   EXPECT_EQ(return_code, 0);
-//   EXPECT_EQ(strcmp(out, "TEXT"), 0);
-//   EXPECT_FALSE(*is_ref);
-//   EXPECT_TRUE(*is_list);
-
-//   int length = -1; // NOLINT
-//   return_code = caosdb_entity_property_get_value_list_length(&property, &length);
-//   EXPECT_EQ(return_code, 0);
-//   EXPECT_EQ(length, 3);
-
-//   for (int i = 0; i < length; i++) {
-//     return_code = caosdb_entity_property_get_string_list_value_at(&property, &out, i);
-//     EXPECT_EQ(return_code, 0);
-//     EXPECT_EQ(strcmp(value_list[i], out), 0); // NOLINT
-//   }
-
-//   return_code = caosdb_entity_delete_property(&property);
-//   EXPECT_EQ(return_code, 0);
-// }
-
-// TEST_F(test_ccaosdb, test_int_list_property) {
-
-//   caosdb_entity_property property;
-//   int return_code(caosdb_entity_create_property(&property));
-//   EXPECT_EQ(return_code, 0);
-
-//   return_code = caosdb_entity_property_set_datatype(&property, "INTEGER", false, true);
-//   EXPECT_EQ(return_code, 0);
-
-//   const int64_t value_list[] = {1, 2, 3}; // NOLINT
-//   return_code = caosdb_entity_property_set_int_list_value(&property, &(value_list)[0], 3);
-//   EXPECT_EQ(return_code, 0);
-
-//   char *dt_out = nullptr;   // NOLINT
-//   bool is_ref[] = {false};  // NOLINT
-//   bool is_list[] = {false}; // NOLINT
-//   return_code = caosdb_entity_property_get_datatype(&property, &dt_out, is_ref, is_list);
-//   EXPECT_EQ(return_code, 0);
-//   EXPECT_STREQ(dt_out, "INTEGER");
-//   EXPECT_FALSE(*is_ref);
-//   EXPECT_TRUE(*is_list);
-
-//   int length = -1; // NOLINT
-//   return_code = caosdb_entity_property_get_value_list_length(&property, &length);
-//   EXPECT_EQ(return_code, 0);
-//   EXPECT_EQ(length, 3);
-
-//   for (int i = 0; i < length; i++) {
-//     int64_t out = -1;
-//     return_code = caosdb_entity_property_get_int_list_value_at(&property, &out, i);
-//     EXPECT_EQ(return_code, 0);
-//     EXPECT_EQ(value_list[i], out); // NOLINT
-//   }
-
-//   return_code = caosdb_entity_delete_property(&property);
-//   EXPECT_EQ(return_code, 0);
-// }
-
-// TEST_F(test_ccaosdb, test_bool_list_property) {
-
-//   caosdb_entity_property property;
-//   int return_code(caosdb_entity_create_property(&property));
-//   EXPECT_EQ(return_code, 0);
-
-//   return_code = caosdb_entity_property_set_datatype(&property, "BOOLEAN", false, true);
-//   EXPECT_EQ(return_code, 0);
-
-//   const bool value_list[] = {true, true, false}; // NOLINT
-//   return_code = caosdb_entity_property_set_boolean_list_value(&property, &(value_list)[0], 3);
-//   EXPECT_EQ(return_code, 0);
-
-//   char *dt_out = nullptr;   // NOLINT
-//   bool is_ref[] = {false};  // NOLINT
-//   bool is_list[] = {false}; // NOLINT
-//   return_code = caosdb_entity_property_get_datatype(&property, &dt_out, is_ref, is_list);
-//   EXPECT_EQ(return_code, 0);
-//   EXPECT_STREQ(dt_out, "BOOLEAN");
-//   EXPECT_FALSE(*is_ref);
-//   EXPECT_TRUE(*is_list);
-
-//   int length = -1; // NOLINT
-//   return_code = caosdb_entity_property_get_value_list_length(&property, &length);
-//   EXPECT_EQ(return_code, 0);
-//   EXPECT_EQ(length, 3);
-
-//   for (int i = 0; i < length; i++) {
-//     bool out = true;
-//     return_code = caosdb_entity_property_get_boolean_list_value_at(&property, &out, i);
-//     EXPECT_EQ(return_code, 0);
-//     EXPECT_EQ(value_list[i], out); // NOLINT
-//   }
-
-//   return_code = caosdb_entity_delete_property(&property);
-//   EXPECT_EQ(return_code, 0);
-// }
+TEST_F(test_ccaosdb, test_string_list_property) {
+
+  caosdb_entity_property property;
+  int return_code(caosdb_entity_create_property(&property));
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype in_type;
+  return_code = caosdb_entity_create_atomic_list_datatype(&in_type, "TEXT");
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_property_set_datatype(&property, &in_type);
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_value in_value;
+  const char *value_list[] = {"val0", "val1", "val2"}; // NOLINT
+  return_code = caosdb_entity_create_string_vector_value(&in_value, value_list, 3);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_property_set_value(&property, &in_value);
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype out_type;
+  return_code = caosdb_entity_property_get_datatype(&property, &out_type);
+  EXPECT_EQ(return_code, 0);
+
+  bool is_a(false);
+  caosdb_entity_datatype_is_atomic(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a);
+  EXPECT_TRUE(is_a);
+  caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  char *out = nullptr; // NOLINT
+  caosdb_entity_datatype_get_datatype_name(&out_type, &out);
+  EXPECT_STREQ(out, "TEXT");
+
+  caosdb_entity_value out_value;
+  caosdb_entity_property_get_value(&property, &out_value);
+  caosdb_entity_value_is_vector(&out_value, &is_a);
+  EXPECT_TRUE(is_a);
+  int length(-1);
+  caosdb_entity_value_get_as_vector_size(&out_value, &length);
+  EXPECT_EQ(length, 3);
+
+  caosdb_entity_value list_elt;
+  for (int i = 0; i < length; i++) {
+    return_code = caosdb_entity_value_get_as_vector_at(&out_value, &list_elt, i);
+    EXPECT_EQ(return_code, 0);
+    return_code = caosdb_entity_value_get_as_string(&list_elt, &out);
+    EXPECT_EQ(return_code, 0);
+    EXPECT_STREQ(value_list[i], out); // NOLINT
+  }
+
+  return_code = caosdb_entity_delete_property(&property);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&in_type);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_value(&in_value);
+  EXPECT_EQ(return_code, 0);
+}
+
+TEST_F(test_ccaosdb, test_int_list_property) {
+
+  caosdb_entity_property property;
+  int return_code(caosdb_entity_create_property(&property));
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype in_type;
+  caosdb_entity_create_atomic_list_datatype(&in_type, "INTEGER");
+  return_code = caosdb_entity_property_set_datatype(&property, &in_type);
+  EXPECT_EQ(return_code, 0);
+
+  const int64_t value_list[] = {1, 2, 3}; // NOLINT
+  caosdb_entity_value in_value;
+  caosdb_entity_create_int_vector_value(&in_value, value_list, 3);
+  return_code = caosdb_entity_property_set_value(&property, &in_value);
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype out_type;
+  return_code = caosdb_entity_property_get_datatype(&property, &out_type);
+  EXPECT_EQ(return_code, 0);
+
+  bool is_a(false);
+  caosdb_entity_datatype_is_atomic(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a);
+  EXPECT_TRUE(is_a);
+  caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  char *out = nullptr; // NOLINT
+  caosdb_entity_datatype_get_datatype_name(&out_type, &out);
+  EXPECT_STREQ(out, "INTEGER");
+
+  caosdb_entity_value out_value;
+  caosdb_entity_property_get_value(&property, &out_value);
+  caosdb_entity_value_is_vector(&out_value, &is_a);
+  EXPECT_TRUE(is_a);
+  int length(-1);
+  caosdb_entity_value_get_as_vector_size(&out_value, &length);
+  EXPECT_EQ(length, 3);
+
+  int64_t out_int = -1;
+  caosdb_entity_value list_elt;
+  for (int i = 0; i < length; i++) {
+    return_code = caosdb_entity_value_get_as_vector_at(&out_value, &list_elt, i);
+    EXPECT_EQ(return_code, 0);
+    return_code = caosdb_entity_value_get_as_integer(&list_elt, &out_int);
+    EXPECT_EQ(return_code, 0);
+    EXPECT_EQ(value_list[i], out_int); // NOLINT
+  }
+
+  return_code = caosdb_entity_delete_property(&property);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&in_type);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_value(&in_value);
+  EXPECT_EQ(return_code, 0);
+}
+
+TEST_F(test_ccaosdb, test_bool_list_property) {
+
+  caosdb_entity_property property;
+  int return_code(caosdb_entity_create_property(&property));
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype in_type;
+  caosdb_entity_create_atomic_list_datatype(&in_type, "BOOLEAN");
+  return_code = caosdb_entity_property_set_datatype(&property, &in_type);
+  EXPECT_EQ(return_code, 0);
+
+  const bool value_list[] = {true, true, false}; // NOLINT
+  caosdb_entity_value in_value;
+  caosdb_entity_create_bool_vector_value(&in_value, value_list, 3);
+  return_code = caosdb_entity_property_set_value(&property, &in_value);
+  EXPECT_EQ(return_code, 0);
+
+  caosdb_entity_datatype out_type;
+  return_code = caosdb_entity_property_get_datatype(&property, &out_type);
+  EXPECT_EQ(return_code, 0);
+
+  bool is_a(false);
+  caosdb_entity_datatype_is_atomic(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  caosdb_entity_datatype_is_list_of_atomic(&out_type, &is_a);
+  EXPECT_TRUE(is_a);
+  caosdb_entity_datatype_is_list_of_reference(&out_type, &is_a);
+  EXPECT_FALSE(is_a);
+  char *out = nullptr; // NOLINT
+  caosdb_entity_datatype_get_datatype_name(&out_type, &out);
+  EXPECT_STREQ(out, "BOOLEAN");
+
+  caosdb_entity_value out_value;
+  caosdb_entity_property_get_value(&property, &out_value);
+  caosdb_entity_value_is_vector(&out_value, &is_a);
+  EXPECT_TRUE(is_a);
+  int length(-1);
+  caosdb_entity_value_get_as_vector_size(&out_value, &length);
+  EXPECT_EQ(length, 3);
+
+  bool out_bool(false);
+  caosdb_entity_value list_elt;
+  for (int i = 0; i < length; i++) {
+    return_code = caosdb_entity_value_get_as_vector_at(&out_value, &list_elt, i);
+    EXPECT_EQ(return_code, 0);
+    return_code = caosdb_entity_value_get_as_bool(&list_elt, &out_bool);
+    EXPECT_EQ(return_code, 0);
+    EXPECT_EQ(value_list[i], out_bool); // NOLINT
+  }
+
+  return_code = caosdb_entity_delete_property(&property);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&in_type);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_value(&in_value);
+  EXPECT_EQ(return_code, 0);
+}
 
 TEST_F(test_ccaosdb, test_entity_with_parent_and_property) {
   caosdb_entity_parent input_parent;
@@ -547,9 +794,12 @@ TEST_F(test_ccaosdb, test_entity_with_parent_and_property) {
   caosdb_entity_property_set_id(&input_property, "property_id");
   caosdb_entity_property_set_name(&input_property, "property_name");
 
-  // TODO(fspreck)
-  // caosdb_entity_property_set_datatype(&input_property, "TEXT", false, false);
-  // caosdb_entity_property_set_string_value(&input_property, "property_value");
+  caosdb_entity_datatype in_type;
+  caosdb_entity_create_atomic_datatype(&in_type, "TEXT");
+  caosdb_entity_value in_value;
+  caosdb_entity_create_string_value(&in_value, "property_value");
+  caosdb_entity_property_set_datatype(&input_property, &in_type);
+  caosdb_entity_property_set_value(&input_property, &in_value);
 
   caosdb_entity_entity entity;
   return_code = caosdb_entity_create_entity(&entity);
@@ -588,21 +838,37 @@ TEST_F(test_ccaosdb, test_entity_with_parent_and_property) {
   caosdb_entity_property_get_name(&output_property, &out);
   EXPECT_EQ(strcmp(in, out), 0);
 
-  // TODO(fspreck)
-  // bool is_list[] = {false}; // NOLINT
-  // bool is_ref[] = {false};  // NOLINT
-  // caosdb_entity_property_get_datatype(&input_property, &in, is_ref, is_list);
-  // EXPECT_FALSE(*is_list);
-  // EXPECT_FALSE(*is_ref);
-  // caosdb_entity_property_get_datatype(&output_property, &out, is_ref, is_list);
-  // EXPECT_FALSE(*is_list);
-  // EXPECT_FALSE(*is_ref);
-  // EXPECT_EQ(strcmp(in, out), 0);
-
-  // TODO(fspreck)
-  // caosdb_entity_property_get_string_value(&input_property, &in);
-  // caosdb_entity_property_get_string_value(&output_property, &out);
-  // EXPECT_EQ(strcmp(in, out), 0);
+  caosdb_entity_datatype out_type;
+  caosdb_entity_property_get_datatype(&output_property, &out_type);
+
+  bool in_is(false);
+  bool out_is(false);
+  caosdb_entity_datatype_is_atomic(&in_type, &in_is);
+  caosdb_entity_datatype_is_atomic(&out_type, &out_is);
+  EXPECT_EQ(in_is, out_is);
+  caosdb_entity_datatype_is_reference(&in_type, &in_is);
+  caosdb_entity_datatype_is_reference(&out_type, &out_is);
+  EXPECT_EQ(in_is, out_is);
+  caosdb_entity_datatype_is_list_of_atomic(&in_type, &in_is);
+  caosdb_entity_datatype_is_list_of_atomic(&out_type, &out_is);
+  EXPECT_EQ(in_is, out_is);
+  caosdb_entity_datatype_is_list_of_reference(&in_type, &in_is);
+  caosdb_entity_datatype_is_list_of_reference(&out_type, &out_is);
+  EXPECT_EQ(in_is, out_is);
+
+  caosdb_entity_datatype_get_datatype_name(&in_type, &in);
+  caosdb_entity_datatype_get_datatype_name(&out_type, &out);
+  EXPECT_STREQ(in, out);
+
+  caosdb_entity_value out_value;
+  caosdb_entity_property_get_value(&output_property, &out_value);
+  caosdb_entity_value_is_string(&in_value, &in_is);
+  EXPECT_TRUE(in_is);
+  caosdb_entity_value_is_string(&out_value, &out_is);
+  EXPECT_TRUE(out_is);
+  caosdb_entity_value_get_as_string(&in_value, &in);
+  caosdb_entity_value_get_as_string(&out_value, &out);
+  EXPECT_STREQ(in, out);
 
   caosdb_entity_parent output_parent;
   return_code = caosdb_entity_entity_get_parent(&entity, &output_parent, 0);
@@ -623,6 +889,10 @@ TEST_F(test_ccaosdb, test_entity_with_parent_and_property) {
   EXPECT_EQ(return_code, 0);
   return_code = caosdb_entity_delete_entity(&entity);
   EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_datatype(&in_type);
+  EXPECT_EQ(return_code, 0);
+  return_code = caosdb_entity_delete_value(&in_value);
+  EXPECT_EQ(return_code, 0);
 
   // This tests the `_deletable` flag. The wrapped cpp objects of
   // `output_parent` and `output_property` are owned by the entity, so
diff --git a/test/test_value.cpp b/test/test_value.cpp
index 15d70a391265530798b756f2bd0ba4b50a932031..25f8a12909e52fee4e3ed3a6b688b1871a41a32f 100644
--- a/test/test_value.cpp
+++ b/test/test_value.cpp
@@ -147,4 +147,31 @@ TEST(test_value, test_scalar_value_to_value) {
   EXPECT_EQ(scalar_value.GetAsInt64(), value.GetAsInt64());
 }
 
+TEST(test_value, test_abstract_value) {
+  std::vector<double> vals;
+  for (double num : {0.0, 5.6, 27.5}) {
+    vals.push_back(num);
+  }
+  Value value(vals);
+  EXPECT_TRUE(value.IsVector());
+
+  AbstractValue *abstract_value = &value;
+  EXPECT_TRUE(abstract_value->IsVector());
+
+  Value value2(*abstract_value);
+  EXPECT_TRUE(value2.IsVector());
+
+  ScalarValue scalar_value = value.GetAsVector().at(2);
+  EXPECT_TRUE(scalar_value.IsDouble());
+  EXPECT_EQ(scalar_value.GetAsDouble(), 27.5);
+
+  AbstractValue *abstract_scalar_value = &scalar_value;
+  EXPECT_TRUE(abstract_scalar_value->IsDouble());
+  EXPECT_EQ(abstract_scalar_value->GetAsDouble(), 27.5);
+
+  Value scalar_value2(*abstract_scalar_value);
+  EXPECT_TRUE(scalar_value2.IsDouble());
+  EXPECT_EQ(scalar_value2.GetAsDouble(), 27.5);
+}
+
 } // namespace caosdb::entity