diff --git a/partners/admin.py b/partners/admin.py
index 2b1ddd057b5450a624f52bfba070d5de1bc36f56..be1335e3096e5e454dea024ebc614285053cd5b6 100644
--- a/partners/admin.py
+++ b/partners/admin.py
@@ -1,15 +1,14 @@
 from django.contrib import admin
 
-from .models import ContactPerson, Partner, Consortium,\
+from .models import Contact, Partner, Consortium,\
     ProspectivePartner, MembershipAgreement
 
 
-admin.site.register(ContactPerson)
+admin.site.register(Contact)
 
 
 class PartnerAdmin(admin.ModelAdmin):
-    search_fields = ['institution', 'institution_acronym',
-                     'institution_address', 'contact_person']
+    search_fields = ['institution', ]
 
 
 admin.site.register(Partner, PartnerAdmin)
diff --git a/partners/constants.py b/partners/constants.py
index ec05c2315596b05013b825e5c27531e75aaca543..104cbbf0f5140ad66f9768b3be6ff7ee268db81f 100644
--- a/partners/constants.py
+++ b/partners/constants.py
@@ -1,19 +1,37 @@
 import datetime
 
 
-PARTNER_TYPES = (
+PARTNER_KINDS = (
+    ('Res. Inst.', 'Research Institute'),
     ('Int. Fund. Agency', 'International Funding Agency'),
     ('Nat. Fund. Agency', 'National Funding Agency'),
+    ('Nat. Lab.', 'National Laboratory'),
     ('Nat. Library', 'National Library'),
-    ('Univ. Library', 'University Library'),
+    ('Nat. Acad.', 'National Academy'),
+    ('Univ. Library', 'University (and its Library)'),
     ('Res. Library', 'Research Library'),
+    ('Prof. Soc.', 'Professional Society'),
     ('Foundation', 'Foundation'),
     ('Individual', 'Individual'),
 )
 
+
+PROSPECTIVE_PARTNER_STATUS = (
+    ('requested', 'Requested (from online form)'),
+    ('added', 'Added internally'),
+    ('processed', 'Processed into Partner object'),
+)
+
+PROSPECTIVE_PARTNER_EVENTS = (
+    ('comment', 'Comment added'),
+)
+
+
 PARTNER_STATUS = (
-    ('Prospective', 'Prospective'),
+    ('Initiated', 'Initiated'),
+    ('Contacted', 'Contacted'),
     ('Negotiating', 'Negotiating'),
+    ('Uninterested', 'Uninterested'),
     ('Active', 'Active'),
     ('Inactive', 'Inactive'),
 )
@@ -26,6 +44,13 @@ CONSORTIUM_STATUS = (
 )
 
 
+PARTNER_EVENTS = (
+    ('initial', 'Contacted (initial)'),
+    ('status_update', 'Status updated'),
+    ('comment', 'Comment added'),
+)
+
+
 MEMBERSHIP_AGREEMENT_STATUS = (
     ('Submitted', 'Request submitted by Partner'),
     ('Pending', 'Sent to Partner, response pending'),
diff --git a/partners/forms.py b/partners/forms.py
index 3aa5ed7fe1168577f6b676b20755236600e105d2..e259096256309c3169843a05db69c94a3026e4cc 100644
--- a/partners/forms.py
+++ b/partners/forms.py
@@ -5,8 +5,9 @@ from django_countries import countries
 from django_countries.widgets import CountrySelectWidget
 from django_countries.fields import LazyTypedChoiceField
 
-from .constants import PARTNER_TYPES
-from .models import ContactPerson, Partner, ProspectivePartner, MembershipAgreement
+from .constants import PARTNER_KINDS
+from .models import Partner, ProspectivePartner, ProspectiveContact, \
+    ProspectivePartnerEvent, MembershipAgreement
 
 from scipost.models import TITLE_CHOICES
 
@@ -22,9 +23,31 @@ class PartnerForm(forms.ModelForm):
 
 
 class ProspectivePartnerForm(forms.ModelForm):
+    """
+    This form is used to internally add a ProspectivePartner.
+    If an external agent requests membership of the SPB,
+    the MembershipQueryForm below is used instead.
+    """
     class Meta:
         model = ProspectivePartner
-        exclude = ['date_received', 'date_processed', 'processed']
+        exclude = ['date_received', 'date_processed']
+        widgets = {'status': forms.HiddenInput()}
+
+
+class ProspectiveContactForm(forms.ModelForm):
+    class Meta:
+        model = ProspectiveContact
+        fields = '__all__'
+        widgets = {'prospartner': forms.HiddenInput()}
+
+
+class ProspectivePartnerEventForm(forms.ModelForm):
+    class Meta:
+        model = ProspectivePartnerEvent
+        exclude = ['prospartner', 'noted_on', 'noted_by']
+        widgets = {
+            'comments': forms.Textarea(attrs={'cols': 16, 'rows': 3}),
+        }
 
 
 class MembershipQueryForm(forms.Form):
@@ -37,7 +60,7 @@ class MembershipQueryForm(forms.Form):
     last_name = forms.CharField(label='* Your last name', max_length=100)
     email = forms.EmailField(label='* Your email address')
     role = forms.CharField(label='* Your role in your organization')
-    partner_type = forms.ChoiceField(choices=PARTNER_TYPES, label='* Partner type')
+    partner_kind = forms.ChoiceField(choices=PARTNER_KINDS, label='* Partner kind')
     institution_name = forms.CharField(label='* Name of your institution')
     country = LazyTypedChoiceField(
         choices=countries, label='* Country', initial='NL',
diff --git a/partners/migrations/0005_auto_20170603_1646.py b/partners/migrations/0005_auto_20170603_1646.py
new file mode 100644
index 0000000000000000000000000000000000000000..323c33155366c09fa911175f93585202187f00f9
--- /dev/null
+++ b/partners/migrations/0005_auto_20170603_1646.py
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-06-03 14:46
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import django_countries.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('scipost', '0055_auto_20170519_0937'),
+        ('partners', '0004_auto_20170519_1425'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Contact',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('kind', models.CharField(max_length=128)),
+                ('title', models.CharField(choices=[('PR', 'Prof.'), ('DR', 'Dr'), ('MR', 'Mr'), ('MRS', 'Mrs')], max_length=4)),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Institution',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('kind', models.CharField(choices=[('Res. Inst.', 'Research Institute'), ('Int. Fund. Agency', 'International Funding Agency'), ('Nat. Fund. Agency', 'National Funding Agency'), ('Nat. Lab.', 'National Laboratory'), ('Nat. Library', 'National Library'), ('Nat. Acad.', 'National Academy'), ('Univ. Library', 'University (and its Library)'), ('Res. Library', 'Research Library'), ('Prof. Soc.', 'Professional Society'), ('Foundation', 'Foundation'), ('Individual', 'Individual')], max_length=32)),
+                ('name', models.CharField(max_length=256)),
+                ('acronym', models.CharField(max_length=16)),
+                ('address', models.CharField(blank=True, max_length=1000, null=True)),
+                ('country', django_countries.fields.CountryField(max_length=2)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='PartnerEvent',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('event', models.CharField(choices=[('initial', 'Contacted (initial)'), ('status_update', 'Status updated'), ('comment', 'Comment added')], max_length=64)),
+                ('comments', models.TextField(blank=True, null=True)),
+                ('noted_on', models.DateTimeField(default=django.utils.timezone.now)),
+                ('noted_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ProspectiveContact',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(choices=[('PR', 'Prof.'), ('DR', 'Dr'), ('MR', 'Mr'), ('MRS', 'Mrs')], max_length=4)),
+                ('first_name', models.CharField(max_length=64)),
+                ('last_name', models.CharField(max_length=64)),
+                ('email', models.EmailField(max_length=254)),
+                ('role', models.CharField(max_length=128)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ProspectivePartnerEvent',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('event', models.CharField(choices=[('comment', 'Comment added')], max_length=64)),
+                ('comments', models.TextField(blank=True, null=True)),
+                ('noted_on', models.DateTimeField(default=django.utils.timezone.now)),
+                ('duration', models.DurationField(blank=True, null=True)),
+                ('noted_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scipost.Contributor')),
+            ],
+        ),
+        migrations.RemoveField(
+            model_name='contactperson',
+            name='user',
+        ),
+        migrations.RemoveField(
+            model_name='partner',
+            name='country',
+        ),
+        migrations.RemoveField(
+            model_name='partner',
+            name='institution_acronym',
+        ),
+        migrations.RemoveField(
+            model_name='partner',
+            name='institution_address',
+        ),
+        migrations.RemoveField(
+            model_name='partner',
+            name='institution_name',
+        ),
+        migrations.RemoveField(
+            model_name='partner',
+            name='partner_type',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartner',
+            name='email',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartner',
+            name='first_name',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartner',
+            name='last_name',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartner',
+            name='partner_type',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartner',
+            name='role',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartner',
+            name='title',
+        ),
+        migrations.AddField(
+            model_name='prospectivepartner',
+            name='kind',
+            field=models.CharField(choices=[('Res. Inst.', 'Research Institute'), ('Int. Fund. Agency', 'International Funding Agency'), ('Nat. Fund. Agency', 'National Funding Agency'), ('Nat. Lab.', 'National Laboratory'), ('Nat. Library', 'National Library'), ('Nat. Acad.', 'National Academy'), ('Univ. Library', 'University (and its Library)'), ('Res. Library', 'Research Library'), ('Prof. Soc.', 'Professional Society'), ('Foundation', 'Foundation'), ('Individual', 'Individual')], default='Univ. Library', max_length=32),
+        ),
+        migrations.AlterField(
+            model_name='partner',
+            name='financial_contact',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='partner_financial_contact', to='partners.Contact'),
+        ),
+        migrations.AlterField(
+            model_name='partner',
+            name='main_contact',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='partner_main_contact', to='partners.Contact'),
+        ),
+        migrations.AlterField(
+            model_name='partner',
+            name='technical_contact',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='partner_technical_contact', to='partners.Contact'),
+        ),
+        migrations.DeleteModel(
+            name='ContactPerson',
+        ),
+        migrations.AddField(
+            model_name='prospectivepartnerevent',
+            name='prospective_partner',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='partners.ProspectivePartner'),
+        ),
+        migrations.AddField(
+            model_name='prospectivecontact',
+            name='institution',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='partners.ProspectivePartner'),
+        ),
+        migrations.AddField(
+            model_name='partnerevent',
+            name='partner',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='partners.Partner'),
+        ),
+        migrations.AddField(
+            model_name='partner',
+            name='institution',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='partners.Institution'),
+        ),
+    ]
diff --git a/partners/migrations/0006_auto_20170604_0459.py b/partners/migrations/0006_auto_20170604_0459.py
new file mode 100644
index 0000000000000000000000000000000000000000..41f7126405b205015e8ad02b0d043bfe24c4ea14
--- /dev/null
+++ b/partners/migrations/0006_auto_20170604_0459.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-06-04 02:59
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('partners', '0005_auto_20170603_1646'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='prospectivecontact',
+            old_name='institution',
+            new_name='prospartner',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartner',
+            name='processed',
+        ),
+        migrations.AddField(
+            model_name='prospectivepartner',
+            name='status',
+            field=models.CharField(choices=[('requested', 'Requested (from online form)'), ('added', 'Added internally'), ('processed', 'Processed into Partner object')], default='added', max_length=32),
+        ),
+        migrations.AlterField(
+            model_name='partner',
+            name='status',
+            field=models.CharField(choices=[('Initiated', 'Initiated'), ('Contacted', 'Contacted'), ('Negotiating', 'Negotiating'), ('Uninterested', 'Uninterested'), ('Active', 'Active'), ('Inactive', 'Inactive')], max_length=16),
+        ),
+    ]
diff --git a/partners/migrations/0007_auto_20170604_0629.py b/partners/migrations/0007_auto_20170604_0629.py
new file mode 100644
index 0000000000000000000000000000000000000000..85e41ea59b2757ae781413cbef9d859840286fb6
--- /dev/null
+++ b/partners/migrations/0007_auto_20170604_0629.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.3 on 2017-06-04 04:29
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('partners', '0006_auto_20170604_0459'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='prospectivepartnerevent',
+            old_name='prospective_partner',
+            new_name='prospartner',
+        ),
+        migrations.RemoveField(
+            model_name='prospectivepartnerevent',
+            name='duration',
+        ),
+    ]
diff --git a/partners/models.py b/partners/models.py
index 82450ab9946f58536bb1892d64b2d97b3a296401..b773bcc1d32ce7f84a1f072ea08ff6ed9e8e10c2 100644
--- a/partners/models.py
+++ b/partners/models.py
@@ -4,20 +4,89 @@ from django.utils import timezone
 
 from django_countries.fields import CountryField
 
