diff --git a/conanfile.py b/conanfile.py
index 0e922bc74936d4341c8a574220c6149459ac6b7e..e2133cea76ce826d168364259d6a519ba28a75d3 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -3,7 +3,7 @@ from conans import ConanFile, CMake, tools
 
 class CaosdbConan(ConanFile):
     name = "caosdb"
-    version = "0.0.8"
+    version = "0.0.9"
     license = "AGPL-3.0-or-later"
     author = "Timm C. Fitschen <t.fitschen@indiscale.com>"
     url = "https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git"
diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h
index 6d542d4f885f38eea214763754e3bbfebe624286..d763bfe1683072c2571e7d9b728343d9ab8a913e 100644
--- a/include/caosdb/entity.h
+++ b/include/caosdb/entity.h
@@ -278,6 +278,9 @@ private:
 class Entity {
 public:
   Entity();
+  inline Entity(const Entity &original) : Entity(CreateProtoEntity()) {
+    this->wrapped->CopyFrom(*original.wrapped);
+  };
   explicit Entity(IdResponse *idResponse);
   explicit inline Entity(ProtoEntity *wrapped) : wrapped(wrapped) {
     errors.wrapped = this->wrapped->mutable_errors();
@@ -287,9 +290,12 @@ public:
     parents.wrapped = this->wrapped->mutable_parents();
   };
 
-  [[nodiscard]] inline auto GetId() const -> const std::string & {
+  [[nodiscard]] inline auto GetId() const noexcept -> const std::string & {
     return wrapped->id();
   };
+  [[nodiscard]] inline auto HasId() const noexcept -> bool {
+    return !wrapped->id().empty();
+  }
   [[nodiscard]] inline auto GetVersionId() const -> const std::string & {
     return wrapped->version().id();
   };
@@ -333,9 +339,7 @@ public:
   }
 
   auto SetRole(const std::string &role) -> void;
-  auto SetId(const std::string &id) -> void;
   auto SetName(const std::string &name) -> void;
-  auto SetVersionId(const std::string &id) -> void;
 
   // TODO(fspreck) ... and also these
   auto SetValue(const std::string &value) -> void;
@@ -348,8 +352,12 @@ public:
   auto AppendParent(const Parent &parent) -> void;
   auto Switch(ProtoEntity *entity) -> void;
 
-private:
+protected:
   static auto CreateProtoEntity() -> ProtoEntity *;
+  auto SetId(const std::string &id) -> void;
+  auto SetVersionId(const std::string &id) -> void;
+
+private:
   ProtoEntity *wrapped;
   Properties properties;
   Parents parents;
diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h
index 7d2f9f9572d4f6f2d44ef7ac167f760d9ce62534..7a4cd4185d45b5a1f93f271973018ff2c2c42126 100644
--- a/include/caosdb/status_code.h
+++ b/include/caosdb/status_code.h
@@ -35,6 +35,8 @@
 namespace caosdb {
 
 enum StatusCode {
+  READY = -4,
+  GO_ON = -3,
   INITIAL = -2,
   EXECUTING = -1,
   SUCCESS = 0,
@@ -48,6 +50,7 @@ enum StatusCode {
   TRANSACTION_STATUS_ERROR = 25,
   TRANSACTION_TYPE_ERROR = 26,
   UNSUPPORTED_FEATURE = 27,
+  ORIGINAL_ENTITY_MISSING_ID = 28,
 };
 
 auto get_status_description(int code) -> const std::string &;
diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h
index 80d77d13c610e999a61b3c452fefbb3222a2ff06..98148d6171306b2d35414934368568faf5bc2962 100644
--- a/include/caosdb/transaction.h
+++ b/include/caosdb/transaction.h
@@ -106,6 +106,35 @@
       "wrong TransactionType.")                                                \
   }
 
+/*
+ * Do all necessary checks and assure that another update can be added as a
+ * sub-request to a transaction.
+ */
+#define ASSERT_CAN_ADD_UPDATE                                                  \
+  if (!IsStatus(TransactionStatus::INITIAL())) {                               \
+    return StatusCode::TRANSACTION_STATUS_ERROR;                               \
+  }                                                                            \
+  switch (this->transaction_type) {                                            \
+  case NONE:                                                                   \
+    this->transaction_type = TransactionType::INSERT;                          \
+  case INSERT:                                                                 \
+  case MIXED_WRITE:                                                            \
+  case MIXED_READ_AND_WRITE:                                                   \
+    break;                                                                     \
+  default:                                                                     \
+    CAOSDB_LOG_ERROR_AND_RETURN_STATUS(                                        \
+      logger_name, StatusCode::TRANSACTION_TYPE_ERROR,                         \
+      "You cannot add an update to this transaction because it has the "       \
+      "wrong TransactionType.")                                                \
+  }                                                                            \
+  if (!entity->HasId()) {                                                      \
+    CAOSDB_LOG_ERROR_AND_RETURN_STATUS(                                        \
+      logger_name, StatusCode::ORIGINAL_ENTITY_MISSING_ID,                     \
+      "You cannot update this entity without any id. Probably you did not "    \
+      "retrieve it first? Entity updates should always start with the "        \
+      "retrieval of the existing entity which may then be changed.")           \
+  }
+
 namespace caosdb::transaction {
 using caosdb::entity::Entity;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
@@ -233,6 +262,16 @@ public:
    */
   auto InsertEntity(Entity *entity) noexcept -> StatusCode;
 
+  /**
+   * Add the entity to this transaction for an update.
+   *
+   * The update is being processed when the Execute() or
+   * ExecuteAsynchronously() methods of this transaction are called.
+   *
+   * Changing the entity afterwards results in undefined behavior.
+   */
+  auto UpdateEntity(Entity *entity) noexcept -> StatusCode;
+
   /**
    * Add an entity id to this transaction for deletion.
    *
diff --git a/src/caosdb/configuration.cpp b/src/caosdb/configuration.cpp
index c24df459f5494232d2fadd2719a0be2d237b8f24..da598676d3256bd4a2a66f6dcbe2ff51c36a136d 100644
--- a/src/caosdb/configuration.cpp
+++ b/src/caosdb/configuration.cpp
@@ -37,6 +37,7 @@
 #include "caosdb/utility.h"                            // for get_home_direc...
 #include <cassert>                                     // for assert
 #include <cstdlib>                                     // for getenv
+#include <cstring>                                     // IWYU pragma: keep
 #include <exception>                                   // IWYU pragma: keep
 #include <grpcpp/security/credentials.h>               // for SslCredentials
 #include <iterator>                                    // for next
diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp
index 72b59e81cf51f86d2f7cb8fe3f482753af590da5..a74d2e61b24ad096b703f53ab6550a63dec2a362 100644
--- a/src/caosdb/transaction.cpp
+++ b/src/caosdb/transaction.cpp
@@ -42,8 +42,14 @@ namespace caosdb {
 auto get_status_description(int code) -> const std::string & {
   static const std::string MISSING_DESCRIPTION = "MISSING DESCRIPTION";
   static const std::map<int, std::string> descriptions = {
-    {StatusCode::INITIAL, "The transaction has not been executed yet and "
-                          "clients can stil change it."},
+    {StatusCode::INITIAL,
+     "The transaction has just been intialized. It has not been executed yet "
+     "and clients can still change it and add sub-transactions."},
+    {StatusCode::GO_ON,
+     "The transaction has a transaction_type yet and clients may add matching "
+     "sub-transaction or execute it right-away."},
+    {StatusCode::READY,
+     "The transaction is ready for execution and cannot be changed anymore."},
     {StatusCode::EXECUTING, "The transaction is currently being executed."},
     {StatusCode::SUCCESS, "The action was successful"},
     {StatusCode::CONNECTION_ERROR,
@@ -68,6 +74,10 @@ auto get_status_description(int code) -> const std::string & {
     {StatusCode::TRANSACTION_TYPE_ERROR,
      "The Transaction has a transaction type which does not allow the "
      "attempted action."},
+    {StatusCode::ORIGINAL_ENTITY_MISSING_ID,
+     "The attempt to update this entity failed because this entity does have "
+     "an id. This is the case when you did not retrieve it before applying any "
+     "changes and instantiated the Entity class explicitely."},
     {StatusCode::UNSUPPORTED_FEATURE,
      "This feature is not available in the this client implementation."}};
   try {
@@ -142,6 +152,17 @@ auto Transaction::InsertEntity(Entity *entity) noexcept -> StatusCode {
   return StatusCode::INITIAL;
 }
 
+auto Transaction::UpdateEntity(Entity *entity) noexcept -> StatusCode {
+  ASSERT_CAN_ADD_UPDATE
+
+  auto *sub_request = this->request->add_requests();
+  auto *proto_entity = sub_request->mutable_update_request();
+
+  entity->Switch(proto_entity);
+
+  return StatusCode::INITIAL;
+}
+
 auto Transaction::Execute() -> TransactionStatus {
   ExecuteAsynchronously();
   this->status.ThrowExceptionIfError();
@@ -221,6 +242,14 @@ auto Transaction::WaitForIt() const noexcept -> TransactionStatus {
       }
       this->result_set = std::make_unique<UniqueResult>(entity);
     } break;
+    case WrappedResponseCase::kUpdateResponse: {
+      auto *updatedIdResponse = responses->mutable_update_response();
+      if (!updatedIdResponse->entity_errors().empty()) {
+        this->status = TransactionStatus::TRANSACTION_ERROR(
+          "The request returned with errors.");
+      }
+      this->result_set = std::make_unique<UniqueResult>(updatedIdResponse);
+    } break;
     case WrappedResponseCase::kInsertResponse: {
       auto *insertedIdResponse = responses->mutable_insert_response();
       if (!insertedIdResponse->entity_errors().empty()) {
diff --git a/test/test_entity.cpp b/test/test_entity.cpp
index 1f3adf99b0702f7a369e20b42d2f8a3af3ea94dd..62e52abf3cccfedae4c9d581f2952fc5f5b9123f 100644
--- a/test/test_entity.cpp
+++ b/test/test_entity.cpp
@@ -61,16 +61,16 @@ TEST(test_entity, test_insert_entity) {
     std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
 
   auto entity = Entity();
-  entity.SetId("entity_id");
-  entity.SetVersionId("version_id");
+  entity.SetRole("entity_role");
+  entity.SetName("entity_name");
 
-  EXPECT_EQ(entity.GetId(), "entity_id");
-  EXPECT_EQ(entity.GetVersionId(), "version_id");
+  EXPECT_EQ(entity.GetRole(), "entity_role");
+  EXPECT_EQ(entity.GetName(), "entity_name");
 
   transaction.InsertEntity(&entity);
 
-  EXPECT_EQ(entity.GetId(), "entity_id");
-  EXPECT_EQ(entity.GetVersionId(), "version_id");
+  EXPECT_EQ(entity.GetRole(), "entity_role");
+  EXPECT_EQ(entity.GetName(), "entity_name");
 }
 
 // TODO(tf) cognitive complexity > 25 (threshold)
@@ -83,7 +83,6 @@ TEST(test_entity, test_from_id_response) { // NOLINT
 
   Entity entity(&idResponse);
 
-  std::cout << entity.ToString() << std::endl;
   EXPECT_EQ(entity.GetId(), "entity_id");
   EXPECT_EQ(entity.GetErrors().Size(), 1);
   EXPECT_EQ(entity.GetErrors().At(0).GetDescription(), "error_desc");
@@ -99,16 +98,19 @@ TEST(test_entity, test_from_id_response) { // NOLINT
   info->set_description("info_desc");
   info->set_code(MessageCode::UNSPECIFIED);
 
+  std::cout << entity.ToString() << std::endl;
   Entity other_ent(&idr_warnings_and_infos);
 
+  std::cout << entity.ToString() << std::endl;
   EXPECT_EQ(other_ent.GetId(), "other_entity_id");
+  std::cout << entity.ToString() << std::endl;
   EXPECT_EQ(other_ent.GetWarnings().Size(), 1);
   EXPECT_EQ(other_ent.GetWarnings().At(0).GetDescription(), "warning_desc");
   EXPECT_EQ(other_ent.GetWarnings().At(0).GetCode(),
             MessageCode::ENTITY_HAS_NO_PROPERTIES);
+  std::cout << entity.ToString() << std::endl;
   EXPECT_EQ(other_ent.GetInfos().Size(), 1);
   EXPECT_EQ(other_ent.GetInfos().At(0).GetDescription(), "info_desc");
   EXPECT_EQ(other_ent.GetInfos().At(0).GetCode(), MessageCode::UNSPECIFIED);
 }
-
 } // namespace caosdb::entity
diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp
index b0f77d074d5d3bbcef2c05d020378992061a98d5..e17f34960438ce51f41e38da7ad7de4444e741dd 100644
--- a/test/test_transaction.cpp
+++ b/test/test_transaction.cpp
@@ -133,4 +133,17 @@ TEST(test_transaction, test_multi_result_set_three) {
   std::cout << rs.At(1).ToString();
 }
 
+TEST(test_transaction, test_update_entity) {
+  const auto *host = "localhost";
+  auto configuration = InsecureConnectionConfiguration(host, 8000);
+  Connection connection(configuration);
+  auto transaction = connection.CreateTransaction();
+
+  caosdb::entity::Entity update_entity;
+  update_entity.SetRole("New role");
+  auto error = transaction->UpdateEntity(&update_entity);
+
+  EXPECT_EQ(error, StatusCode::ORIGINAL_ENTITY_MISSING_ID);
+}
+
 } // namespace caosdb::transaction