diff --git a/dynamicforms/migrations/0014_auto_20180616_1812.py b/dynamicforms/migrations/0014_auto_20180616_1812.py
new file mode 100644
index 0000000000000000000000000000000000000000..5933a33697e2811600c1ac8e51b7c4038a5efb20
--- /dev/null
+++ b/dynamicforms/migrations/0014_auto_20180616_1812.py
@@ -0,0 +1,24 @@
+# Generated by Django 2.0.6 on 2018-06-16 16:12
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dynamicforms', '0013_auto_20180616_0149'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='answer',
+            name='entry',
+            field=models.ForeignKey(help_text='Entrée associée à la réponse.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='dynamicforms.FormEntry', verbose_name='entrée'),
+        ),
+        migrations.AlterField(
+            model_name='question',
+            name='type',
+            field=models.CharField(choices=[('text-small', 'Texte court'), ('text-long', 'Texte long'), ('yes-no', 'Oui/Non'), ('date', 'Date'), ('sex', 'Sexe')], max_length=100, verbose_name='type de question'),
+        ),
+    ]
diff --git a/dynamicforms/models.py b/dynamicforms/models.py
index 8985fbd479e286caffc1707376cf551e52a52c69..fff267b995eec0900f193553d4095865ad3a9ace 100644
--- a/dynamicforms/models.py
+++ b/dynamicforms/models.py
@@ -141,6 +141,7 @@ class Answer(models.Model):
     entry = models.ForeignKey(
         'FormEntry',
         on_delete=models.CASCADE,
+        null=True,
         related_name='answers',
         verbose_name='entrée',
         help_text="Entrée associée à la réponse.")
diff --git a/dynamicforms/serializers.py b/dynamicforms/serializers.py
index d1e77cf99ef1b20d6b6afbd8897688a89b5c68f7..eba1369ca1101c989fbacf84c4cdb322c9bd9d07 100644
--- a/dynamicforms/serializers.py
+++ b/dynamicforms/serializers.py
@@ -1,5 +1,6 @@
 """Dynamic forms serializers."""
 
+from typing import List
 from rest_framework import serializers
 from .models import Form, Section, Question, Answer, FormEntry, File
 
@@ -74,6 +75,21 @@ class FormEntrySerializer(serializers.ModelSerializer):
     )
     answers = AnswerSerializer(many=True)
 
+    def create(self, validated_data: dict) -> FormEntry:
+        """Create a form entry for validated input data."""
+        form = validated_data['form']
+        answers: List[dict] = validated_data['answers']
+        answer_serializer = AnswerSerializer()
+
+        form_entry = FormEntry.objects.create(form=form)
+
+        # Assign the newly created entry to each answer
+        for answer_data in answers:
+            answer = answer_serializer.create(answer_data)
+            form_entry.answers.add(answer)
+
+        return form_entry
+
     class Meta:  # noqa
         model = FormEntry
         fields = ('id', 'form', 'submitted', 'answers',)
diff --git a/projects/admin.py b/projects/admin.py
index 55de80060423203f87cb87286bcd14b7ff5ceb05..341c647b5619dfe54598015d1b634cfe606cd231 100644
--- a/projects/admin.py
+++ b/projects/admin.py
@@ -85,4 +85,5 @@ class ParticipationAdmin(admin.ModelAdmin):
 
     list_display = ('user', 'edition', 'submitted', 'state')
     list_filter = ('edition', 'submitted', 'state',)
+    readonly_fields = ('submitted',)
     search_fields = ('user__first_name', 'user__last_name', 'user__email',)
