diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9c9bf023c63fab22c7e9e8fd8eb65b3d7ef9043..b6ceac5e4ba32636ef076b018e6a33c150ed76a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+* New EntityState plug-in. The plug-in disabled by default and can be enabled
+  by setting the server property `EXT_ENTITY_STATE=ENABLED`. See
+  [!62](https://gitlab.com/caosdb/caosdb-server/-/merge_requests/62) for more
+  information.
 * New server property `SERVER_SIDE_SCRIPTING_BIN_DIRS` which accepts a comma or
   space separated list as values. The server looks for scripts in all
   directories in the order or the list and uses the first matching file.
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
index 40bf290e4ceb96194974aca7bd0ddbf58c2fe6a9..b64acf509685048b6b0036fbf77d20ea19a9363a 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
@@ -13,10 +13,7 @@ import org.caosdb.server.transaction.WriteTransaction;
  *
  * @author Timm Fitschen (t.fitschen@indiscale.com)
  */
-@JobAnnotation(
-    time = JobExecutionTime.CHECK,
-    transaction = WriteTransaction.class,
-    loadAlways = true)
+@JobAnnotation(time = JobExecutionTime.CHECK, transaction = WriteTransaction.class)
 public class CheckStateTransition extends EntityStateJob {
 
   private static final Message TRANSITION_NOT_ALLOWED =
diff --git a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
index 30a4de8e04d69ad3c4b1ea1c1ebc9a082d8f4920..39f4d6754ed665a0590c7a02f1e1882b8a907b64 100644
--- a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
+++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
@@ -49,6 +49,8 @@ import org.jdom2.Element;
  */
 public abstract class EntityStateJob extends EntityJob {
 
+  protected static final String SERVER_PROPERTY_EXT_ENTITY_STATE = "EXT_ENTITY_STATE";
+
   public static final String TO_STATE_PROPERTY_NAME = "to";
   public static final String FROM_STATE_PROPERTY_NAME = "from";
   public static final String FINAL_STATE_PROPERTY_NAME = "final";
diff --git a/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java b/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
index e7dc54af6d6f951725cc66e6839a476ba4cdb060..8bb1e00ac8de9c4dddab2c7a770c01ba19568572 100644
--- a/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
+++ b/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
@@ -3,6 +3,7 @@ package org.caosdb.server.jobs.core;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.entity.ClientMessage;
 import org.caosdb.server.entity.Entity;
 import org.caosdb.server.entity.EntityInterface;
@@ -35,8 +36,15 @@ public class InitEntityStateJobs extends EntityStateJob implements Observer {
 
   @Override
   protected void run() {
-    State newState = handleNewState();
-    handleOldState(newState);
+    if ("ENABLED".equals(CaosDBServer.getServerProperty(SERVER_PROPERTY_EXT_ENTITY_STATE))) {
+      State newState = handleNewState();
+      State oldState = handleOldState(newState);
+      if (newState != null || oldState != null) {
+        appendJob(CheckStateTransition.class);
+        appendJob(MakeStateProperty.class);
+        appendJob(MakeStateMessage.class);
+      }
+    }
   }
 
   /**
@@ -49,12 +57,14 @@ public class InitEntityStateJobs extends EntityStateJob implements Observer {
    * that to happen and changes the {@EntityStatus} back to normal.
    *
    * @param newState
+   * @return The old state or null.
    */
-  private void handleOldState(State newState) {
+  private State handleOldState(State newState) {
+    State oldState = null;
     try {
       if (getEntity() instanceof UpdateEntity) {
         List<State> states = initStateMessage(((UpdateEntity) getEntity()).getOriginal());
-        State oldState = null;
+        oldState = null;
         if (states.size() == 1) {
           oldState = states.get(0);
         }
@@ -65,12 +75,13 @@ public class InitEntityStateJobs extends EntityStateJob implements Observer {
     } catch (Message m) {
       getEntity().addWarning(STATE_ERROR_IN_ORIGINAL_ENTITY(m));
     }
+    return oldState;
   }
 
   /**
    * Converts the state property of this entity into a state message.
    *
-   * @return The new state.
+   * @return The new state or null.
    */
   private State handleNewState() {
     State newState = null;
diff --git a/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
index ce1fd5a974b61c7ed69e7ad633af10a6dc027a24..38fd239844df96cd94025225600964fb9e1bdd74 100644
--- a/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
+++ b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
@@ -1,6 +1,7 @@
 package org.caosdb.server.jobs.core;
 
 import java.util.List;
+import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.jobs.JobAnnotation;
@@ -16,24 +17,30 @@ import org.caosdb.server.transaction.Retrieve;
  *
  * @author Timm Fitschen (t.fitschen@indiscale.com)
  */
-@JobAnnotation(loadAlways = true, time = JobExecutionTime.POST_TRANSACTION)
+@JobAnnotation(
+    loadAlways = true,
+    transaction = Retrieve.class,
+    time = JobExecutionTime.POST_TRANSACTION)
 public class MakeStateMessage extends EntityStateJob {
 
   @Override
   protected void run() {
-    try {
-      // fetch all state properties and remove them from the entity (indicated by "true")
-      List<Property> stateProperties = getStateProperties(true);
 
-      // only add the State during Retrieve. In all other cases, the State is already present.
-      if (stateProperties != null && getTransaction() instanceof Retrieve) {
-        for (Property s : stateProperties) {
-          State stateMessage = createState(s);
-          getEntity().addMessage(stateMessage);
+    if ("ENABLED".equals(CaosDBServer.getServerProperty(SERVER_PROPERTY_EXT_ENTITY_STATE))) {
+      try {
+        // fetch all state properties and remove them from the entity (indicated by "true")
+        List<Property> stateProperties = getStateProperties(true);
+
+        // only add the State during Retrieve. In all other cases, the State is already present.
+        if (stateProperties != null && getTransaction() instanceof Retrieve) {
+          for (Property s : stateProperties) {
+            State stateMessage = createState(s);
+            getEntity().addMessage(stateMessage);
+          }
         }
+      } catch (Message e) {
+        getEntity().addError(e);
       }
-    } catch (Message e) {
-      getEntity().addError(e);
     }
   }
 }
diff --git a/src/main/java/org/caosdb/server/jobs/core/MakeStateProperty.java b/src/main/java/org/caosdb/server/jobs/core/MakeStateProperty.java
index 2df5cb7bd047ad361e756103e600a0b16f72f86d..0d290c0fb43d258d0f4700d60422c5a7541abf82 100644
--- a/src/main/java/org/caosdb/server/jobs/core/MakeStateProperty.java
+++ b/src/main/java/org/caosdb/server/jobs/core/MakeStateProperty.java
@@ -5,10 +5,7 @@ import org.caosdb.server.jobs.JobAnnotation;
 import org.caosdb.server.jobs.JobExecutionTime;
 import org.caosdb.server.transaction.WriteTransaction;
 
-@JobAnnotation(
-    loadAlways = true,
-    transaction = WriteTransaction.class,
-    time = JobExecutionTime.PRE_TRANSACTION)
+@JobAnnotation(transaction = WriteTransaction.class, time = JobExecutionTime.PRE_TRANSACTION)
 public class MakeStateProperty extends EntityStateJob {
 
   @Override