diff --git a/projects/factory.py b/projects/factory.py
index 7df4ee01405ea46db64e827510985e290a18ae8c..f826ab8d36261eb8a99acd51cdcfb2a725f1fcfb 100644
--- a/projects/factory.py
+++ b/projects/factory.py
@@ -4,7 +4,7 @@ import factory
 import factory.django
 
 from users.factory import UserFactory
-from .models import Project, Edition, Participation
+from .models import Project, Edition, Participation, EditionForm
 
 
 class ProjectFactory(factory.DjangoModelFactory):
@@ -37,6 +37,14 @@ class EditionFactory(factory.DjangoModelFactory):
         return project and project or ProjectFactory.create()
 
 
+class EditionFormFactory(factory.DjangoModelFactory):
+
+    class Meta:  # noqa
+        model = EditionForm
+
+    deadline = factory.Faker('future_date')
+
+
 class ParticipationFactory(factory.DjangoModelFactory):
     """Participation object factory."""
 
diff --git a/projects/serializers.py b/projects/serializers.py
index 7017c3e410ea270c6688fedfc3f50f5cf54661e0..6f7033d73dd2cae83667df89f726628e8021b2ef 100644
--- a/projects/serializers.py
+++ b/projects/serializers.py
@@ -4,12 +4,12 @@ from django.db import transaction
 from rest_framework import serializers
 
 from core.fields import MarkdownField
+from dynamicforms.serializers import FormDetailSerializer, FormEntrySerializer
+from profiles.serializers import TutorSerializer
 from users.fields import UserField
 from users.serializers import UserSerializer
-from profiles.serializers import TutorSerializer
-from dynamicforms.serializers import FormDetailSerializer, FormEntrySerializer
 
-from .models import Edition, Participation, Project, EditionForm
+from .models import Edition, EditionForm, Participation, Project
 
 
 class ProjectSerializer(serializers.HyperlinkedModelSerializer):
@@ -25,53 +25,20 @@ class ProjectSerializer(serializers.HyperlinkedModelSerializer):
         }
 
 
-class ParticipationSerializer(serializers.ModelSerializer):
-    """Serializer for project edition participations."""
-
-    user = UserField(
-        label='Utilisateur',
-        help_text='Identifier for the user who participates.')
-
-    edition = serializers.PrimaryKeyRelatedField(
-        queryset=Edition.objects.all(),
-        label='Édition',
-        help_text='Identifier for the associated edition.')
-
-    entry = FormEntrySerializer(write_only=True)
-
-    def create(self, validated_data: dict) -> Participation:
-        """Explicitly create as entry is a nested serializer."""
-        with transaction.atomic():
-            entry_data = validated_data['entry']
-            entry = FormEntrySerializer().create(entry_data)
-
-            participation = Participation.objects.create(
-                user=validated_data['user'],
-                edition=validated_data['edition'],
-                state=Participation.STATE_PENDING,
-                entry=entry,
-            )
-
-        return participation
-
-    class Meta:  # noqa
-        model = Participation
-        fields = ('id', 'submitted', 'user', 'edition', 'state', 'entry',)
-        extra_kwargs = {
-            'state': {
-                'label': 'State of the participation.'
-            }
-        }
-
-
 class EditionFormSerializer(serializers.ModelSerializer):
     """Serializer for edition form objects."""
 
     edition = serializers.PrimaryKeyRelatedField(read_only=True)
+    title = serializers.SerializerMethodField()
+
+    def get_title(self, obj) -> str:
+        """Return the form's title if form is set."""
+        form = getattr(obj, 'form', None)
+        return form and str(form) or None
 
     class Meta:  # noqa
         model = EditionForm
-        fields = ('id', 'edition', 'deadline')
+        fields = ('id', 'title', 'edition', 'deadline')
 
 
 class EditionFormDetailSerializer(EditionFormSerializer):
@@ -92,6 +59,7 @@ class EditionListSerializer(serializers.HyperlinkedModelSerializer):
     organizers = serializers.SerializerMethodField()
     participations = serializers.SerializerMethodField()
     edition_form = EditionFormSerializer()
+    participates = serializers.SerializerMethodField()
 
     def get_organizers(self, obj: Edition) -> int:
         """Return the number of organizers."""
