diff --git a/src/main/java/org/caosdb/server/entity/ClientMessage.java b/src/main/java/org/caosdb/server/entity/ClientMessage.java
index 22f257da013541584ad592d717fe7f5663f3baea..19fe9675a4dd0190304cd3164eb9fa4a40a6312f 100644
--- a/src/main/java/org/caosdb/server/entity/ClientMessage.java
+++ b/src/main/java/org/caosdb/server/entity/ClientMessage.java
@@ -41,4 +41,14 @@ public class ClientMessage extends Message {
   public String getProperty(String key) {
     return properties.get(key);
   }
+
+  @Override
+  public String toString() {
+    return this.type + " - " + this.properties.toString();
+  }
+
+  @Override
+  public int hashCode() {
+    return type.hashCode() + this.properties.hashCode();
+  }
 }
diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java
index 76e6ba5d4e3c2b02299715cae681c48083ff97c4..e88bff24c28fb5151e35f2593450065dd637e10c 100644
--- a/src/main/java/org/caosdb/server/jobs/Job.java
+++ b/src/main/java/org/caosdb/server/jobs/Job.java
@@ -225,14 +225,6 @@ public abstract class Job {
     }
   }
 
-  //  @Override
-  //  public boolean notifyObserver(final String e, final Observable o) {
-  //    if (getEntity().getEntityStatus() != EntityStatus.UNQUALIFIED) {
-  //      getTransaction().getSchedule().runJob(this);
-  //    }
-  //    return true;
-  //  }
-
   static HashMap<String, Class<? extends Job>> allClasses = null;
   private static List<Class<? extends Job>> loadAlways;
 
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 65df0fefa425617de6fcaf568782a8922a186b31..642d63bc4a091538bf6e8d4747256e833e0f51c0 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
@@ -16,28 +16,41 @@ public class CheckStateTransition extends EntityStateJob {
 
   private static final Message TRANSITION_NOT_ALLOWED =
       new Message(MessageType.Error, "Transition not allowed.");
+  private static final Message INITIAL_STATE_NOT_ALLOWED =
+      new Message(MessageType.Error, "Initial state not allowed.");
+  private static final Message FINAL_STATE_NOT_ALLOWED =
+      new Message(MessageType.Error, "Final state not allowed.");
+  private static final String FORCE_FINAL_STATE = "forceFinalState";
 
   @Override
   protected void run() {
     try {
+      State nextState = getState();
+      if (nextState != null) {
+        checkStateValid(nextState);
+      }
       if (getEntity() instanceof UpdateEntity) {
-        StateMessage toState = getStateMessage();
-        StateMessage fromState = getStateMessage(((UpdateEntity) getEntity()).getOriginal());
-        checkStateTransition(fromState, toState);
+        State fromState = getState(((UpdateEntity) getEntity()).getOriginal());
+        checkStateTransition(fromState, nextState);
       } else if (getEntity() instanceof DeleteEntity) {
-        StateMessage finalState = getStateMessage();
-        checkFinalState(finalState);
+        if (nextState != null) checkFinalState(nextState);
       } else {
-        StateMessage initialState = getStateMessage();
-        checkInitialState(initialState);
+        if (nextState != null) checkInitialState(nextState);
       }
     } catch (Message m) {
       getEntity().addError(m);
     }
   }
 
+  private void checkStateValid(State state) throws Message {
+    if (state.isFinal() || state.isInitial() || state.getStateModel().getStates().contains(state)) {
+      return;
+    }
+    throw STATE_NOT_IN_STATE_MODEL;
+  }
+
   /** Check if state is valid and transition is allowed */
-  private void checkStateTransition(StateMessage fromState, StateMessage toState) throws Message {
+  private void checkStateTransition(State fromState, State toState) throws Message {
     if (fromState == null && toState == null) {
       return;
     } else if (fromState == null && toState != null) {
@@ -50,32 +63,44 @@ public class CheckStateTransition extends EntityStateJob {
 
     StateModel stateModel = findMatchingStateModel(fromState, toState);
     if (stateModel == null) {
+      // change from one stateModel to another
       checkInitialState(toState);
       checkFinalState(fromState);
       return;
     }
 
     for (Transition t : stateModel.getTransitions()) {
-      if (t.fromStatesInclude(fromState.getState()) && t.toStatesInclude(toState.getState())) {
+      if (t.fromStatesInclude(fromState) && t.toStatesInclude(toState)) {
+        // TODO permissions
         return;
       }
     }
     throw TRANSITION_NOT_ALLOWED;
   }
 
-  private StateModel findMatchingStateModel(StateMessage fromState, StateMessage toState)
-      throws Message {
+  private StateModel findMatchingStateModel(State fromState, State toState) throws Message {
     if (fromState.getStateModel().equals(toState.getStateModel())) {
       return fromState.getStateModel();
     }
     return null;
   }
 
-  private void checkFinalState(StateMessage fromState) {
-    // TODO Auto-generated method stub
+  private void checkFinalState(State fromState) throws Message {
+    if (!fromState.isFinal()) {
+      if ("true"
+          .equalsIgnoreCase(getTransaction().getContainer().getFlags().get(FORCE_FINAL_STATE))) {
+        // TODO permissions
+        return;
+      }
+      throw FINAL_STATE_NOT_ALLOWED;
+    }
+    // TODO permissions
   }
 
-  private void checkInitialState(StateMessage toState) {
-    // TODO Auto-generated method stub
+  private void checkInitialState(State toState) throws Message {
+    if (!toState.isInitial()) {
+      throw INITIAL_STATE_NOT_ALLOWED;
+    }
+    // TODO permissions
   }
 }
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 32f1e0193102430e4923c96c9ceb7fa2099b52da..6137f72fb199aa45a354449199962fa0cea8f8a2 100644
--- a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
+++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
@@ -1,5 +1,6 @@
 package org.caosdb.server.jobs.core;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -24,24 +25,81 @@ import org.caosdb.server.jobs.EntityJob;
 import org.caosdb.server.query.Query;
 import org.jdom2.Element;
 
+/**
+ * The EntityStateJob is the abstract base class for four EntityJobs.
+ *
+ * <p>1. The InitEntityState job reads ClientMessages or StateProperties with tag state and converts
+ * them into instances of State. This job runs during WriteTransactions. This job runs during the
+ * INIT Phase and does not perform any checks other than those necessary for the conversion.
+ *
+ * <p>2. The CheckStateTransition job checks if the attempted state transition is in compliance with
+ * the state model. This job runs during the CHECK phase and should do all necessary consistency and
+ * permission checks.
+ *
+ * <p>3. The MakeStateProperty job constructs an ordinary Property from the State right before the
+ * entity is being written to the back-end and after any checks run.
+ *
+ * <p>4. The MakeStateMessage job converts a state property (back) into State messages and appends
+ * them to the entity.
+ *
+ * <p>Only the 4th job runs during Retrieve transitions. During WriteTransactions all four jobs do
+ * run.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
 public abstract class EntityStateJob extends EntityJob {
 
+  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";
+  public static final String INITIAL_STATE_PROPERTY_NAME = "final";
+  public static final String STATE_RECORD_TYPE_NAME = "State";
+  public static final String STATE_MODEL_RECORD_TYPE_NAME = "StateModel";
+  public static final String TRANSITION_RECORD_TYPE_NAME = "Transition";
+  public static final String STATE_XML_TAG = "State";
+  public static final String MODEL_ATTRIBUTE_NAME = "model";
+  public static final String STATE_ATTRIBUTE_NAME = "name";
+
+  public static final Message STATE_MODEL_NOT_FOUND =
+      new Message(MessageType.Error, "StateModel not found.");
+  public static final Message STATE_NOT_IN_STATE_MODEL =
+      new Message(MessageType.Error, "State does not exist in this StateModel.");
+  public static final Message COULD_NOT_GENERATE_STATE_MESSAGE =
+      new Message(MessageType.Error, "Could not generate the state message.");
+  public static final Message COULD_NOT_GENERATE_TRANSITIONS =
+      new Message(MessageType.Error, "Could not generate transitions.");
+  public static final Message COULD_NOT_GENERATE_STATES =
+      new Message(MessageType.Error, "Could not generate states.");
+  public static final Message STATE_MODEL_NOT_SPECIFIED =
+      new Message(MessageType.Error, "State model not specified.");
+  public static final Message STATE_NOT_SPECIFIED =
+      new Message(MessageType.Error, "State not specified.");
+
+  /**
+   * Represents a Transition which is identified by a name and the two States from and to which an
+   * entity is being transitioned.
+   *
+   * <p>Currently, only exactly one toState and one fromState can be defined. However, it might be
+   * allowed in the future to have multiple states here.
+   *
+   * @author Timm Fitschen (t.fitschen@indiscale.com)
+   */
   public class Transition {
 
-    private String transitionIdVersion;
     private State fromState;
     private State toState;
+    private String name;
 
     public Transition(EntityInterface transition) throws Message {
-      this.transitionIdVersion = transition.getIdVersion();
+      this.name = transition.getName();
       this.fromState = getFromState(transition);
       this.toState = getToState(transition);
     }
 
     private State getToState(EntityInterface transition) throws Message {
       for (Property p : transition.getProperties()) {
-        if (p.getName().equals("to")) {
-          return createStateMessage(p).getState();
+        if (p.getName().equals(TO_STATE_PROPERTY_NAME)) {
+          return createState(p);
         }
       }
       return null;
@@ -49,8 +107,8 @@ public abstract class EntityStateJob extends EntityJob {
 
     private State getFromState(EntityInterface transition) throws Message {
       for (Property p : transition.getProperties()) {
-        if (p.getName().equals("from")) {
-          return createStateMessage(p).getState();
+        if (p.getName().equals(FROM_STATE_PROPERTY_NAME)) {
+          return createState(p);
         }
       }
       return null;
@@ -75,48 +133,167 @@ public abstract class EntityStateJob extends EntityJob {
     @Override
     public boolean equals(Object obj) {
       if (obj instanceof Transition) {
-        return ((Transition) obj).transitionIdVersion.equals(this.transitionIdVersion);
+        Transition that = (Transition) obj;
+        return Objects.equals(this.getName(), that.getName())
+            && Objects.equals(this.getFromState(), that.getFromState())
+            && Objects.equals(this.getToState(), that.getToState());
       }
       return false;
     }
 
+    public String getName() {
+      return this.name;
+    }
+
     @Override
-    public int hashCode() {
-      return 1239141238 + this.transitionIdVersion.hashCode();
+    public String toString() {
+      return "Transition (name="
+          + getName()
+          + ", from="
+          + getFromState().getStateName()
+          + ", to="
+          + getToState().getStateName()
+          + ")";
     }
   }
 
+  /**
+   * The State instance represents a single entity state. This class is used for concrete state (the
+   * state of a stateful entity, say a Record) and abstract states (states which belong to a
+   * StateModel entity).
+   *
+   * <p>States are identified via their name and the name of the model to which they belong.
+   *
+   * <p>States are represented by Records with the state's name as the Record name. They belong to a
+   * StateModel iff the StateModel RecordType references the State Record. Each State should only
+   * belong to one StateModel.
+   *
+   * <p>Furthermore, States are the start or end point of Transitions which belong to the same
+   * StateModel. Each State can be part of several transitions at the same time.
+   *
+   * @author Timm Fitschen (t.fitschen@indiscale.com)
+   */
   public class State implements ToElementable {
 
-    private String stateIdVersion;
+    private String stateModelName = null;
+    private String stateName = null;
+    private EntityInterface stateEntity = null;
+    private EntityInterface stateModelEntity = null;
+    private StateModel stateModel;
 
-    public State(EntityInterface stateEntity) {
-      this.stateIdVersion = stateEntity.getIdVersion();
+    public State(String stateName, String stateModelName) throws Message {
+      this.stateName = stateName;
+      this.stateModelName = stateModelName;
+    }
+
+    public State(EntityInterface stateEntity, EntityInterface stateModelEntity) throws Message {
+      this.stateEntity = stateEntity;
+      this.stateName = stateEntity.getName();
+      this.stateModelEntity = stateModelEntity;
+      this.stateModelName = stateModelEntity.getName();
     }
 
     @Override
     public boolean equals(Object obj) {
       if (obj instanceof State) {
-        return ((State) obj).stateIdVersion.equals(this.stateIdVersion);
+        State that = (State) obj;
+        return Objects.equals(that.getStateName(), this.getStateName())
+            && Objects.equals(that.getStateModelName(), this.getStateModelName());
       }
       return false;
     }
 
     @Override
     public int hashCode() {
-      return 1239141238 + this.stateIdVersion.hashCode();
+      return 21364234 + this.getStateName().hashCode() + this.getStateModelName().hashCode();
     }
 
     @Override
     public void addToElement(Element ret) {
-      // TODO Auto-generated method stub
+      Element e = new Element(STATE_XML_TAG);
+      if (this.getStateModelName() != null) {
+        e.setAttribute(MODEL_ATTRIBUTE_NAME, this.getStateModelName());
+      }
+      if (this.getStateName() != null) {
+        e.setAttribute(STATE_ATTRIBUTE_NAME, this.getStateName());
+      }
+      ret.addContent(e);
+    }
+
+    private String getStateModelName() {
+      return this.stateModelName;
+    }
+
+    private String getStateName() {
+      return this.stateName;
+    }
+
+    public StateModel getStateModel() throws Message {
+      if (this.stateModel == null) {
+        this.stateModel = new StateModel(getStateModelEntity());
+      }
+      return this.stateModel;
+    }
+
+    public boolean isInitial() throws Message {
+      return Objects.equals(this, getStateModel().initialState);
+    }
+
+    public boolean isFinal() throws Message {
+      return Objects.equals(this, getStateModel().finalState);
+    }
+
+    /**
+     * Create a property which represents the current entity state of a stateful entity.
+     *
+     * @return stateProperty
+     * @throws Message
+     */
+    public Property createStateProperty() throws Message {
+      EntityInterface stateRecordType = getStateRecordType();
+      Property stateProperty = new Property(stateRecordType);
+      stateProperty.setDatatype(new ReferenceDatatype2(stateRecordType));
+      stateProperty.setValue(new ReferenceValue(getStateEntity(), false));
+      stateProperty.setStatementStatus(StatementStatus.FIX);
+      return stateProperty;
+    }
 
+    public EntityInterface getStateEntity() throws Message {
+      if (this.stateEntity == null) {
+        this.stateEntity = retrieveStateEntity(getStateModelEntity(), this.getStateName());
+      }
+      return this.stateEntity;
+    }
+
+    public EntityInterface getStateModelEntity() throws Message {
+      if (this.stateModelEntity == null) {
+        this.stateModelEntity = retrieveStateModelEntity(this.getStateModelName());
+      }
+      return this.stateModelEntity;
+    }
+
+    @Override
+    public String toString() {
+      return "State (name=" + getStateName() + ", model=" + getStateModelName() + ")";
     }
   }
 
+  /**
+   * A StateModel is an abstract definition of a Finite State Machine for entities.
+   *
+   * <p>It consists of a set of States, a set of transitions, a initial state and a final state.
+   *
+   * <p>If the StateModel has no initial state, it cannot be initialized (no entity will ever be in
+   * any of the StateModel's states) without using the forceInitialState flag.
+   *
+   * <p>If the StateModel has not final state, an entity with any of the states from this StateModel
+   * cannot leave this StateModel (and cannot be deleted either) without using the forceFinalState
+   * flag.
+   *
+   * @author Timm Fitschen (t.fitschen@indiscale.com)
+   */
   public class StateModel {
 
-    private String stateModelIdVersion;
     private String name;
     private Set<State> states;
     private Set<Transition> transitions;
@@ -124,8 +301,7 @@ public abstract class EntityStateJob extends EntityJob {
     private State finalState;
 
     public StateModel(EntityInterface stateModelEntity) throws Message {
-      this.stateModelIdVersion = stateModelEntity.getIdVersion();
-      this.getName();
+      this.name = stateModelEntity.getName();
       this.states = getStates(stateModelEntity);
       this.transitions = getTransitions(stateModelEntity);
       this.finalState = getFinalState(stateModelEntity);
@@ -134,8 +310,8 @@ public abstract class EntityStateJob extends EntityJob {
 
     private State getInitialState(EntityInterface stateModelEntity) throws Message {
       for (Property p : stateModelEntity.getProperties()) {
-        if (p.getName().equals("initial")) {
-          return createStateMessage(p).getState();
+        if (p.getName().equals(INITIAL_STATE_PROPERTY_NAME)) {
+          return createState(p);
         }
       }
       return null;
@@ -143,8 +319,8 @@ public abstract class EntityStateJob extends EntityJob {
 
     private State getFinalState(EntityInterface stateModelEntity) throws Message {
       for (Property p : stateModelEntity.getProperties()) {
-        if (p.getName().equals("final")) {
-          return createStateMessage(p).getState();
+        if (p.getName().equals(FINAL_STATE_PROPERTY_NAME)) {
+          return createState(p);
         }
       }
       return null;
@@ -152,7 +328,7 @@ public abstract class EntityStateJob extends EntityJob {
 
     private Set<Transition> getTransitions(EntityInterface stateModelEntity) throws Message {
       for (Property p : stateModelEntity.getProperties()) {
-        if (p.getName().equals("Transition")) {
+        if (p.getName().equals(TRANSITION_RECORD_TYPE_NAME)) {
           return createTransitions(p);
         }
       }
@@ -175,7 +351,6 @@ public abstract class EntityStateJob extends EntityJob {
           }
         }
       } catch (Exception e) {
-        e.printStackTrace();
         throw COULD_NOT_GENERATE_TRANSITIONS;
       }
       return result;
@@ -183,33 +358,32 @@ public abstract class EntityStateJob extends EntityJob {
 
     private Set<State> getStates(EntityInterface stateModelEntity) throws Message {
       for (Property p : stateModelEntity.getProperties()) {
-        if (p.getName().equals("State")) {
-          return createStates(p);
+        if (p.getName().equals(STATE_RECORD_TYPE_NAME)) {
+          return createStates(p, stateModelEntity);
         }
       }
       return null;
     }
 
-    private Set<State> createStates(Property p) throws Message {
-      Set<State> result = new HashSet<>();
+    private Set<State> createStates(Property p, EntityInterface stateModelEntity) throws Message {
+      if (!(p.getDatatype() instanceof AbstractCollectionDatatype)) {
+        return null;
+      }
       try {
-        if (!(p.getDatatype() instanceof AbstractCollectionDatatype)) {
-          return result;
-        }
+        Set<State> result = new HashSet<>();
         p.parseValue();
         CollectionValue vals = (CollectionValue) p.getValue();
         for (IndexedSingleValue val : vals) {
           if (val.getWrapped() instanceof ReferenceValue) {
             Integer refid = ((ReferenceValue) val.getWrapped()).getId();
-            EntityInterface transition = retrieveValidEntity(refid);
-            result.add(new State(transition));
+            EntityInterface stateEntity = retrieveValidEntity(refid);
+            result.add(new State(stateEntity, stateModelEntity));
           }
         }
+        return result;
       } catch (Exception e) {
-        e.printStackTrace();
         throw COULD_NOT_GENERATE_STATES;
       }
-      return result;
     }
 
     public String getName() {
@@ -235,104 +409,12 @@ public abstract class EntityStateJob extends EntityJob {
     @Override
     public boolean equals(Object obj) {
       if (obj instanceof StateModel) {
-        return ((StateModel) obj).stateModelIdVersion.equals(this.stateModelIdVersion);
+        return ((StateModel) obj).getName().equals(this.getName());
       }
       return false;
     }
-
-    @Override
-    public int hashCode() {
-      return 1239141238 + this.stateModelIdVersion.hashCode();
-    }
   }
 
-  public class StateMessage implements ToElementable {
-
-    String name;
-    String model;
-    private EntityInterface stateModelEntity;
-    private EntityInterface stateEntity;
-
-    /**
-     * @param name
-     * @param model
-     */
-    public StateMessage(String name, String model) {
-      this.name = name;
-      this.model = model;
-    }
-
-    @Override
-    public void addToElement(Element ret) {
-      Element e = new Element("State");
-      e.setAttribute("model", this.model);
-      e.setAttribute("name", this.name);
-      ret.addContent(e);
-    }
-
-    public Property createStateProperty() {
-      EntityInterface stateRecordType = getStateRecordType();
-      Property stateProperty = new Property(stateRecordType);
-      stateProperty.setDatatype(new ReferenceDatatype2(stateRecordType));
-      stateProperty.setValue(new ReferenceValue(getStateEntity(), false));
-      stateProperty.setStatementStatus(StatementStatus.FIX);
-      return stateProperty;
-    }
-
-    private EntityInterface getStateRecordType() {
-      try {
-        return retrieveValidSparseEntityByName("State");
-      } catch (Message e) {
-        // TODO Auto-generated catch block
-        e.printStackTrace();
-        return null;
-      }
-    }
-
-    public EntityInterface getStateModelEntity() {
-      if (this.stateModelEntity == null) {
-        try {
-          this.stateModelEntity = retrieveStateModelEntity(model);
-        } catch (Message e) {
-          // TODO Auto-generated catch block
-          e.printStackTrace();
-        }
-      }
-      return this.stateModelEntity;
-    }
-
-    public EntityInterface getStateEntity() {
-      if (this.stateEntity == null) {
-        try {
-          this.stateEntity = retrieveStateEntity(getStateModelEntity(), this.name);
-        } catch (Message e) {
-          // TODO Auto-generated catch block
-          e.printStackTrace();
-        }
-      }
-      return this.stateEntity;
-    }
-
-    public StateModel getStateModel() throws Message {
-      return new StateModel(getStateModelEntity());
-    }
-
-    public State getState() {
-      return new State(getStateEntity());
-    }
-  }
-
-  private static final Message STATE_MODEL_NOT_FOUND =
-      new Message(MessageType.Error, "StateModel does not exist.");
-  private static final Message STATE_ENTITY_NOT_FOUND =
-      new Message(MessageType.Error, "State does not exist in this StateModel.");
-  private static final Message COULD_NOT_GENERATE_STATE_MESSAGE =
-      new Message(MessageType.Error, "Could not generate the state message");
-  private static final Message COULD_NOT_GENERATE_TRANSITIONS =
-      new Message(MessageType.Error, "Could not generate transitions");
-  private static final Message COULD_NOT_GENERATE_STATES =
-      new Message(MessageType.Error, "Could not generate states");
-
   protected EntityInterface retrieveStateEntity(EntityInterface stateModelEntity, String stateName)
       throws Message {
 
@@ -340,10 +422,10 @@ public abstract class EntityStateJob extends EntityJob {
     try {
       list = execute(new GetIDByName(stateName)).getList();
     } catch (EntityDoesNotExistException e) {
-      throw STATE_ENTITY_NOT_FOUND;
+      throw STATE_NOT_IN_STATE_MODEL;
     }
     for (Property p : stateModelEntity.getProperties()) {
-      if (Objects.equals(p.getName(), "State")) {
+      if (Objects.equals(p.getName(), STATE_RECORD_TYPE_NAME)) {
         p.parseValue();
         for (IndexedSingleValue val : ((CollectionValue) p.getValue())) {
           ReferenceValue refid = (ReferenceValue) val.getWrapped();
@@ -357,7 +439,7 @@ public abstract class EntityStateJob extends EntityJob {
         }
       }
     }
-    throw STATE_ENTITY_NOT_FOUND;
+    throw STATE_NOT_IN_STATE_MODEL;
   }
 
   protected EntityInterface retrieveStateModelEntity(String stateModel) throws Message {
@@ -368,99 +450,114 @@ public abstract class EntityStateJob extends EntityJob {
     }
   }
 
-  protected StateMessage getStateMessage() {
-    return getStateMessage(false);
+  protected EntityInterface getStateRecordType() throws Message {
+    // TODO this should be cached
+    return retrieveValidSparseEntityByName(STATE_RECORD_TYPE_NAME);
   }
 
-  protected StateMessage getStateMessage(EntityInterface entity) {
-    return getStateMessage(entity, false);
+  protected State getState() {
+    return getState(false);
   }
 
-  protected StateMessage getStateMessage(EntityInterface entity, boolean remove) {
+  protected State getState(EntityInterface entity) {
+    return getState(entity, false);
+  }
+
+  protected State getState(EntityInterface entity, boolean remove) {
     Iterator<ToElementable> messages = entity.getMessages().iterator();
     while (messages.hasNext()) {
       ToElementable s = messages.next();
-      if (s instanceof StateMessage) {
+      if (s instanceof State) {
         if (remove) {
           messages.remove();
         }
-        return (StateMessage) s;
+        return (State) s;
       }
     }
     return null;
   }
 
-  protected StateMessage getStateMessage(boolean remove) {
-    return getStateMessage(getEntity(), remove);
+  protected State getState(boolean remove) {
+    return getState(getEntity(), remove);
   }
 
-  protected Property getStateProperty(EntityInterface entity, boolean remove) {
+  protected List<Property> getStateProperties(EntityInterface entity, boolean remove) {
     Iterator<Property> it = entity.getProperties().iterator();
+    List<Property> result = new ArrayList<>();
     while (it.hasNext()) {
       Property p = it.next();
-      if (Objects.equals(p.getName(), "State")) {
+      if (Objects.equals(p.getName(), STATE_RECORD_TYPE_NAME)) {
         if (!(p.getDatatype() instanceof ReferenceDatatype)) {
           continue;
         }
         if (remove) {
           it.remove();
         }
-        return p;
+        result.add(p);
       }
     }
-    return null;
+    return result;
   }
 
-  protected Property getStateProperty(boolean remove) {
-    return getStateProperty(getEntity(), remove);
+  protected List<Property> getStateProperties(boolean remove) {
+    return getStateProperties(getEntity(), remove);
   }
 
-  protected ClientMessage getStateClientMessage(EntityInterface entity, boolean remove) {
-    Iterator<Message> stateMessages = entity.getMessages("State").iterator();
+  protected List<ClientMessage> getStateClientMessages(EntityInterface entity, boolean remove) {
+    Iterator<Message> stateMessages = entity.getMessages(STATE_XML_TAG).iterator();
+    List<ClientMessage> result = new ArrayList<>();
     while (stateMessages.hasNext()) {
       Message s = stateMessages.next();
       if (s instanceof ClientMessage) {
         if (remove) {
           stateMessages.remove();
         }
-        return (ClientMessage) s;
+        result.add((ClientMessage) s);
       }
     }
-    return null;
+    return result;
   }
 
-  protected ClientMessage getStateClientMessage(boolean remove) {
-    return getStateClientMessage(getEntity(), remove);
+  protected List<ClientMessage> getStateClientMessages(boolean remove) {
+    return getStateClientMessages(getEntity(), remove);
   }
 
-  protected StateMessage createStateMessage(ClientMessage s) throws Message {
-    String stateModel = s.getProperty("model");
-    String stateName = s.getProperty("name");
-    return new StateMessage(stateName, stateModel);
+  protected State createState(ClientMessage s) throws Message {
+    String stateModel = s.getProperty(MODEL_ATTRIBUTE_NAME);
+    if (stateModel == null) {
+      throw STATE_MODEL_NOT_SPECIFIED;
+    }
+    String stateName = s.getProperty(STATE_ATTRIBUTE_NAME);
+    if (stateName == null) {
+      throw STATE_NOT_SPECIFIED;
+    }
+    return new State(stateName, stateModel);
   }
 
-  protected StateMessage createStateMessage(Property p) throws Message {
+  protected State createState(Property p) throws Message {
     try {
       p.parseValue();
       ReferenceValue refid = (ReferenceValue) p.getValue();
       EntityInterface stateEntity = retrieveValidSparseEntityById(refid.getId(), null);
 
       EntityInterface stateModelEntity = findStateModel(stateEntity);
-      return new StateMessage(stateEntity.getName(), stateModelEntity.getName());
+      return new State(stateEntity, stateModelEntity);
     } catch (Exception e) {
-      // TODO Auto-generated catch block
-      e.printStackTrace();
       throw COULD_NOT_GENERATE_STATE_MESSAGE;
     }
   }
 
   EntityInterface findStateModel(EntityInterface stateEntity) throws Exception {
+    // TODO this should be cached
     Query query =
         new Query(
-            "FIND Record StateModel WHICH REFERENCES " + Integer.toString(stateEntity.getId()),
+            "FIND RECORD "
+                + STATE_MODEL_RECORD_TYPE_NAME
+                + " WHICH REFERENCES "
+                + Integer.toString(stateEntity.getId()),
             getUser(),
             null);
     query.execute(getTransaction().getAccess());
-    return retrieveValidSparseEntityById(query.getResultSet().get(0), null);
+    return retrieveValidEntity(query.getResultSet().get(0));
   }
 }
diff --git a/src/main/java/org/caosdb/server/jobs/core/InitEntityState.java b/src/main/java/org/caosdb/server/jobs/core/InitEntityState.java
index 9a92dd42206ca798a7c6e60ef8ffe77d9dc9f488..4b1d74d9827b88bb286fac0d4a1f9b494eb803ad 100644
--- a/src/main/java/org/caosdb/server/jobs/core/InitEntityState.java
+++ b/src/main/java/org/caosdb/server/jobs/core/InitEntityState.java
@@ -1,5 +1,7 @@
 package org.caosdb.server.jobs.core;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 import org.caosdb.server.entity.ClientMessage;
 import org.caosdb.server.entity.Entity;
@@ -24,10 +26,23 @@ public class InitEntityState extends EntityStateJob implements Observer {
   @Override
   protected void run() {
     try {
-      State newState = initStateMessage(getEntity());
+      State newState = null;
+      {
+        List<State> states = initStateMessage(getEntity());
+        if (states.size() > 1) {
+          throw new Message(
+              MessageType.Error, "Currently, each entity can only have one state at a time.");
+        } else if (states.size() == 1) {
+          newState = states.get(0);
+        }
+      }
       try {
         if (getEntity() instanceof UpdateEntity) {
-          State oldState = initStateMessage(((UpdateEntity) getEntity()).getOriginal());
+          List<State> states = initStateMessage(((UpdateEntity) getEntity()).getOriginal());
+          State oldState = null;
+          if (states.size() == 1) {
+            oldState = states.get(0);
+          }
           if (!Objects.equals(newState, oldState)) {
             getEntity().acceptObserver(this);
           }
@@ -46,26 +61,33 @@ public class InitEntityState extends EntityStateJob implements Observer {
         MessageType.Warning, "State error in previous entity version\n" + m.getDescription());
   }
 
-  private State initStateMessage(EntityInterface entity) throws Message {
-    ClientMessage stateClientMessage = getStateClientMessage(entity, true);
-    if (stateClientMessage != null) {
-      StateMessage stateMessage = createStateMessage(stateClientMessage);
-      entity.addMessage(stateMessage);
-      return stateMessage.getState();
+  private List<State> initStateMessage(EntityInterface entity) throws Message {
+    List<ClientMessage> stateClientMessages = getStateClientMessages(entity, true);
+    List<State> result = new ArrayList<>();
+    if (stateClientMessages != null) {
+      for (ClientMessage s : stateClientMessages) {
+        State stateMessage = createState(s);
+        entity.addMessage(stateMessage);
+        result.add(stateMessage);
+      }
     }
-    Property stateProperty = getStateProperty(entity, true);
-    if (stateProperty != null) {
-      StateMessage stateMessage = createStateMessage(stateProperty);
-      entity.addMessage(stateMessage);
-      return stateMessage.getState();
+    List<Property> stateProperties = getStateProperties(entity, true);
+    if (stateProperties != null) {
+      for (Property p : stateProperties) {
+        State stateMessage = createState(p);
+        entity.addMessage(stateMessage);
+        result.add(stateMessage);
+      }
     }
-    return null;
+    return result;
   }
 
   @Override
   public boolean notifyObserver(String e, Observable o) {
     if (e == Entity.ENTITY_STATUS_CHANGED_EVENT) {
       if (o == getEntity() && getEntity().getEntityStatus() == EntityStatus.VALID) {
+        // The Update.deriveUpdate method didn't recognize that the state is changing and set the
+        // entity to "VALID"
         getEntity().setEntityStatus(EntityStatus.QUALIFIED);
         return false;
       }
diff --git a/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fb97b49bf3f1b5bab105164f9ecfee5d020949a
--- /dev/null
+++ b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
@@ -0,0 +1,29 @@
+package org.caosdb.server.jobs.core;
+
+import java.util.List;
+import org.caosdb.server.entity.Message;
+import org.caosdb.server.entity.wrapper.Property;
+import org.caosdb.server.jobs.JobAnnotation;
+import org.caosdb.server.jobs.JobExecutionTime;
+import org.caosdb.server.transaction.Retrieve;
+
+@JobAnnotation(loadAlways = true, time = JobExecutionTime.POST_TRANSACTION)
+public class MakeStateMessage extends EntityStateJob {
+
+  @Override
+  protected void run() {
+    try {
+      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);
+    }
+  }
+}
diff --git a/src/main/java/org/caosdb/server/jobs/core/ParseStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/MakeStateProperty.java
similarity index 56%
rename from src/main/java/org/caosdb/server/jobs/core/ParseStateMessage.java
rename to src/main/java/org/caosdb/server/jobs/core/MakeStateProperty.java
index d7cd71dc844b0b0001a0cab52b32d43a2560d843..2df5cb7bd047ad361e756103e600a0b16f72f86d 100644
--- a/src/main/java/org/caosdb/server/jobs/core/ParseStateMessage.java
+++ b/src/main/java/org/caosdb/server/jobs/core/MakeStateProperty.java
@@ -1,5 +1,6 @@
 package org.caosdb.server.jobs.core;
 
+import org.caosdb.server.entity.Message;
 import org.caosdb.server.jobs.JobAnnotation;
 import org.caosdb.server.jobs.JobExecutionTime;
 import org.caosdb.server.transaction.WriteTransaction;
@@ -8,15 +9,21 @@ import org.caosdb.server.transaction.WriteTransaction;
     loadAlways = true,
     transaction = WriteTransaction.class,
     time = JobExecutionTime.PRE_TRANSACTION)
-public class ParseStateMessage extends EntityStateJob {
+public class MakeStateProperty extends EntityStateJob {
 
   @Override
   protected void run() {
-    StateMessage s = getStateMessage(true);
-    if (s != null) addStateProperty(s);
+    State s = getState();
+    if (s != null) {
+      try {
+        addStateProperty(s);
+      } catch (Message e) {
+        getEntity().addError(e);
+      }
+    }
   }
 
-  private void addStateProperty(StateMessage stateEntity) {
+  private void addStateProperty(State stateEntity) throws Message {
     getEntity().addProperty(stateEntity.createStateProperty());
   }
 }
diff --git a/src/main/java/org/caosdb/server/jobs/core/WriteStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/WriteStateMessage.java
deleted file mode 100644
index 9fa21b7c0ee2248afa5606515fba858cd0e24361..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/jobs/core/WriteStateMessage.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.caosdb.server.jobs.core;
-
-import org.caosdb.server.entity.Message;
-import org.caosdb.server.entity.wrapper.Property;
-import org.caosdb.server.jobs.JobAnnotation;
-import org.caosdb.server.jobs.JobExecutionTime;
-
-@JobAnnotation(loadAlways = true, time = JobExecutionTime.POST_TRANSACTION)
-public class WriteStateMessage extends EntityStateJob {
-
-  @Override
-  protected void run() {
-    try {
-      Property stateProperty = getStateProperty(true);
-      if (stateProperty != null) {
-        StateMessage stateMessage = createStateMessage(stateProperty);
-        getEntity().addMessage(stateMessage);
-      }
-    } catch (Message e) {
-      getEntity().addError(e);
-    }
-  }
-}