-from .constants import PARTNER_TYPES, PARTNER_STATUS, CONSORTIUM_STATUS,\
+from .constants import PARTNER_KINDS, PARTNER_STATUS, CONSORTIUM_STATUS,\
+    PROSPECTIVE_PARTNER_STATUS, PROSPECTIVE_PARTNER_EVENTS, PARTNER_EVENTS,\
     MEMBERSHIP_AGREEMENT_STATUS, MEMBERSHIP_DURATION
 
 from scipost.constants import TITLE_CHOICES
+from scipost.models import Contributor
 
 
-class ContactPerson(models.Model):
+########################
+# Prospective Partners #
+########################
+
+class ProspectivePartner(models.Model):
+    """
+    Created from the membership_request page, after submitting a query form.
+    """
+    kind = models.CharField(max_length=32, choices=PARTNER_KINDS,
+                            default='Univ. Library')
+    institution_name = models.CharField(max_length=256)
+    country = CountryField()
+    date_received = models.DateTimeField(default=timezone.now)
+    date_processed = models.DateTimeField(blank=True, null=True)
+    status = models.CharField(max_length=32, choices=PROSPECTIVE_PARTNER_STATUS,
+                              default='added')
+
+    def __str__(self):
+        return '%s (received %s), %s' % (self.institution_name,
+                                         self.date_received.strftime("%Y-%m-%d"),
+                                         self.get_status_display())
+
+class ProspectiveContact(models.Model):
+    """
+    A ProspectiveContact is a person's name and contact details, with a
+    link to a Prospective Partner and a role within it.
+    It does not have a corresponding User object.
+    It is meant to be used internally at SciPost, during Partner mining.
+    """
+    prospartner = models.ForeignKey(ProspectivePartner, on_delete=models.CASCADE)
+    title = models.CharField(max_length=4, choices=TITLE_CHOICES)
+    first_name = models.CharField(max_length=64)
+    last_name = models.CharField(max_length=64)
+    email = models.EmailField()
+    role = models.CharField(max_length=128)
+
+
+class ProspectivePartnerEvent(models.Model):
+    prospartner = models.ForeignKey(ProspectivePartner, on_delete=models.CASCADE)
+    event = models.CharField(max_length=64, choices=PROSPECTIVE_PARTNER_EVENTS)
+    comments = models.TextField(blank=True, null=True)
+    noted_on = models.DateTimeField(default=timezone.now)
+    noted_by = models.ForeignKey(Contributor, on_delete=models.CASCADE)
+
+    def __str__(self):
+        return '%s: %s' % (str(self.prospective_partner), self.get_event_display())
+
+
+###########################
+# Partner-related objects #
+###########################
+
+class Institution(models.Model):
+    """
+    An Institution is any form of academic organization which SciPost interacts with.
+    """
+    kind = models.CharField(max_length=32, choices=PARTNER_KINDS)
+    name = models.CharField(max_length=256)
+    acronym = models.CharField(max_length=16)
+    address = models.CharField(max_length=1000, blank=True, null=True)
+    country = CountryField()
+
+    def __str__(self):
+        return '%s (%s)' % (self.name, self.get_kind_display())
+
+
+class Contact(models.Model):
     """
-    A ContactPerson is a simple form of User which is meant
+    A Contact is a simple form of User which is meant
     to be associated to Partner objects
     (main contact, financial/technical contact etc).
-    ContactPersons and Contributors have different rights.
+    Contacts and Contributors have different rights.
     """
     user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True)