@@ -101,15 +69,71 @@ class EditionListSerializer(serializers.HyperlinkedModelSerializer):
         """Return the number of participations."""
         return obj.participations.count()
 
+    def get_participates(self, obj: Edition) -> bool:
+        """Return whether the current user participates in the edition."""
+        request = self.context['request']
+        if not request.user:
+            return False
+        return request.user.pk in obj.participations.values_list('user__pk')
+
     class Meta:  # noqa
         model = Edition
         fields = ('id', 'url', 'name', 'year', 'project', 'description',
-                  'organizers', 'participations', 'edition_form',)
+                  'organizers', 'participations', 'edition_form',
+                  'participates',)
         extra_kwargs = {
             'url': {'view_name': 'api:edition-detail'},
         }
 
 
+class ParticipationSerializer(serializers.ModelSerializer):
+    """Serializer for project edition participations."""
+
+    user = UserField(
+        label='Utilisateur',
+        help_text='Identifier for the user who participates.')
+
+    edition_id = serializers.PrimaryKeyRelatedField(
+        source='edition',
+        queryset=Edition.objects.all(),
+        label='Édition',
+        help_text='Identifier for the associated edition.')
+
+    edition_form_title = serializers.SerializerMethodField()
+
+    entry = FormEntrySerializer(write_only=True)
+
+    def get_edition_form_title(self, obj: Participation) -> str:
+        form = getattr(obj.edition, 'edition_form', None)
+        return form and str(form) or None
+
+    def create(self, validated_data: dict) -> Participation:
+        """Explicitly create as entry is a nested serializer."""
+        with transaction.atomic():
+            entry_data = validated_data['entry']
+            entry = FormEntrySerializer().create(entry_data)
+
+            participation = Participation.objects.create(
+                user=validated_data['user'],
+                edition=validated_data['edition'],
+                state=Participation.STATE_PENDING,
+                entry=entry,
+            )
+
+        return participation
+
+    class Meta:  # noqa
+        model = Participation
+        fields = ('id', 'submitted', 'user',
+                  'edition_id', 'edition_form_title',
+                  'state', 'entry',)
+        extra_kwargs = {
+            'state': {
+                'label': 'State of the participation.'
+            }
+        }
+
+
 class EditionDetailSerializer(EditionListSerializer):
     """Detail serializer for Edition objects."""
 
diff --git a/projects/views.py b/projects/views.py
index 6d5d1815d5d499a3705a810ee533638126a1331a..6d9548be770e8084f49749c82a82c2c07fb3d2ca 100644
--- a/projects/views.py
+++ b/projects/views.py
@@ -1,6 +1,10 @@
 """Projects views."""
 
+from django.utils.timezone import now
 from rest_framework import mixins, permissions, viewsets
+from rest_framework.response import Response
+from rest_framework.decorators import action
+from django_filters.rest_framework.backends import DjangoFilterBackend
 
 from django_filters import rest_framework as filters
 
@@ -214,16 +218,50 @@ class EditionViewSet(viewsets.ReadOnlyModelViewSet):
         }
     """
 
-    queryset = Edition.objects.all()
+    queryset = Edition.objects.all().prefetch_related(
+        'participations', 'organizers'
+    )
     permission_classes = (permissions.IsAuthenticated,)
     filter_backends = (filters.DjangoFilterBackend,)
     filter_fields = ('project', 'year',)
 
     def get_serializer_class(self):
-        if self.action == 'list':
-            return EditionListSerializer
-        elif self.action == 'retrieve':
+        if self.action == 'retrieve':
             return EditionDetailSerializer
+        return EditionListSerializer
+
+    @action(methods=['get'], detail=False)
+    def open_registrations(self, request, **kwargs):
+        """Return a list of the editions with open registrations.
+
+        These are the editions that have an edition form set and whose
+        deadline is a future date.
+
+        ### Example response
+
+            [
+                {
+                    "id": 1,
+                    "url": "http://localhost:8000/api/editions/1/",
+                    "name": "",
+                    "year": 2018,
+                    "project": 1,
+                    "description": "",
+                    "organizers": 0,
+                    "participations": 3,
+                    "edition_form": {
+                        "id": 1,
+                        "edition": 1,
+                        "deadline": "2018-06-30"
+                    }
+                }
+            ]
+        """
+        queryset = self.get_queryset().filter(
+            edition_form__isnull=False,
+            edition_form__deadline__gte=now().date())
+        serializer = self.get_serializer(queryset, many=True)
+        return Response(serializer.data)
 
 
 class ParticipationViewSet(mixins.CreateModelMixin,
@@ -251,7 +289,8 @@ class ParticipationViewSet(mixins.CreateModelMixin,
                     "date_of_birth": null,
                     "url": "http://localhost:8000/api/users/3/"
                 },
-                "edition": 1,
+                "edition_id": 1,
+                "edition_form_title": "Inscriptions à Oser la Prépa 2018"
                 "state": "valid"
             },
         ]
@@ -276,7 +315,8 @@ class ParticipationViewSet(mixins.CreateModelMixin,
                 "date_of_birth": null,
                 "url": "http://localhost:8000/api/users/3/"
             },
-            "edition": 1,
+            "edition_id": 1,
+            "edition_form_title": "Inscriptions à Oser la Prépa 2018"
             "state": "valid"
         }
     """
