diff --git a/gracedb/superevents/api/serializers.py b/gracedb/superevents/api/serializers.py
index 83aaf6146a07efac7dfcf28dbdba2d34d67880c7..c70e53824e9050823ae9e0a57f9a3d8ea18d577a 100644
--- a/gracedb/superevents/api/serializers.py
+++ b/gracedb/superevents/api/serializers.py
@@ -1,11 +1,13 @@
 from rest_framework import serializers, validators
+from rest_framework.exceptions import ValidationError
 from django.contrib.auth import get_user_model
 from django.utils.translation import ugettext_lazy as _
 from django.conf import settings
 from ..models import Superevent, Labelling, Log, VOEvent, EMObservation, \
     EMFootprint, Signoff
 
-from .fields import ParentObjectDefault, CommaSeparatedOrListField
+from .fields import ParentObjectDefault, CommaSeparatedOrListField, \
+    ChoiceDisplayField
 from .settings import SUPEREVENT_LOOKUP_FIELD
 
 from events.models import Event, Label, Tag, EMGroup
@@ -25,6 +27,9 @@ class SupereventSerializer(serializers.ModelSerializer):
     default_error_messages = {
         'event_assigned': _('Event {graceid} is already assigned to a '
                             'Superevent'),
+        'category_mismatch': _('Event {graceid} is of type \'{e_category}\', '
+                               'and cannot be assigned to a superevent of '
+                                'type \'{s_category}\''),
     }
 
     # Fields
@@ -33,6 +38,9 @@ class SupereventSerializer(serializers.ModelSerializer):
     preferred_event = EventGraceidField(required=True)
     created = serializers.DateTimeField(format=settings.GRACE_STRFTIME_FORMAT,
         read_only=True)
+    category = ChoiceDisplayField(required=True,
+        choices=Superevent.SUPEREVENT_CATEGORY_CHOICES)
+
     # Add custom fields
     gw_events = serializers.SerializerMethodField(read_only=True)
     em_events = serializers.SerializerMethodField(read_only=True)
@@ -47,26 +55,40 @@ class SupereventSerializer(serializers.ModelSerializer):
 
     class Meta:
         model = Superevent
-        fields = ('superevent_id', 'created', 'submitter', 'preferred_event',
-            'events', 't_start', 't_0', 't_end', 'gw_events', 'em_events',
-            'labels', 'links', 'user')
+        fields = ('superevent_id', 'category', 'created', 'submitter',
+            'preferred_event', 'events', 't_start', 't_0', 't_end',
+            'gw_events', 'em_events', 'labels', 'links', 'user')
 
     def validate(self, data):
         data = super(SupereventSerializer, self).validate(data)
         preferred_event = data.get('preferred_event')
         events = data.get('events')
+        category = data.get('category')
+        category_display = \
+            dict(Superevent.SUPEREVENT_CATEGORY_CHOICES)[category]
 
         # Make sure preferred_event is not already assigned
-        if preferred_event:
-            if (preferred_event.superevent or hasattr(preferred_event,
-                'superevent_preferred_for')):
-                self.fail('event_assigned', graceid=preferred_event.graceid())
+        if (preferred_event.superevent or hasattr(preferred_event,
+            'superevent_preferred_for')):
+            self.fail('event_assigned', graceid=preferred_event.graceid())
+
+        # Check that preferred_event has the correct type for the superevent
+        # it's being assigned to
+        if not Superevent.event_category_check(preferred_event, category):
+            self.fail('category_mismatch', graceid=preferred_event.graceid(),
+                e_category=preferred_event.get_event_category(),
+                s_category=category_display)
 
         # Make sure the events are not already assigned to a superevent
         if events:
             for ev in events:
                 if (ev.superevent or hasattr(ev, 'superevent_preferred_for')):
                     self.fail('event_assigned', graceid=ev.graceid())
+                # Check each event for type compatibility
+                if not Superevent.event_category_check(ev, category):
+                    self.fail('category_mismatch', graceid=ev.graceid(),
+                        e_category=ev.get_event_category(),
+                        s_category=category_display)
 
         return data
 