+    kind = models.CharField(max_length=128)
     title = models.CharField(max_length=4, choices=TITLE_CHOICES)
 
     def __str__(self):
@@ -29,24 +98,34 @@ class Partner(models.Model):
     Supporting Partners.
     These are the official Partner objects created by SciPost Admin.
     """
-    partner_type = models.CharField(max_length=32, choices=PARTNER_TYPES)
+    institution = models.ForeignKey(Institution, on_delete=models.CASCADE,
+                                    blank=True, null=True)
     status = models.CharField(max_length=16, choices=PARTNER_STATUS)
-    institution_name = models.CharField(max_length=256)
-    institution_acronym = models.CharField(max_length=10)
-    institution_address = models.CharField(max_length=1000, blank=True, null=True)
-    country = CountryField()
-    main_contact = models.ForeignKey(ContactPerson, on_delete=models.CASCADE,
+    main_contact = models.ForeignKey(Contact, on_delete=models.CASCADE,
                                      blank=True, null=True,
                                      related_name='partner_main_contact')
-    financial_contact = models.ForeignKey(ContactPerson, on_delete=models.CASCADE,
+    financial_contact = models.ForeignKey(Contact, on_delete=models.CASCADE,
                                           blank=True, null=True,
                                           related_name='partner_financial_contact')
-    technical_contact = models.ForeignKey(ContactPerson, on_delete=models.CASCADE,
+    technical_contact = models.ForeignKey(Contact, on_delete=models.CASCADE,
                                           blank=True, null=True,
                                           related_name='partner_technical_contact')
 
     def __str__(self):
-        return self.institution_acronym + ' (' + self.get_status_display() + ')'
+        if self.institution:
+            return self.institution.acronym + ' (' + self.get_status_display() + ')'
+        return self.get_status_display()
+
+
+class PartnerEvent(models.Model):
+    partner = models.ForeignKey(Partner, on_delete=models.CASCADE)
+    event = models.CharField(max_length=64, choices=PARTNER_EVENTS)
+    comments = models.TextField(blank=True, null=True)
+    noted_on = models.DateTimeField(default=timezone.now)
+    noted_by = models.ForeignKey(Contributor, on_delete=models.CASCADE)
+
+    def __str__(self):
+        return '%s: %s' % (str(self.partner), self.get_event_display())
 
 
 class Consortium(models.Model):
@@ -61,31 +140,6 @@ class Consortium(models.Model):
         verbose_name_plural = 'consortia'
 
 
-class ProspectivePartner(models.Model):
-    """
-    Created from the membership_request page, after submitting a query form.
-    """
-    title = models.CharField(max_length=4, choices=TITLE_CHOICES)
-    first_name = models.CharField(max_length=32)
-    last_name = models.CharField(max_length=32)
-    email = models.EmailField()
-    role = models.CharField(max_length=128)
-    partner_type = models.CharField(max_length=32, choices=PARTNER_TYPES)
-    institution_name = models.CharField(max_length=256)
-    country = CountryField()
-    date_received = models.DateTimeField(default=timezone.now)
-    date_processed = models.DateTimeField(blank=True, null=True)
-    processed = models.BooleanField(default=False)
-
-    def __str__(self):
-        resp = "processed"
-        if not self.processed:
-            resp = "unprocessed"
-        return '%s (received %s), %s' % (self.institution_name,
-                                         self.date_received.strftime("%Y-%m-%d"),
-                                         resp)
-
-
 class MembershipAgreement(models.Model):
     """
     Agreement for membership of the Supporting Partners Board.