diff --git a/projects/migrations/0007_auto_20180616_1812.py b/projects/migrations/0007_auto_20180616_1812.py
new file mode 100644
index 0000000000000000000000000000000000000000..7835d9d7f1539e21b13ba82aecd56e7a0d1be6fc
--- /dev/null
+++ b/projects/migrations/0007_auto_20180616_1812.py
@@ -0,0 +1,30 @@
+# Generated by Django 2.0.6 on 2018-06-16 16:12
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dynamicforms', '0014_auto_20180616_1812'),
+        ('projects', '0006_auto_20180616_0145'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='participation',
+            name='entry',
+            field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_participation', to='dynamicforms.FormEntry'),
+        ),
+        migrations.AlterField(
+            model_name='editionform',
+            name='edition',
+            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='edition_form', to='projects.Edition', verbose_name='édition'),
+        ),
+        migrations.AlterField(
+            model_name='editionform',
+            name='form',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='dynamicforms.Form', verbose_name="formulaire d'inscription"),
+        ),
+    ]
diff --git a/projects/migrations/0008_auto_20180616_1919.py b/projects/migrations/0008_auto_20180616_1919.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a3740d95f0795c16037c58061be8fc61c505913
--- /dev/null
+++ b/projects/migrations/0008_auto_20180616_1919.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.0.6 on 2018-06-16 17:19
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('projects', '0007_auto_20180616_1812'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='participation',
+            name='entry',
+            field=models.OneToOneField(help_text="Réponses au formulaire d'inscription", null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project_participation', to='dynamicforms.FormEntry', verbose_name='entrée'),
+        ),
+    ]
diff --git a/projects/models.py b/projects/models.py
index 559f3fa7075c51176df3b48712cdf53b991400ee..a29e38be85b4af202ecdc4a1fa71cd130ddf3ab1 100644
--- a/projects/models.py
+++ b/projects/models.py
@@ -180,7 +180,14 @@ class Participation(models.Model):
         auto_now_add=True, verbose_name='soumis le',
         help_text='Date de soumission de la participation')
 
-    # TODO link to the edition form? to a form entry?
+    entry = models.OneToOneField(
+        'dynamicforms.FormEntry',
+        on_delete=models.CASCADE,
+        null=True,
+        related_name='project_participation',
+        verbose_name='entrée',
+        help_text="Réponses au formulaire d'inscription",
+    )
 
     STATE_PENDING = 'pending'
     STATE_VALIDATED = 'valid'
diff --git a/projects/serializers.py b/projects/serializers.py
index 7b4fd6db1f1ddf60928e226e5736b6e86e3acf2f..7017c3e410ea270c6688fedfc3f50f5cf54661e0 100644
--- a/projects/serializers.py
+++ b/projects/serializers.py
@@ -1,12 +1,13 @@
 """Projects serializers."""
 
+from django.db import transaction
 from rest_framework import serializers
 
 from core.fields import MarkdownField
 from users.fields import UserField
 from users.serializers import UserSerializer
 from profiles.serializers import TutorSerializer
-from dynamicforms.serializers import FormDetailSerializer
+from dynamicforms.serializers import FormDetailSerializer, FormEntrySerializer
 
 from .models import Edition, Participation, Project, EditionForm
 
@@ -36,9 +37,26 @@ class ParticipationSerializer(serializers.ModelSerializer):
         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',)