@@ -109,18 +131,32 @@ class SupereventUpdateSerializer(SupereventSerializer):
     Used for updates ONLY (PUT/PATCH). Overrides validation which is needed
     for object creation.
     """
+    allowed_fields = ('t_start', 't_0', 't_end', 'preferred_event')
 
     def __init__(self, *args, **kwargs):
         super(SupereventUpdateSerializer, self).__init__(*args, **kwargs)
-        self.fields['events'].read_only = True
-        self.fields['labels'].read_only = True
+        # Set all fields except self.allowed_fields to be read_only.
+        # Safeguard against attempts to set other attributes through this
+        # resource, but still allows us to return the same serialized object
+        # as for the serializer that this inherits from
+        for name in self.fields:
+            if name not in self.allowed_fields:
+                self.fields.get(name).read_only = True
 
     def validate(self, data):
-        # We don't want to use the SupereventSerializers validate method, which
-        # is why we use that class in the super() call here
+        # We don't want to use the SupereventSerializer's validate method,
+        # which is why we use that class in the super() call here
         data = super(SupereventSerializer, self).validate(data)
         preferred_event = data.get('preferred_event')
 
+        # Only pass through attributes which are being changed
+        updated_attributes = {k: v for k,v in data.items()
+            if getattr(self.instance, k, None) != v}
+
+        # Fail if nothing would be updated
+        if not updated_attributes:
+            raise ValidationError('Request would not modify the superevent')
+
         # Make sure preferred_event is not already assigned
         if preferred_event and (self.instance.preferred_event != 
             preferred_event):
@@ -134,7 +170,7 @@ class SupereventUpdateSerializer(SupereventSerializer):
                 preferred_event.superevent_preferred_for != self.instance):
                 self.fail('event_assigned', graceid=preferred_event.graceid())
 
-        return data
+        return updated_attributes
 
     def update(self, instance, validated_data):
         # Function-level import to prevent circular import in alerts
@@ -155,6 +191,9 @@ class SupereventEventSerializer(serializers.ModelSerializer):
     default_error_messages = {
         'event_assigned': _('Event {graceid} is already assigned to a '
                             'Superevent'),
+        'category_mismatch': _('Event {graceid} is of type \'{e_category}\', '
+                               'and cannot be assigned to a superevent of '
+                               'type \'{s_category}\''),
     }
     self = serializers.SerializerMethodField(read_only=True)
     event = EventGraceidField(write_only=True)
@@ -174,9 +213,20 @@ class SupereventEventSerializer(serializers.ModelSerializer):
 
     def validate(self, data):
         data = super(SupereventEventSerializer, self).validate(data)
-        event = data.get('event', None)
+        event = data.get('event')
+        superevent = data.get('superevent')
+
+        # Check if event is already assigned to a superevent
         if (event.superevent or hasattr(event, 'superevent_preferred_for')):
             self.fail('event_assigned', graceid=event.graceid())
+
+        # Check that event has the correct type for the superevent it's being
+        # assigned to
+        if not superevent.event_compatible(event):
+            self.fail('category_mismatch', graceid=event.graceid(),
+                e_category=event.get_event_category(),
+                s_category=superevent.get_category_display())
+
         return data
 
     def create(self, validated_data):
diff --git a/gracedb/superevents/utils.py b/gracedb/superevents/utils.py
index f3384bdc296dae9d36f8bd3a5345232693b3f6e7..8b75fe1a1d174a3a8870fa8c8007397f09dffab7 100644
--- a/gracedb/superevents/utils.py
+++ b/gracedb/superevents/utils.py
@@ -28,7 +28,8 @@ logger = logging.getLogger(__name__)
 # TODO:
 # Add decorator to check access permissions (??) not sure if we should do it here or in the viewset itself
 def create_superevent(submitter, t_start, t_0, t_end, preferred_event,
-    events=[], labels=[], add_log_message=True, issue_alert=True):
+    events=[], labels=[], category='P', add_log_message=True,
+    issue_alert=True):
     """
     Utility method for creating superevents.
 
@@ -49,7 +50,8 @@ def create_superevent(submitter, t_start, t_0, t_end, preferred_event,
 
     # Create superevent
     s = Superevent.objects.create(submitter=submitter, t_start=t_start,
-        t_0=t_0, t_end=t_end, preferred_event=preferred_event)
+        t_0=t_0, t_end=t_end, preferred_event=preferred_event,
+        category=category)
 
     # Create a log message to record initial superevent parameters
     creation_comment = ("Superevent created with t_start={t_start}, t_0={t_0},"
@@ -267,6 +269,16 @@ def add_event_to_superevent(superevent, event, user, add_event_log=True,
     We return log objects in case they are needed elsewhere
     """
 
+    # Check that the event is of the correct type to be added
+    # to a superevent
+    if not superevent.event_compatible(event):
+        raise Superevent.EventCategoryMismatchError(
+            _(('Event {graceid} is of type \'{e_category}\', and '
+               'cannot be assigned to a superevent of type '
+               '\'{s_category}\'').format(graceid=event.graceid(),
+                e_category=event.get_event_category(),
+                s_category=superevent.get_category_display())))
+
     # Add event to superevent
     superevent.events.add(event)