diff --git a/partners/templates/partners/_partner_card.html b/partners/templates/partners/_partner_card.html
index 31175dccfad720d674489d66f3165abe4803c9af..0a5e7df5986d015e49134ef685943f99ef01ffee 100644
--- a/partners/templates/partners/_partner_card.html
+++ b/partners/templates/partners/_partner_card.html
@@ -3,12 +3,12 @@
 <div class="card-block">
   <div class="row">
     <div class="col-1">
-      <p>{{ partner.country }}</p>
+      <p>{{ partner.institution.country }}</p>
     </div>
     <div class="col-4">
-      <h3>{{ partner.institution_name }}</h3>
-      <p>{{ partner.institution_acronym }}</p>
-      <p>({{ pp.get_partner_type_display }})</p>
+      <h3>{{ partner.institution.name }}</h3>
+      <p>{{ partner.institution.acronym }}</p>
+      <p>({{ pp.get_kind_display }})</p>
     </div>
     <div class="col-4">
       {% if partner.main_contact %}
diff --git a/partners/templates/partners/_prospartner_event_li.html b/partners/templates/partners/_prospartner_event_li.html
new file mode 100644
index 0000000000000000000000000000000000000000..2200a8cb9c40d57cd047e4f9e4a28667ba9826a4
--- /dev/null
+++ b/partners/templates/partners/_prospartner_event_li.html
@@ -0,0 +1,7 @@
+<li id="{{ event.id }}">
+  <div class="font-weight-bold">{{ event.get_event_display }} <small class="text-muted">noted {{ event.noted_on }} by {{ event.noted_by }}</small>
+  </div>
+  {% if event.comments %}
+  <div>{{ event.comments|linebreaks }}</div>
+  {% endif %}
+</li>
diff --git a/partners/templates/partners/_prospective_partner_card.html b/partners/templates/partners/_prospective_partner_card.html
index fe2624c247b05a878622743b0c213a80467220cc..d83086eade3e35562cb1519bdffe27786507f25d 100644
--- a/partners/templates/partners/_prospective_partner_card.html
+++ b/partners/templates/partners/_prospective_partner_card.html
@@ -7,16 +7,53 @@
     </div>
     <div class="col-4">
       <h3>{{ pp.institution_name }}</h3>