+        fields = ('id', 'submitted', 'user', 'edition', 'state', 'entry',)
         extra_kwargs = {
             'state': {
                 'label': 'State of the participation.'
diff --git a/projects/views.py b/projects/views.py
index 55f34ad68e262de51ec0d46d69f54026d69cd275..6d5d1815d5d499a3705a810ee533638126a1331a 100644
--- a/projects/views.py
+++ b/projects/views.py
@@ -46,8 +46,25 @@ class ProjectViewSet(viewsets.ReadOnlyModelViewSet):
             "id": 1,
             "url": "http://localhost:8000/api/projects/1/",
             "name": "Oser la Prépa",
-            "description": "Oser la Prépa est un stage d'acclimatation aux Classes Préparatoires de deux semaines qui se déroule chaque été.",
-            "logo": null
+            "description": "Oser la Prépa est un stage d'acclimatation…",
+            "logo": null,
+            "editions": [
+                {
+                    "id": 1,
+                    "url": "http://localhost:8000/api/editions/1/",
+                    "name": "",
+                    "year": 2018,
+                    "project": 1,
+                    "description": "",
+                    "organizers": 0,
+                    "participations": 2,
+                    "edition_form": {
+                        "id": 1,
+                        "edition": 1,
+                        "deadline": "2018-06-30"
+                    }
+                }
+            ]
         }
 
     """
@@ -82,16 +99,15 @@ class EditionViewSet(viewsets.ReadOnlyModelViewSet):
                 "url": "http://localhost:8000/api/editions/1/",
                 "name": "",
                 "year": 2018,
-                "project": {
-                    "id": 1,
-                    "url": "http://localhost:8000/api/projects/1/",
-                    "name": "Oser la Prépa",
-                    "description": "Oser la Prépa est un stage…",
-                    "logo": null
-                },
+                "project": 1,
                 "description": "",
                 "organizers": 0,
-                "participations": 1
+                "participations": 2,
+                "edition_form": {
+                    "id": 1,
+                    "edition": 1,
+                    "deadline": "2018-06-30"
+                }
             }
         ]
 
@@ -99,42 +115,103 @@ class EditionViewSet(viewsets.ReadOnlyModelViewSet):
 
     Retrieve a specific edition.
 
+    Each `participation` in `participations` has the following format:
+
+        {
+            "id": 3,
+            "submitted": "2018-06-07T00:31:37.947085+02:00",
+            "user": {
+                "id": 3,
+                "email": "john.doe@example.com",
+                "profile_type": null,
+                "first_name": "John",
+                "last_name": "Doe",
+                "gender": null,
+                "phone_number": "+33 6 12 34 56 78",
+                "date_of_birth": null,
+                "url": "http://localhost:8000/api/users/3/"
+            },
+            "edition": 1,
+            "state": "valid"
+        }
+
     ### Example response
 
-        [
-            {
+        {
+            "id": 1,
+            "url": "http://localhost:8000/api/editions/1/",
+            "name": "",
+            "year": 2018,
+            "project": 1,
+            "description": "",
+            "organizers": [],
+            "participations": [],
+            "edition_form": {
                 "id": 1,
-                "url": "http://localhost:8000/api/editions/1/",
-                "name": "",
-                "year": 2018,
-                "project": {
-                    "id": 1,
-                    "url": "http://localhost:8000/api/projects/1/",
-                    "name": "Oser la Prépa",
-                    "description": "Oser la Prépa est un stage…",
-                    "logo": null
+                "edition": 1,
+                "deadline": "2018-06-30",
+                "form": {
+                    "id": 2,
+                    "url": "http://localhost:8000/api/forms/2/",
+                    "slug": "inscriptions-a-oser-la-prepa-2018",
+                    "title": "Inscription à Oser la Prépa 2018",
+                    "entries_count": 1,
+                    "sections": [
+                        {
+                            "id": 1,
+                            "title": "Enfant",
+                            "form": 2,
+                            "questions": [
+                                {
+                                    "id": 14,
+                                    "type": "text-small",
+                                    "text": "Nom",
+                                    "help_text": "",
+                                    "required": true,
+                                    "section": 1
+                                },
+                            ]
+                        }
+                    ],
+                    "files": [
+                        {
+                            "id": 1,
+                            "name": "Autorisation parentale",
+                            "file": "http://localhost:8000/file.pdf",
+                            "form": 2
+                        }
+                    ]
                 },
-                "description": "",
-                "organizers": [ ],
-                "participations": [
-                {
-                    "id": 3,
-                    "submitted": "2018-06-07T00:31:37.947085+02:00",
+                "recipient": {
                     "user": {
-                    "id": 3,
-                    "email": "john.doe@example.com",
-                    "profile_type": null,
-                    "first_name": "John",
-                    "last_name": "Doe",
-                    "gender": null,
-                    "phone_number": "+33 6 12 34 56 78",
-                    "date_of_birth": null,
-                    "url": "http://localhost:8000/api/users/3/"
-                },
-                "edition": 1,
-                "state": "valid"
-            },
-        ]
+                        "id": 3,
+                        "email": "john.doe@example.com",
+                        "profile_type": null,
+                        "first_name": "John",
+                        "last_name": "Doe",
+                        "gender": null,
+                        "phone_number": "+33 6 12 34 56 78",
+                        "date_of_birth": null,
+                        "url": "http://localhost:8000/api/users/3/"
+                    },
+                    "address": {
+                        "line1": "Rue de Rivoli",
+                        "line2": "",
+                        "post_code": "75001",
+                        "city": "Paris",
+                        "country": {
+                            "code": "FR",
+                            "name": "France"
+                        }
+                    },
+                    "promotion": 2020,
+                    "tutoring_groups": [
+                        1
+                    ],
+                    "url": "http://localhost:8000/api/tutors/1/"
+                }
+            }
+        }
     """
 
     queryset = Edition.objects.all()