diff --git a/greg/api/filters.py b/greg/api/filters.py
index 307fe3202146880b9c943e485356ac6e95558476..14ef355eb0bc7935dc090f5cf189055e2df78765 100644
--- a/greg/api/filters.py
+++ b/greg/api/filters.py
@@ -1,6 +1,6 @@
 from django_filters import rest_framework as filters
 
-from greg.models import Person, PersonRole
+from greg.models import Person, PersonRole, PersonIdentity
 
 
 class PersonRoleFilter(filters.FilterSet):
@@ -19,3 +19,9 @@ class PersonFilter(filters.FilterSet):
     class Meta:
         model = Person
         fields = ["first_name", "last_name", "verified"]
+
+
+class PersonIdentityFilter(filters.FilterSet):
+    class Meta:
+        model = PersonIdentity
+        fields = ["type", "verified_by_id"]
diff --git a/greg/api/serializers/person.py b/greg/api/serializers/person.py
index 77eec13afec9c235552e84b503b085ee80c44a77..f4e5f2b29452ff926c536ebcd13c313aaa510551 100644
--- a/greg/api/serializers/person.py
+++ b/greg/api/serializers/person.py
@@ -1,6 +1,6 @@
 from rest_framework import serializers
 
-from greg.models import Person, PersonRole, Role
+from greg.models import Person, PersonRole, Role, PersonIdentity
 
 
 class PersonSerializer(serializers.ModelSerializer):
@@ -31,3 +31,9 @@ class PersonRoleSerializer(serializers.ModelSerializer):
             "updated",
             "role",
         ]
+
+
+class PersonIdentitySerializer(serializers.ModelSerializer):
+    class Meta:
+        model = PersonIdentity
+        fields = "__all__"
diff --git a/greg/api/urls.py b/greg/api/urls.py
index 69743c28704421b8128bbe25b19f66d3e7412a42..a589fe46911f3cc8193e20e7516b49d1d96d2598 100644
--- a/greg/api/urls.py
+++ b/greg/api/urls.py
@@ -13,6 +13,7 @@ from greg.api.views.organizational_unit import OrganizationalUnitViewSet
 from greg.api.views.person import (
     PersonRoleViewSet,
     PersonViewSet,
+    PersonIdentityViewSet,
 )
 from greg.api.views.role import RoleViewSet
 from greg.api.views.health import Health
@@ -43,7 +44,19 @@ urlpatterns += [
     ),
     re_path(
         r"^persons/(?P<person_id>[0-9]+)/roles/(?P<id>[0-9]+)/$",
-        PersonRoleViewSet.as_view({"get": "retrieve"}),
+        PersonRoleViewSet.as_view(
+            {"get": "retrieve", "patch": "partial_update", "delete": "destroy"}
+        ),
         name="person_role-detail",
     ),
+    re_path(
+        r"^persons/(?P<person_id>[0-9]+)/identities/$",
+        PersonIdentityViewSet.as_view({"get": "list", "post": "create"}),
+        name="person_identity-list",
+    ),
+    re_path(
+        r"^persons/(?P<person_id>[0-9]+)/identities/(?P<id>[0-9]+)$",
+        PersonIdentityViewSet.as_view({"delete": "destroy", "patch": "partial_update"}),
+        name="person_identity-detail",
+    ),
 ]
diff --git a/greg/api/views/person.py b/greg/api/views/person.py
index 3566e930e53ba46bc3f6682d0b59eddbc7e30632..9918256b5f05aba0416213c34d3245d08728f21a 100644
--- a/greg/api/views/person.py
+++ b/greg/api/views/person.py
@@ -1,12 +1,17 @@
 from django.core.exceptions import ValidationError
 from django_filters import rest_framework as filters
 from drf_spectacular.utils import extend_schema, OpenApiParameter
-from rest_framework import viewsets
+from rest_framework import viewsets, status
+from rest_framework.response import Response
 
-from greg.api.filters import PersonFilter, PersonRoleFilter
+from greg.api.filters import PersonFilter, PersonRoleFilter, PersonIdentityFilter
 from greg.api.pagination import PrimaryKeyCursorPagination
-from greg.api.serializers.person import PersonSerializer, PersonRoleSerializer
-from greg.models import Person, PersonRole
+from greg.api.serializers.person import (
+    PersonSerializer,
+    PersonRoleSerializer,
+    PersonIdentitySerializer,
+)
+from greg.models import Person, PersonRole, PersonIdentity
 
 
 class PersonViewSet(viewsets.ModelViewSet):