-      <p>({{ pp.get_partner_type_display }})</p>
+      <p>({{ pp.get_kind_display }})</p>
       <p>Received {{ pp.date_received }}</p>
+      {% if pp.date_processed %}
+      <p>Processed {{ pp.date_processed }}</p>
+      {% endif %}
+      <p>{{ pp.get_status_display }}</p>
     </div>
     <div class="col-4">
-      <p>Contact: {{ pp.get_title_display }} {{ pp.first_name }} {{ pp.last_name }}</p>
-      <p>(role: {{ pp.role }})</p>
-      <p>{{ pp.email }}</p>
+      <h3>Contacts:</h3>
+      <ul class="list-group list-group-flush">
+	{% for contact in pp.prospectivecontact_set.all %}
+	<li class="list-group-item flex-column align-items-start">
+	  <p>Role: {{ contact.role }}</p>
+	  <p>{{ contact.get_title_display }} {{ contact.first_name }} {{ contact.last_name }}</p>
+	  <p>{{ contact.email }}</p>
+	</li>
+	{% endfor %}
+      </ul>
     </div>
     <div class="col-3">
-      <p>Edit</p>
+      <ul class="list-group list-group-flush">
+	<li class="list-group-item">
+	  <a href="{% url 'partners:add_prospartner_contact' prospartner_id=pp.id %}">Add a contact</a>
+	</li>
+      </ul>
+    </div>
+  </div>
+
+  <div class="row">
+    <div class="col-1"></div>
+    <div class="col-6">
+      <h3>Events</h3>
+      <ul>
+	{% for event in pp.prospectivepartnerevent_set.all %}
+	{% include 'partners/_prospartner_event_li.html' with event=event %}
+	{% empty %}
+	<li>No events were found.</li>
+	{% endfor %}
+      </ul>
+    </div>
+    <div class="col-5">
+      <h3>Add an event</h3>
+      <form action="{% url 'partners:add_prospartner_event' prospartner_id=pp.id %}" method="post">
+	{% csrf_token %}
+	{{ ppevent_form|bootstrap }}
+	<input type="submit" name="submit" value="Submit">
+      </form>
     </div>
   </div>
 </div>