@@ -284,3 +324,5 @@ class ParticipationViewSet(mixins.CreateModelMixin,
     queryset = Participation.objects.all()
     serializer_class = ParticipationSerializer
     permission_classes = (permissions.IsAuthenticated,)
+    filter_backends = (DjangoFilterBackend,)
+    filter_fields = ('user', 'state',)
diff --git a/tests/test_projects/test_edition_api.py b/tests/test_projects/test_edition_api.py
index f8c5dc1680dabbf6ceebbbc82238734f5ce3ff59..ff59f407491264f7f5203e3390bb90bbd34d2613 100644
--- a/tests/test_projects/test_edition_api.py
+++ b/tests/test_projects/test_edition_api.py
@@ -3,7 +3,7 @@
 from rest_framework import status
 from tests.utils import SimpleAPITestCase, logged_in
 
-from projects.factory import EditionFactory
+from projects.factory import EditionFactory, EditionFormFactory
 
 
 class EditionEndpointsTest(SimpleAPITestCase):
@@ -13,7 +13,7 @@ class EditionEndpointsTest(SimpleAPITestCase):
 
     read_expected_fields = {'id', 'url', 'name', 'year', 'project',
                             'description', 'organizers', 'participations',
-                            'edition_form',}
+                            'edition_form', 'participates'}
 
     def setUp(self):
         self.factory.create_batch(3)
@@ -51,3 +51,12 @@ class EditionEndpointsTest(SimpleAPITestCase):
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         fields = set(response.data)
         self.assertSetEqual(fields, self.read_expected_fields)
+
+    @logged_in
+    def test_list_open_registrations(self):
+        edition = self.factory.create()
+        EditionFormFactory.create(edition=edition)
+        url = '/api/editions/open_registrations/'
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(len(response.data), 1)
diff --git a/tests/test_projects/test_participation_api.py b/tests/test_projects/test_participation_api.py
index 75422d1795db8c2f9a656416eca968eb878b2947..83781452e648c07a231ba23f30ca668c45171549 100644
--- a/tests/test_projects/test_participation_api.py
+++ b/tests/test_projects/test_participation_api.py
@@ -13,7 +13,7 @@ class ParticipationReadTest(SimpleAPITestCase):
 
     factory = ParticipationFactory
 
-    read_expected_fields = {'id', 'user', 'edition',
+    read_expected_fields = {'id', 'user', 'edition_id', 'edition_form_title',
                             'state', 'submitted'}
 
     def setUp(self):
@@ -66,7 +66,7 @@ class ParticipationCreateTest(SimpleAPITestCase):
         }
         payload = {
             'user': user.pk,
-            'edition': edition.pk,
+            'edition_id': edition.pk,
             'entry': entry,
         }
         return self.client.post('/api/project-participations/',
@@ -80,5 +80,7 @@ class ParticipationCreateTest(SimpleAPITestCase):
     def test_returns_expected_fields(self):
         response = self.perform_create()
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
-        expected = {'id', 'user', 'edition', 'state', 'submitted'}
+        expected = {
+            'id', 'user', 'edition_id', 'edition_form_title',
+            'state', 'submitted'}
         self.assertSetEqual(expected, set(response.data))