@@ -42,6 +47,7 @@ class PersonRoleViewSet(viewsets.ModelViewSet):
     pagination_class = PrimaryKeyCursorPagination
     filter_backends = (filters.DjangoFilterBackend,)
     filterset_class = PersonRoleFilter
+    lookup_field = "id"
 
     def get_queryset(self):
         qs = self.queryset
@@ -62,3 +68,45 @@ class PersonRoleViewSet(viewsets.ModelViewSet):
             raise ValidationError("No person id")
 
         serializer.save(person_id=person_id)
+
+
+class PersonIdentityViewSet(viewsets.ModelViewSet):
+    """
+    Person identity API
+    """
+
+    queryset = PersonIdentity.objects.all().order_by("id")
+    serializer_class = PersonIdentitySerializer
+    pagination_class = PrimaryKeyCursorPagination
+    filter_backends = (filters.DjangoFilterBackend,)
+    filterset_class = PersonIdentityFilter
+    # This is set so that the id parameter in the path of the URL is used for looking up objects
+    lookup_url_kwarg = "id"
+
+    def get_queryset(self):
+        qs = self.queryset
+        if not self.kwargs:
+            return qs.none()
+        person_id = self.kwargs["person_id"]
+        qs = qs.filter(person_id=person_id)
+        return qs
+
+    def create(self, request, *args, **kwargs):
+        # Want to get the person id which is part of the API path and then
+        # include this with the data used to create the identity for the person
+        person_id = self.kwargs["person_id"]
+
+        if person_id is None:
+            # Should not happen, the person ID is part of the API path
+            raise ValidationError("No person id")
+
+        input_data = request.data.copy()
+        input_data["person"] = person_id
+
+        serializer = self.get_serializer(data=input_data)
+        serializer.is_valid(raise_exception=True)
+        self.perform_create(serializer)
+        headers = self.get_success_headers(serializer.data)
+        return Response(
+            serializer.data, status=status.HTTP_201_CREATED, headers=headers
+        )
diff --git a/greg/migrations/0001_initial.py b/greg/migrations/0001_initial.py
index 62250ae108abb1d187c24eb74c4fba07ee65b464..75cc9c477c8b3a9b1946f9e1e67a0a5513f98d79 100644
--- a/greg/migrations/0001_initial.py
+++ b/greg/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.2.5 on 2021-08-05 13:10
+# Generated by Django 3.2.5 on 2021-08-04 11:07
 
 import datetime
 import dirtyfields.dirtyfields
@@ -166,7 +166,7 @@ class Migration(migrations.Migration):
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('created', models.DateTimeField(auto_now_add=True)),
                 ('updated', models.DateTimeField(auto_now=True)),
-                ('type', models.CharField(choices=[('PASSPORT_NUMBER', 'Passport Number'), ('FEIDE_ID', 'Feide Id')], max_length=15)),
+                ('type', models.CharField(choices=[('ID_PORTEN', 'Id Porten'), ('FEIDE_ID', 'Feide Id'), ('PASSPORT', 'Passport'), ('DRIVERS_LICENSE', 'Drivers License'), ('NATIONAL_ID_CARD', 'National Id Card'), ('OTHER', 'Other')], max_length=16)),
                 ('source', models.CharField(max_length=256)),
                 ('value', models.CharField(max_length=256)),
                 ('verified', models.CharField(blank=True, choices=[('AUTOMATIC', 'Automatic'), ('MANUAL', 'Manual')], max_length=9)),
@@ -185,7 +185,7 @@ class Migration(migrations.Migration):
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('created', models.DateTimeField(auto_now_add=True)),
                 ('updated', models.DateTimeField(auto_now=True)),
-                ('consent_given_at', models.DateField()),
+                ('consent_given_at', models.DateField(null=True)),
                 ('consent', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='link_person_consent', to='greg.consent')),
                 ('person', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='link_person_consent', to='greg.person')),
             ],
diff --git a/greg/models.py b/greg/models.py
index 46d4a4883581d4c6184d4cdd684605fe9cbfe229..a0db3da1a55c194ed4d5b04f52af2c7ed3c37239 100644
--- a/greg/models.py
+++ b/greg/models.py
@@ -131,10 +131,14 @@ class Notification(BaseModel):
 
 
 class PersonIdentity(BaseModel):
-    # TODO: Add more types
     class IdentityType(models.TextChoices):
-        PASSPORT_NUMBER = "PASSPORT_NUMBER"
+        ID_PORTEN = "ID_PORTEN"
         FEIDE_ID = "FEIDE_ID"