diff --git a/partners/templates/partners/add_prospartner_contact.html b/partners/templates/partners/add_prospartner_contact.html
new file mode 100644
index 0000000000000000000000000000000000000000..bad159fd663a70e20ec51b63d1e81bf87c5ef8bf
--- /dev/null
+++ b/partners/templates/partners/add_prospartner_contact.html
@@ -0,0 +1,30 @@
+{% extends 'scipost/base.html' %}
+
+{% block pagetitle %}: Supporting Partners: add contact{% endblock pagetitle %}
+
+{% load bootstrap %}
+
+{% block content %}
+
+<section>
+  <div class="flex-container">
+    <div class="flex-greybox">
+      <h1>Add a Contact for a Prospective Partner</h1>
+    </div>
+  </div>
+
+  <h3>Add a contact for {{ prospartner.institution_name }}:</h3>
+  <br/>
+  <form action="{% url 'partners:add_prospartner_contact' prospartner_id=prospartner.id %}" method="post">
+    {% csrf_token %}
+    {{ form|bootstrap }}
+    <input class="btn btn-primary" type="submit" value="Submit"/>
+  </form>
+
+  {% if errormessage %}
+  <p class="text-danger">{{ errormessage }}</p>
+  {% endif %}
+
+</section>
+
+{% endblock content %}
diff --git a/partners/templates/partners/manage_partners.html b/partners/templates/partners/manage_partners.html
index 95fb172639561b9c03f3d03c7a97711b0ccdf18a..9cfa3e406c2e6a2d2c314e06155cfc8773327e34 100644
--- a/partners/templates/partners/manage_partners.html
+++ b/partners/templates/partners/manage_partners.html
@@ -5,51 +5,100 @@
 
 {% block content %}
 
-<div class="flex-container">
-  <div class="flex-greybox">
-    <h1>Partners Management Page</h1>
+  <div class="row">
+    <div class="col-12">
+      <h1 class="highlight">Partners Management Page</h1>
+    </div>
   </div>
-</div>
 
-<section>
-  <div class="flex-container">
-    <div class="flex-greybox">
-      <h2>Partners</h2>
+
+  <div class="row">
+    <div class="col-12">
+      <div class="tab-nav-container">
+	<div class="tab-nav-inner">
+	  <ul class="nav btn-group personal-page-nav" role="tablist">
+	    <li class="nav-item btn btn-secondary">
+	      <a href="#prospartners" class="nav-link active" data-toggle="tab">Prospective Partners</a>
+	    </li>
+	    <li class="nav-item btn btn-secondary">
+	      <a href="#partners" class="nav-link" data-toggle="tab">Partners</a>
+	    </li>
+	    <li class="nav-item btn btn-secondary">
+	      <a href="#agreements" class="nav-link" data-toggle="tab">Agreements</a>
+	    </li>
+	  </ul>
+	</div>
+      </div>
     </div>
   </div>
-  <ul class="list-group list-group-flush">
-    {% for partner in partners %}
-    <li class="list-group-item">{% include 'partners/_partner_card.html' with partner=partner %}</li>
-    {% endfor %}
-  </ul>
-</section>
-
-<section>
-  <div class="flex-container">
-    <div class="flex-greybox">
-      <h2>Prospective Partners (not yet processed)</h2>
+
+  <div class="tab-content">
+    <div class="tab-pane active" id="prospartners" role="tabpanel">
+      <div class="row">
+	<div class="col-12">
+	  <h2 class="highlight">Prospective Partners</h2>
+	</div>
+      </div>
+      <h3><a href="{% url 'partners:add_prospective_partner' %}">Add a prospective partner</a></h3>
+      <br/>
+
+      <div class="row">
+	<div class="col-2">Country</div>
+	<div class="col-3">Institution name</div>
+	<div class="col-3">Kind</div>
+	<div class="col-3">Status</div>
+	<div class="col-1">Date received</div>
+      </div>
+
+      <div id="accordion" role="tablist" aria-multiselectable="true">
+	{% for partner in prospective_partners %}
+	<div class="card">
+	  <div class="card-header" role="tab" id="heading{{ partner.id }}">
+	    <h4 class="mb-0">
+	      <a data-toggle="collapse" data-parent="#accordion" href="#collapse{{ partner.id }}" aria-expanded="true" aria-controls="collapse{{ partner.id }}">
+		<div class="row">
+		  <div class="col-2">{{ partner.get_country_display }}</div>
+		  <div class="col-3">{{ partner.institution_name }}</div>
+		  <div class="col-3">{{ partner.get_kind_display }}</div>
+		  <div class="col-3">{{ partner.get_status_display }}</div>
+		  <div class="col-1">{{ partner.date_received|date:"Y-m-d" }}</div>
+		</div>
+	      </a>
+	    </h4>
+	  </div>
+	  <div id="collapse{{ partner.id }}" class="collapse" role="tabpanel" aria-labelledby="heading{{ partner.id}}">
+	    {% include 'partners/_prospective_partner_card.html' with pp=partner %}
+	  </div>
+	</div>
+	{% endfor %}
+      </div>
     </div>
-  </div>
-  <h3><a href="{% url 'partners:add_prospective_partner' %}">Add a prospective partner</a></h3>
-  <br/>
-  <ul class="list-group list-group-flush">
-    {% for partner in prospective_partners %}
-    <li class="list-group-item">{% include 'partners/_prospective_partner_card.html' with pp=partner %}</li>
-    {% endfor %}
-  </ul>
-</section>
-
-<section>
-  <div class="flex-container">
-    <div class="flex-greybox">
-      <h2>Agreements</h2>
+
+    <div class="tab-pane" id="partners" role="tabpanel">
+      <div class="row">
+	<div class="col-12">
+	  <h2 class="highlight">Partners</h2>
+	</div>
+      </div>
+      <ul class="list-group list-group-flush">
+	{% for partner in partners %}
+	<li class="list-group-item">{% include 'partners/_partner_card.html' with partner=partner %}</li>
+	{% endfor %}
+      </ul>
+    </div>
+
+    <div class="tab-pane" id="agreements" role="tabpanel">
+      <div class="row">
+	<div class="col-12">
+	  <h2 class="highlight">Agreements</h2>
+	</div>
+      </div>
+      <ul>
+	{% for agreement in agreements %}
+	<li>{{ agreement }}</li>
+	{% endfor %}
+      </ul>
     </div>
   </div>
-  <ul>
-    {% for agreement in agreements %}
-    <li>{{ agreement }}</li>
-    {% endfor %}
-  </ul>
-</section>
 
 {% endblock content %}
diff --git a/partners/urls.py b/partners/urls.py
index 5558aaaed1659597f7511d16fc50b80acc947868..f49cd75db4fc302d5cbd9b89ccb458fe3b6d99da 100644
--- a/partners/urls.py
+++ b/partners/urls.py
@@ -11,4 +11,10 @@ urlpatterns = [
     url(r'^manage$', views.manage, name='manage'),
     url(r'^add_prospective_partner$', views.add_prospective_partner,
         name='add_prospective_partner'),
+    url(r'^add_prospective_contact/(?P<prospartner_id>[0-9]+)$',
+        views.add_prospartner_contact,
+        name='add_prospartner_contact'),
+    url(r'^add_prospartner_event/(?P<prospartner_id>[0-9]+)$',
+        views.add_prospartner_event,
+        name='add_prospartner_event'),
 ]
diff --git a/partners/views.py b/partners/views.py
index 7473a5352a699f7b2c940ad97dcffaab3554fd68..19c9b7e9cd91383d3a29fa1ff102d37fbc7538c0 100644
--- a/partners/views.py
+++ b/partners/views.py
@@ -1,11 +1,14 @@
 from django.contrib import messages
-from django.shortcuts import render, reverse, redirect
+from django.db import transaction
+from django.shortcuts import get_object_or_404, render, reverse, redirect
 from django.utils import timezone
 
 from guardian.decorators import permission_required
 
-from .models import Partner, ProspectivePartner, MembershipAgreement
-from .forms import ProspectivePartnerForm, MembershipQueryForm
+from .models import Partner, ProspectivePartner, ProspectiveContact, \
+    ProspectivePartnerEvent, MembershipAgreement
+from .forms import ProspectivePartnerForm, ProspectiveContactForm, \
+    ProspectivePartnerEventForm, MembershipQueryForm
 
 
 def supporting_partners(request):