+        PASSPORT = "PASSPORT"
+        DRIVERS_LICENSE = "DRIVERS_LICENSE"
+        NATIONAL_ID_CARD = "NATIONAL_ID_CARD"
+        # Sponsor writes what is used in the value column
+        OTHER = "OTHER"
 
     class Verified(models.TextChoices):
         AUTOMATIC = "AUTOMATIC"
@@ -143,7 +147,7 @@ class PersonIdentity(BaseModel):
     person = models.ForeignKey(
         "Person", on_delete=models.PROTECT, related_name="person"
     )
-    type = models.CharField(max_length=15, choices=IdentityType.choices)
+    type = models.CharField(max_length=16, choices=IdentityType.choices)
     source = models.CharField(max_length=256)
     value = models.CharField(max_length=256)
     verified = models.CharField(max_length=9, choices=Verified.choices, blank=True)
@@ -202,7 +206,8 @@ class PersonConsent(BaseModel):
     consent = models.ForeignKey(
         "Consent", on_delete=models.PROTECT, related_name="link_person_consent"
     )
-    consent_given_at = models.DateField()
+    # If the date is blank it means the person has not given consent yet
+    consent_given_at = models.DateField(null=True)
 
     class Meta:
         constraints = [
diff --git a/greg/signals.py b/greg/signals.py
index 3a3a52726fde5a9651633d7c0e658f3e79c0dfe7..e1d0f60c7abab9bd97d0c988a5e6e46ea6d4037d 100644
--- a/greg/signals.py
+++ b/greg/signals.py
@@ -1,5 +1,6 @@
 import time
 import logging
+from typing import Dict
 
 from django.db import models
 from django.dispatch import receiver
@@ -9,6 +10,9 @@ from greg.models import (
     PersonRole,
     Role,
     Notification,
+    PersonIdentity,
+    PersonConsent,
+    Consent,
 )
 
 logger = logging.getLogger(__name__)
@@ -17,6 +21,8 @@ SUPPORTED_MODELS = (
     Person,
     PersonRole,
     Role,
+    PersonIdentity,
+    PersonConsent,
 )
 
 
@@ -30,6 +36,10 @@ def disconnect_notification_signals(*args, **kwargs):
     models.signals.pre_save.disconnect(dispatch_uid="add_changed_fields_callback")
     models.signals.post_save.disconnect(dispatch_uid="save_notification_callback")
     models.signals.post_delete.disconnect(dispatch_uid="delete_notification_callback")
+    models.signals.m2m_changed.connect(
+        receiver=m2m_changed_notification_callback,
+        dispatch_uid="m2m_changed_notification_callback",
+    )
 
 
 @receiver(models.signals.post_migrate)
@@ -70,7 +80,7 @@ def add_changed_fields_callback(sender, instance, raw, *args, **kwargs):
     Makes note of any dirty (changed) fields before they are saved,
     stuffing them in the instance for use in any post-save callbacks.
     """
-    if not isinstance(instance, (Person, PersonRole)):
+    if not isinstance(instance, SUPPORTED_MODELS):
         return
     changed = instance.is_dirty()
     if not changed:
@@ -84,11 +94,8 @@ def add_changed_fields_callback(sender, instance, raw, *args, **kwargs):
 def save_notification_callback(sender, instance, created, *args, **kwargs):
     if not isinstance(instance, SUPPORTED_MODELS):
         return
-    meta = {}
+    meta = _create_metadata(instance)
     operation = "add" if created else "update"
-    if isinstance(instance, PersonRole):
-        meta["person_id"] = instance.person.id
-        meta["role_id"] = instance.role.id
     _store_notification(
         identifier=instance.id,
         object_type=instance._meta.object_name,
@@ -101,13 +108,78 @@ def save_notification_callback(sender, instance, created, *args, **kwargs):
 def delete_notification_callback(sender, instance, *args, **kwargs):
     if not isinstance(instance, SUPPORTED_MODELS):
         return
-    meta = {}
-    if isinstance(instance, PersonRole):
-        meta["person_id"] = instance.person.id
-        meta["role_id"] = instance.role.id
+    meta = _create_metadata(instance)
     _store_notification(
         identifier=instance.id,
         object_type=instance._meta.object_name,
         operation="delete",
         **meta
     )
+
+
+@receiver(models.signals.m2m_changed, dispatch_uid="m2m_changed_notification_callback")
+def m2m_changed_notification_callback(
+    sender, instance, action, *args, model=None, pk_set=None, **kwargs
+):
+    if action not in ("post_add", "post_remove"):
+        return
+    if sender not in (PersonConsent, PersonRole, PersonIdentity):
+        return
+
+    operation = "add" if action == "post_add" else "delete"
+    instance_type = type(instance)
+
+    if sender is PersonConsent:
+        person_consents = []
+        if instance_type is Person and model is Consent:
+            person_consents = PersonConsent.objects.filter(
+                person_id=instance.id, consent_id__in=pk_set
+            )
+        elif instance_type is Consent and model is Person:
+            person_consents = PersonConsent.objects.filter(
+                consent_id=instance.id, person_id__in=pk_set
+            )
+
+        for pc in person_consents:
+            meta = _create_metadata(pc)
+            _store_notification(
+                identifier=pc.id,
+                object_type=PersonConsent._meta.object_name,
+                operation=operation,
+                **meta
+            )
+    elif sender is PersonRole:
+        person_roles = []
+        if instance_type is Person and model is Role:
+            person_roles = PersonRole.objects.filter(
+                person_id=instance.id, role_id__in=pk_set
+            )
+        elif instance_type is Role and model is Person:
+            person_roles = PersonRole.objects.filter(
+                role_id=instance.id, person_id__in=pk_set
+            )
+
+        for pr in person_roles:
+            meta = _create_metadata(pr)
+            _store_notification(
+                identifier=pr.id,
+                object_type=PersonRole._meta.object_name,
+                operation=operation,
+                **meta
+            )
+
+
+def _create_metadata(instance) -> Dict:
+    meta = {}
+
+    if isinstance(instance, PersonRole):
+        meta["person_id"] = instance.person.id
+        meta["role_id"] = instance.role.id
+    if isinstance(instance, PersonIdentity):
+        meta["person_id"] = instance.person.id
+        meta["identity_id"] = instance.id
+    if isinstance(instance, PersonConsent):
+        meta["person_id"] = instance.person.id
+        meta["consent_id"] = instance.consent.id
+
+    return meta
diff --git a/greg/tests/api/test_person.py b/greg/tests/api/test_person.py
index f79d9d26a2475b542cc22dda4c9dbb7ed7104ce4..a81e383fd2f1c75cd3d35d899195a2ead12be997 100644
--- a/greg/tests/api/test_person.py
+++ b/greg/tests/api/test_person.py
@@ -1,10 +1,11 @@
-import pytest
+from typing import Dict
 
+import pytest
 from rest_framework import status
 from rest_framework.reverse import reverse
 from rest_framework.status import HTTP_200_OK
 
-from greg.models import Person
+from greg.models import Person, PersonIdentity, Sponsor, Role, OrganizationalUnit
 
 
 @pytest.fixture
@@ -29,6 +30,81 @@ def person_bar() -> Person:
     )
 
 
+@pytest.fixture
+def sponsor_guy() -> Sponsor:
+    return Sponsor.objects.create(feide_id="guy@example.org")
+
+
+@pytest.fixture
+def person_foo_verified(person_foo, sponsor_guy) -> PersonIdentity:
+    return PersonIdentity.objects.create(
+        person=person_foo,
+        type=PersonIdentity.IdentityType.PASSPORT,
+        source="Test",
+        value="12345",
+        verified=PersonIdentity.Verified.MANUAL,
+        verified_by=sponsor_guy,
+        verified_when="2021-06-15",
+    )
+
+
+@pytest.fixture
+def person_foo_not_verified(person_foo) -> PersonIdentity:
+    return PersonIdentity.objects.create(
+        person=person_foo,
+        type=PersonIdentity.IdentityType.DRIVERS_LICENSE,
+        source="Test",
+        value="12345",
+    )
+
+
+@pytest.fixture
+def role_visiting_professor() -> Role:
+    return Role.objects.create(
+        type="visiting_professor",
+        name_nb="Gjesteprofessor",
+        name_en="Visiting professor",
+        description_nb="Gjesteprofessor",
+        description_en="Visiting professor",
+        default_duration_days=180,
+    )
+
+
+@pytest.fixture
+def unit_human_resources() -> OrganizationalUnit:
+    return OrganizationalUnit.objects.create(
+        orgreg_id="org_unit_1", name_nb="Personal", name_en="Human Resources"
+    )
+
+
+@pytest.fixture()
+def role_test_guest() -> Role:
+    return Role.objects.create(type="Test Guest")
+
+
+@pytest.fixture()
+def sponsor_bar() -> Sponsor:
+    return Sponsor.objects.create(feide_id="bar")
+
+
+@pytest.fixture
+def unit_foo() -> OrganizationalUnit:
+    return OrganizationalUnit.objects.create(orgreg_id="12345", name_en="foo_unit")
+
+
+@pytest.fixture
+def role_data_guest(
+    role_test_guest: Role, sponsor_bar: Sponsor, unit_foo: OrganizationalUnit
+) -> Dict:
+    return {
+        "role": "Test Guest",
+        "start_date": "2021-06-10",
+        "end_date": "2021-08-10",
+        "registered_by": sponsor_bar.id,
+        "unit": unit_foo.id,
+    }
+
+
 @pytest.mark.django_db
 def test_get_person(client, person_foo):
     resp = client.get(reverse("person-detail", kwargs={"id": person_foo.id}))
@@ -48,29 +124,33 @@ def test_persons(client, person_foo, person_bar):
 
 
 @pytest.mark.django_db
-def test_persons_verified_filter_include(client, setup_db_test_data):
+def test_persons_verified_filter_include(
+    client, person_bar, person_foo, person_foo_verified
+):
     url = reverse("person-list")
     response = client.get(url, {"verified": "true"})
     results = response.json()["results"]
     assert len(results) == 1
-    # The following person will have a verified identity set up for him
-    # in the test data
-    assert results[0]["first_name"] == "Christopher"
-    assert results[0]["last_name"] == "Flores"
+    assert results[0]["first_name"] == "Foo"
+    assert results[0]["last_name"] == "Foo"
 
 
 @pytest.mark.django_db
-def test_persons_verified_filter_exclude(client, setup_db_test_data):
+def test_persons_verified_filter_exclude(
+    client, person_bar, person_foo, person_foo_verified
+):
     url = reverse("person-list")
     response = client.get(url, {"verified": "false"})
     results = response.json()["results"]
-    names = [(result["first_name"], result["last_name"]) for result in results]
-    assert len(results) == 9
-    assert ("Christopher", "Flores") not in names
+    assert len(results) == 1
+    assert results[0]["first_name"] == "Bar"
+    assert results[0]["last_name"] == "Bar"
 
 
 @pytest.mark.django_db
-def test_add_role(client, person_foo):
+def test_add_role(
+    client, person_foo, role_visiting_professor, sponsor_guy, unit_human_resources
+):
     url = reverse("person_role-list", kwargs={"person_id": person_foo.id})
     roles_for_person = client.get(url).json()["results"]
 
@@ -78,7 +158,7 @@ def test_add_role(client, person_foo):
     assert len(roles_for_person) == 0
 
     role_data = {
-        "role": "Visiting Professor",
+        "role": "visiting_professor",
         "start_date": "2021-06-10",
         "end_date": "2021-08-10",
         "registered_by": "1",
@@ -94,3 +174,191 @@ def test_add_role(client, person_foo):
     # Check that the role shows up when listing roles for the person now
     assert len(roles_for_person) == 1
     assert roles_for_person[0]["id"] == response_data["id"]
+
+
+@pytest.mark.django_db
+def test_update_role(client, person_foo, role_data_guest):
+    url = reverse("person_role-list", kwargs={"person_id": person_foo.id})
+    response = client.post(url, role_data_guest)
+    response_data = response.json()
+
+    assert response_data["start_date"] == "2021-06-10"
+
+    # Update the date and check that the change is registered
+    role_id = response.json()["id"]
+    updated_role = role_data_guest.copy()
+    updated_role["start_date"] = "2021-06-15"
+
+    url_detail = reverse(
+        "person_role-detail", kwargs={"person_id": person_foo.id, "id": role_id}
+    )
+    client.patch(url_detail, updated_role)
+
+    updated_role_data = client.get(url)
+    updated_data = updated_role_data.json()["results"][0]
+
+    assert updated_data["id"] == role_id
+    assert updated_data["start_date"] == "2021-06-15"
+
+
+@pytest.mark.django_db
+def test_delete_role(client, person_foo, role_data_guest):
+    url = reverse("person_role-list", kwargs={"person_id": person_foo.id})
+    role_id = client.post(url, role_data_guest).json()["id"]
+    roles_for_person = client.get(url).json()["results"]
+
+    assert len(roles_for_person) == 1
+
+    url_detail = reverse(
+        "person_role-detail", kwargs={"person_id": person_foo.id, "id": role_id}
+    )
+    client.delete(url_detail)
+
+    assert len(client.get(url).json()["results"]) == 0
+
+
+@pytest.mark.django_db
+def test_identity_list(
+    client, person_foo, person_foo_verified, person_foo_not_verified
+):
+    response = client.get(
+        reverse("person-list"),
+        data={"first_name": person_foo.first_name, "last_name": person_foo.last_name},
+    )
+    person_id = response.json()["results"][0]["id"]
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_id})
+    )
+    data = response.json()["results"]
+    assert len(data) == 2
+
+
+@pytest.mark.django_db
+def test_identity_add(client, person_foo):
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+    assert len(results) == 0
+
+    data = {
+        "type": PersonIdentity.IdentityType.FEIDE_ID,
+        "source": "Test source",
+        "value": "12345",
+    }
+    client.post(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id}), data=data
+    )
+
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+    assert len(results) == 1
+
+
+@pytest.mark.django_db
+def test_identity_delete(client, person_foo):
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+    assert len(results) == 0
+
+    data = {
+        "type": PersonIdentity.IdentityType.FEIDE_ID,
+        "source": "Test source",
+        "value": "12345",
+    }
+    post_response = client.post(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id}), data=data
+    )
+    identity_id = post_response.json()["id"]
+
+    # Create two identities for the user
+    data = {
+        "type": PersonIdentity.IdentityType.PASSPORT,
+        "source": "Test",
+        "value": "1234413241235",
+    }
+    post_response2 = client.post(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id}), data=data
+    )
+    identity_id2 = post_response2.json()["id"]
+
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+    assert len(results) == 2
+
+    # Delete the first identity created
+    client.delete(
+        reverse(
+            "person_identity-detail",
+            kwargs={"person_id": person_foo.id, "id": identity_id},
+        )
+    )
+
+    # Check that the other identity is still there
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+
+    assert len(results) == 1
+    assert results[0]["id"] == identity_id2
+
+
+@pytest.mark.django_db
+def test_identity_update(client, person_foo):
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+    assert len(results) == 0
+
+    data = {
+        "type": PersonIdentity.IdentityType.FEIDE_ID,
+        "source": "Test source",
+        "value": "12345",
+    }
+    client.post(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id}), data=data
+    )
+
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+    assert len(results) == 1
+
+    identity_id = results[0]["id"]
+
+    assert results[0]["type"] == data["type"]
+    assert results[0]["source"] == data["source"]
+    assert results[0]["value"] == data["value"]
+
+    data_updated = {
+        "type": PersonIdentity.IdentityType.PASSPORT,
+        "source": "Test",
+        "value": "10000",
+    }
+    patch_response = client.patch(
+        reverse(
+            "person_identity-detail",
+            kwargs={"person_id": person_foo.id, "id": identity_id},
+        ),
+        data=data_updated,
+    )
+    assert patch_response.status_code == status.HTTP_200_OK
+
+    response = client.get(
+        reverse("person_identity-list", kwargs={"person_id": person_foo.id})
+    )
+    results = response.json()["results"]
+    assert len(results) == 1
+
+    assert results[0]["type"] == data_updated["type"]
+    assert results[0]["source"] == data_updated["source"]
+    assert results[0]["value"] == data_updated["value"]
diff --git a/greg/tests/conftest.py b/greg/tests/conftest.py
index bbdf24d3973f2d452e7e45e22ae1a81e2bcda7b5..9280ae7c911ecba5bece2b7cfc414d49e183cb69 100644
--- a/greg/tests/conftest.py
+++ b/greg/tests/conftest.py
@@ -9,22 +9,9 @@ from django.contrib.auth import get_user_model
 # see https://github.com/joke2k/faker/issues/753
 import pytest
 
-from greg.tests.populate_database import DatabasePopulation
-
 logging.getLogger("faker").setLevel(logging.ERROR)
 
 
-# The database is populated once when scope is session.
-# If the scope is changed to function some additional
-# logic is needed to make sure the old data is cleaned
-# before the seeding is run again
-@pytest.fixture(scope="session")
-def setup_db_test_data(django_db_setup, django_db_blocker):
-    with django_db_blocker.unblock():
-        database_seeder = DatabasePopulation()
-        database_seeder.populate_database()
-
-
 @pytest.fixture
 def client() -> APIClient:
     user, _ = get_user_model().objects.get_or_create(username="test")
diff --git a/greg/tests/models/test_consent.py b/greg/tests/models/test_consent.py
index ad800bd596686e43520556a1acb2ec22aa2a76c6..8f7711e08ff37e9e20dc7f8ce9eacca45f51ce34 100644
--- a/greg/tests/models/test_consent.py
+++ b/greg/tests/models/test_consent.py
@@ -1,8 +1,11 @@
+import datetime
+
 import pytest
 
 from greg.models import (
     Person,
     Consent,
+    PersonConsent,
 )
 
 
@@ -17,9 +20,9 @@ def person() -> Person:
     )
 
 
-@pytest.mark.django_db
-def test_add_consent_to_person(person):
-    consent = Consent.objects.create(
+@pytest.fixture()
+def consent() -> Consent:
+    return Consent.objects.create(
         type="it_guidelines",
         consent_name_en="IT Guidelines",
         consent_name_nb="IT Regelverk",
@@ -28,4 +31,25 @@ def test_add_consent_to_person(person):
         consent_link_en="https://example.org/it_guidelines",
         user_allowed_to_change=False,
     )
-    person.consents.add(consent, through_defaults={"consent_given_at": "2021-06-20"})
+
+
+@pytest.mark.django_db
+def test_add_consent_to_person(person: Person, consent: Consent):
+    consent_given_date = "2021-06-20"
+    person.consents.add(consent, through_defaults={"consent_given_at": consent_given_date})  # type: ignore
+    person_consent_links = PersonConsent.objects.filter(person_id=person.id)
+
+    assert len(person_consent_links) == 1
+    assert person_consent_links[0].person_id == person.id
+    assert person_consent_links[0].consent_id == consent.id
+    assert person_consent_links[0].consent_given_at == datetime.date(2021, 6, 20)
+
+
+@pytest.mark.django_db
+def test_add_not_acknowledged_consent_to_person(person: Person, consent: Consent):
+    person.consents.add(consent)
+    person_consent_links = PersonConsent.objects.filter(person_id=person.id)
+    assert len(person_consent_links) == 1
+    assert person_consent_links[0].person_id == person.id
+    assert person_consent_links[0].consent_id == consent.id
+    assert person_consent_links[0].consent_given_at is None
diff --git a/greg/tests/test_notifications.py b/greg/tests/test_notifications.py
new file mode 100644
index 0000000000000000000000000000000000000000..8172a2be5cf1668a39a8f848bd62e420f349730f
--- /dev/null
+++ b/greg/tests/test_notifications.py
@@ -0,0 +1,235 @@
+import pytest
+
+from greg.models import (
+    Person,
+    Notification,
+    Consent,
+    Role,
+    OrganizationalUnit,
+    Sponsor,
+    PersonConsent,
+    PersonIdentity,
+)
+
+
+@pytest.fixture
+def person() -> Person:
+    return Person.objects.create(
+        first_name="Test",
+        last_name="Tester",
+        date_of_birth="2000-01-27",
+        email="test@example.org",
+        mobile_phone="123456789",
+    )
+
+
+@pytest.fixture
+def role_foo() -> Role:
+    return Role.objects.create(type="role_foo", name_en="Role Foo")
+
+
+@pytest.fixture
+def org_unit_bar() -> OrganizationalUnit:
+    return OrganizationalUnit.objects.create(orgreg_id="bar_unit")
+
+
+@pytest.fixture
+def sponsor() -> Sponsor:
+    return Sponsor.objects.create(feide_id="sponsor_id")
+
+
+@pytest.fixture
+def consent() -> Consent:
+    return Consent.objects.create(
+        type="it_guidelines",
+        consent_name_en="IT Guidelines",
+        consent_name_nb="IT Regelverk",
+        consent_description_en="IT Guidelines description",
+        consent_description_nb="IT Regelverk beskrivelse",
+        consent_link_en="https://example.org/it_guidelines",
+        user_allowed_to_change=False,
+    )
+
+
+@pytest.fixture
+def person_identity(person: Person) -> PersonIdentity:
+    return PersonIdentity.objects.create(
+        person=person,
+        type=PersonIdentity.IdentityType.PASSPORT_NUMBER,
+        source="Test",
+        value="12345678901",
+    )
+
+
+@pytest.mark.django_db
+def test_person_add_notification(person: Person):
+    notifications = Notification.objects.filter(object_type="Person")
+    assert len(notifications) == 1
+    assert notifications[0].operation == "add"
+    assert notifications[0].identifier == person.id
+
+
+@pytest.mark.django_db
+def test_person_update_notification(person: Person):
+    person.first_name = "New first name"
+    person.save()
+    notifications = Notification.objects.filter(object_type="Person")
+    assert len(notifications) == 2
+    assert notifications[1].operation == "update"
+    assert notifications[1].identifier == person.id
+
+
+@pytest.mark.django_db
+def test_person_delete_notification(person: Person):
+    person_id = person.id
+    person.delete()
+    notifications = Notification.objects.filter(object_type="Person")
+    assert len(notifications) == 2
+    assert notifications[1].operation == "delete"
+    assert notifications[1].identifier == person_id
+
+
+@pytest.mark.django_db
+def test_role_add_notification(
+    person: Person, role_foo: Role, org_unit_bar: OrganizationalUnit, sponsor: Sponsor
+):
+    person.roles.add(  # type: ignore
+        role_foo,
+        through_defaults={
+            "start_date": "2021-05-06",
+            "end_date": "2021-10-20",
+            "unit": org_unit_bar,
+            "registered_by": sponsor,
+        },
+    )
+    notifications = Notification.objects.filter(object_type="PersonRole")
+    assert len(notifications) == 1
+    assert notifications[0].operation == "add"
+    meta_data = notifications[0].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["role_id"] == role_foo.id
+
+
+@pytest.mark.django_db
+def test_role_update_notification(
+    person: Person, role_foo: Role, org_unit_bar: OrganizationalUnit, sponsor: Sponsor
+):
+    person.roles.add(  # type: ignore
+        role_foo,
+        through_defaults={
+            "start_date": "2021-05-06",
+            "end_date": "2021-10-20",
+            "unit": org_unit_bar,
+            "registered_by": sponsor,
+        },
+    )
+
+    assert len(person.person_roles.all()) == 1
+    person_role = person.person_roles.all()[0]
+    person_role.end_date = "2021-10-21"
+    person_role.save()
+    notifications = Notification.objects.filter(object_type="PersonRole")
+    assert len(notifications) == 2
+    assert notifications[1].operation == "update"
+    meta_data = notifications[1].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["role_id"] == role_foo.id
+
+
+@pytest.mark.django_db
+def test_role_delete_notification(
+    person: Person, role_foo: Role, org_unit_bar: OrganizationalUnit, sponsor: Sponsor
+):
+    person.roles.add(  # type: ignore
+        role_foo,
+        through_defaults={
+            "start_date": "2021-05-06",
+            "end_date": "2021-10-20",
+            "unit": org_unit_bar,
+            "registered_by": sponsor,
+        },
+    )
+
+    assert len(person.person_roles.all()) == 1
+    person_role = person.person_roles.all()[0]
+    person_role.delete()
+    notifications = Notification.objects.filter(object_type="PersonRole")
+    assert len(notifications) == 2
+    assert notifications[1].operation == "delete"
+    meta_data = notifications[1].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["role_id"] == role_foo.id
+
+
+@pytest.mark.django_db
+def test_consent_add_notification(person: Person, consent: Consent):
+    person.consents.add(consent, through_defaults={"consent_given_at": "2021-06-20"})  # type: ignore
+    notifications = Notification.objects.filter(object_type="PersonConsent")
+    assert len(notifications) == 1
+    assert notifications[0].identifier == person.id
+    meta_data = notifications[0].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["consent_id"] == consent.id
+
+
+@pytest.mark.django_db
+def test_consent_update_notification(person: Person, consent: Consent):
+    person.consents.add(consent, through_defaults={"consent_given_at": "2021-06-20"})  # type: ignore
+    person_consents = PersonConsent.objects.filter(person=person, consent=consent)
+    person_consents[0].consent_given_at = "2021-06-21"
+    person_consents[0].save()
+
+    notifications = Notification.objects.filter(object_type="PersonConsent")
+    assert len(notifications) == 2
+    assert notifications[0].identifier == person.id
+    meta_data = notifications[0].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["consent_id"] == consent.id
+
+
+@pytest.mark.django_db
+def test_consent_delete_notification(person: Person, consent: Consent):
+    person.consents.add(consent, through_defaults={"consent_given_at": "2021-06-20"})  # type: ignore
+    person_consents = PersonConsent.objects.filter(person=person, consent=consent)
+    person_consents[0].delete()
+    notifications = Notification.objects.filter(object_type="PersonConsent")
+
+    assert len(notifications) == 2
+    assert notifications[1].identifier == person.id
+    assert notifications[1].operation == "delete"
+    meta_data = notifications[0].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["consent_id"] == consent.id
+
+
+@pytest.mark.django_db
+def test_person_identity_add_notification(
+    person: Person, person_identity: PersonIdentity, sponsor: Sponsor
+):
+    notifications = Notification.objects.filter(object_type="PersonIdentity")
+    assert len(notifications) == 1
+    assert notifications[0].identifier == person.id
+    assert notifications[0].operation == "add"
+
+    meta_data = notifications[0].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["identity_id"] == person_identity.id
+
+
+@pytest.mark.django_db
+def test_person_identity_update_notification(
+    person: Person, person_identity: PersonIdentity, sponsor: Sponsor
+):
+    person_identity.verified = PersonIdentity.Verified.MANUAL
+    person_identity.verified_by = sponsor
+    person_identity.verified_when = "2021-08-02"
+    person_identity.save()
+
+    notifications = Notification.objects.filter(object_type="PersonIdentity")
+    # One notification for adding person identity and one for updating it
+    assert len(notifications) == 2
+    assert notifications[1].operation == "update"
+
+    meta_data = notifications[1].meta
+    assert meta_data["person_id"] == person.id
+    assert meta_data["identity_id"] == person_identity.id