@@ -15,20 +18,26 @@ def supporting_partners(request):
     return render(request, 'partners/supporting_partners.html', context)
 
 
+@transaction.atomic
 def membership_request(request):
     query_form = MembershipQueryForm(request.POST or None)
     if query_form.is_valid():
-        query = ProspectivePartner(
+        prospartner = ProspectivePartner(
+            kind=query_form.cleaned_data['partner_kind'],
+            institution_name=query_form.cleaned_data['institution_name'],
+            country=query_form.cleaned_data['country'],
+            date_received=timezone.now(),
+            status = 'requested',
+        )
+        prospartner.save()
+        contact = ProspectiveContact(
+            prospartner=prospartner,
             title=query_form.cleaned_data['title'],
             first_name=query_form.cleaned_data['first_name'],
             last_name=query_form.cleaned_data['last_name'],
             email=query_form.cleaned_data['email'],
-            partner_type=query_form.cleaned_data['partner_type'],
-            institution_name=query_form.cleaned_data['institution_hame'],
-            country=query_form.cleaned_data['country'],
-            date_received=timezone.now(),
         )
-        query.save()
+        contact.save()
         ack_message = ('Thank you for your SPB Membership query. '
                        'We will get back to you in the very near future '
                        'with further details.')
@@ -43,22 +52,67 @@ def manage(request):
     """
     Lists relevant info regarding management of Supporting Partners Board.
     """
-    partners = Partner.objects.all().order_by('country', 'institution_name')
-    prospective_partners = ProspectivePartner.objects.filter(
-        processed=False).order_by('date_received')
+    partners = Partner.objects.all()
+    prospective_partners = ProspectivePartner.objects.all(
+    ).order_by('country', 'institution_name')
+    ppevent_form = ProspectivePartnerEventForm()
     agreements = MembershipAgreement.objects.all().order_by('date_requested')
     context = {'partners': partners,
                'prospective_partners': prospective_partners,
+               'ppevent_form': ppevent_form,
                'agreements': agreements, }
     return render(request, 'partners/manage_partners.html', context)
 
 
 @permission_required('scipost.can_manage_SPB', return_403=True)
+@transaction.atomic
 def add_prospective_partner(request):
-    form = ProspectivePartnerForm(request.POST or None)
+    form = ProspectivePartnerForm(request.POST or None,
+                                  initial={'status': 'added',
+                                           'kind': 'Univ. Library'})
     if form.is_valid():
-        form.save()
-        messages.success(request, 'Prospective Partners successfully added')
-        return redirect(reverse('partners:manage'))
+        pp = form.save()
+        messages.success(request, 'Prospective Partner successfully added')
+#        return redirect(reverse('partners:manage'))
+        return redirect(reverse('partners:add_prospartner_contact',
+                                kwargs={'prospartner_id': pp.id}))
     context = {'form': form}
     return render(request, 'partners/add_prospective_partner.html', context)
+
+
+@permission_required('scipost.can_manage_SPB', return_403=True)
+@transaction.atomic
+def add_prospartner_contact(request, prospartner_id):
+    prospartner = get_object_or_404(ProspectivePartner, pk=prospartner_id)
+    form = ProspectiveContactForm(
+        request.POST or None, initial={'prospartner': prospartner })
+    if form.is_valid():
+        form.save()
+        messages.success(request, 'Contact successfully added to Prospective Partner')
+        return redirect(reverse('partners:manage'))
+    context = {'form': form,
+               'prospartner': prospartner }
+    return render(request, 'partners/add_prospartner_contact.html', context)
+
+
+@permission_required('scipost.can_manage_SPB', return_403=True)
+@transaction.atomic
+def add_prospartner_event(request, prospartner_id):
+    prospartner = get_object_or_404(ProspectivePartner, pk=prospartner_id)
+    if request.method == 'POST':
+        ppevent_form = ProspectivePartnerEventForm(request.POST)
+        if ppevent_form.is_valid():
+            ppevent = ProspectivePartnerEvent(
+                prospartner=prospartner,
+                event=ppevent_form.cleaned_data['event'],
+                comments=ppevent_form.cleaned_data['comments'],
+                noted_on=timezone.now(),
+                noted_by=request.user.contributor,)
+            ppevent.save()
+            return redirect(reverse('partners:manage'))
+        else:
+            errormessage = 'The form was invalidly filled.'
+            return render(request, 'scipost/error.html', {'errormessage': errormessage})
+    else:
+        errormessage = 'This view can only be posted to.'
+        return render(request, 'scipost/error.html', {'errormessage': errormessage})