diff --git a/greg/api/filters.py b/greg/api/filters.py
index 14ef355eb0bc7935dc090f5cf189055e2df78765..c7af546c6b456316345e0f4977fc2a1675cec7e9 100644
--- a/greg/api/filters.py
+++ b/greg/api/filters.py
@@ -1,19 +1,27 @@
-from django_filters import rest_framework as filters
+from django_filters.rest_framework import (
+    BaseInFilter,
+    BooleanFilter,
+    FilterSet,
+)
 
-from greg.models import Person, PersonRole, PersonIdentity
+from greg.models import (
+    Person,
+    PersonRole,
+    PersonIdentity,
+)
 
 
-class PersonRoleFilter(filters.FilterSet):
-    type = filters.BaseInFilter(field_name="role__type", lookup_expr="in")
+class PersonRoleFilter(FilterSet):
+    type = BaseInFilter(field_name="role__type", lookup_expr="in")
 
     class Meta:
         model = PersonRole
         fields = ["type"]
 
 
-class PersonFilter(filters.FilterSet):
-    verified = filters.BooleanFilter(
-        field_name="person__verified_by_id", lookup_expr="isnull", exclude=True
+class PersonFilter(FilterSet):
+    verified = BooleanFilter(
+        field_name="identities__verified_by_id", lookup_expr="isnull", exclude=True
     )
 
     class Meta:
@@ -21,7 +29,7 @@ class PersonFilter(filters.FilterSet):
         fields = ["first_name", "last_name", "verified"]
 
 
-class PersonIdentityFilter(filters.FilterSet):
+class PersonIdentityFilter(FilterSet):
     class Meta:
         model = PersonIdentity
         fields = ["type", "verified_by_id"]
diff --git a/greg/api/views/person.py b/greg/api/views/person.py
index 40f9c8d07fc1b8cb556046aa3536f3e21029cf5d..bb58842992f4afc293d90a6f7457388046579382 100644
--- a/greg/api/views/person.py
+++ b/greg/api/views/person.py
@@ -36,7 +36,7 @@ class PersonViewSet(viewsets.ModelViewSet):
         ]
     )
     def list(self, request, *args, **kwargs):
-        return super().list(request)
+        return super().list(request, *args, **kwargs)
 
 
 class PersonRoleViewSet(viewsets.ModelViewSet):
diff --git a/greg/models.py b/greg/models.py
index f3ea02a00daaf67ea465e20d532059025eb9d9f2..f50684c81784f6c226c99befc88de038603ad037 100644
--- a/greg/models.py
+++ b/greg/models.py
@@ -1,4 +1,7 @@
-from datetime import date
+from datetime import (
+    date,
+    datetime,
+)
 
 from dirtyfields import DirtyFieldsMixin
 from django.db import models
@@ -57,6 +60,65 @@ class Person(BaseModel):
             self.last_name,
         )
 
+    @property
+    def is_registered(self) -> bool:
+        """
+        A registered guest is a person who has completed
+        the registration process.
+
+        The registration process requires that the guest has verified
+        their email address via a token link, filled in the required
+        personal information along with providing at least one
+        verification method, and accepted the institution's mandatory
+        consent forms.
+
+        The registered guest may or may not already be verified,
+        depending on the verification method.  However, before a
+        guest is cleared for account creation at the institution's
+        IGA, the guest must be both registered (``is_registered``)
+        and verified (``is_verified``).
+        """
+        # registration_completed_date is set only after accepting consents
+        return (
+            self.registration_completed_date is not None
+            and self.registration_completed_date <= datetime.now()
+        )
+
+    @property
+    def is_verified(self) -> bool:
+        """
+        A verified guest is a person whom has had their personal
+        identity verified.
+
+        Due to the diversity of guests at a university institution,
+        there are many ways for guests to identify themselves.
+        These include Feide ID, passport number, driver's license,
+        national ID card, or another manual (human) verification.
+
+        Some of these methods are implicitly trusted (Feide ID) because
+        the guest is likely a visitor from another academic institution
+        who has already been pre-verified.  Others are manul, such
+        as the sponsor vouching for having checked the guest's
+        personal details against his or her passport.
+
+        The verified guest may or may not have completed
+        the registration process which implies that it is only
+        the combination of being registered (``is_registered``)
+        and being verified (``is_verified``) that qualifies for being cleared
+        for account creation in the IGA.
+
+        Note that we do not distinguish between the quality,
+        authenticity, or trust level of the guest's associated identities.
+        """
+        # the requirement is minimum one personal identity
+        return (
+            self.identities.filter(
+                verified_when__isnull=False,
+                verified_when__lte=datetime.now(),
+            ).count()
+            >= 1
+        )
+
 
 class Role(BaseModel):
     """A role variant."""
@@ -155,7 +217,7 @@ class PersonIdentity(BaseModel):
         MANUAL = "MANUAL"
 
     person = models.ForeignKey(
-        "Person", on_delete=models.CASCADE, related_name="person"
+        "Person", on_delete=models.CASCADE, related_name="identities"
     )
     type = models.CharField(max_length=18, choices=IdentityType.choices)
     source = models.CharField(max_length=256)
@@ -167,15 +229,14 @@ class PersonIdentity(BaseModel):
     verified_when = models.DateField(null=True)
 
     def __repr__(self):
-        return (
-            "{}(id={!r}, type={!r}, source={!r}, value={!r}, verified_by={!r})".format(
-                self.__class__.__name__,
-                self.pk,
-                self.type,
-                self.source,
-                self.value,
-                self.verified_by,
-            )
+        return "{}(id={!r}, type={!r}, source={!r}, value={!r}, verified_by={!r}, verified_when={!r})".format(
+            self.__class__.__name__,
+            self.pk,
+            self.type,
+            self.source,
+            self.value,
+            self.verified_by,
+            self.verified_when,
         )
 
 
diff --git a/greg/tests/models/test_person.py b/greg/tests/models/test_person.py
index f4aeba5820a90b2ce592f87e6cf1e124b81c2d12..87455763326c84b814830ceb84f00f52c9b869fa 100644
--- a/greg/tests/models/test_person.py
+++ b/greg/tests/models/test_person.py
@@ -1,3 +1,7 @@
+from datetime import (
+    datetime,
+    timedelta,
+)
 from functools import partial
 
 import pytest
@@ -5,6 +9,7 @@ import pytest
 from greg.models import (
     OrganizationalUnit,
     Person,
+    PersonIdentity,
     PersonRole,
     Role,
     Sponsor,
@@ -30,7 +35,7 @@ def role_bar() -> Role:
 
 
 @pytest.fixture
-def person(role_foo, role_bar) -> Person:
+def person(role_foo: Role, role_bar: Role) -> Person:
     person = Person.objects.create(
         first_name="Test",
         last_name="Tester",
@@ -56,9 +61,117 @@ def person(role_foo, role_bar) -> Person:
     return person
 
 
+@pytest.fixture
+def a_year_ago() -> datetime:
+    return datetime.now() - timedelta(days=365)
+
+
+@pytest.fixture
+def a_year_into_future() -> datetime:
+    return datetime.now() + timedelta(days=365)
+
+
+@pytest.fixture
+def feide_id(a_year_ago: datetime) -> PersonIdentity:
+    return PersonIdentity(
+        type=PersonIdentity.IdentityType.FEIDE_ID,
+        verified_when=a_year_ago,
+    )
+
+
+@pytest.fixture
+def passport(a_year_ago: datetime) -> PersonIdentity:
+    return PersonIdentity(
+        type=PersonIdentity.IdentityType.PASSPORT,
+        verified_when=a_year_ago,
+    )
+
+
+@pytest.fixture
+def unverified_passport() -> PersonIdentity:
+    return PersonIdentity(type=PersonIdentity.IdentityType.PASSPORT)
+
+
+@pytest.fixture
+def future_identity(a_year_into_future: datetime) -> PersonIdentity:
+    return PersonIdentity(
+        type=PersonIdentity.IdentityType.NATIONAL_ID_CARD,
+        verified_when=a_year_into_future,
+    )
+
+
+@pytest.fixture
+def feide_verified(person: Person, feide_id: PersonIdentity) -> Person:
+    person.identities.add(feide_id, bulk=False)
+    return person
+
+
 @pytest.mark.django_db
 def test_add_multiple_roles_to_person(person, role_foo, role_bar):
     person_roles = person.roles.all()
     assert len(person_roles) == 2
     assert role_foo in person_roles
     assert role_bar in person_roles
+
+
+@pytest.mark.django_db
+def test_identities(person: Person, feide_id: PersonIdentity, passport: PersonIdentity):
+    person.identities.add(feide_id, bulk=False)
+    assert list(person.identities.all()) == [feide_id]
+    person.identities.add(passport, bulk=False)
+    assert list(person.identities.all()) == [feide_id, passport]
+
+
+@pytest.mark.django_db
+def test_is_registered_incomplete(person):
+    assert person.registration_completed_date is None
+    assert not person.is_registered
+
+
+@pytest.mark.django_db
+def test_is_registered_completed_in_past(person, a_year_ago):
+    person.registration_completed_date = a_year_ago
+    assert person.is_registered
+
+
+@pytest.mark.django_db
+def test_is_registered_completed_in_future(person, a_year_into_future):
+    person.registration_completed_date = a_year_into_future
+    assert not person.is_registered
+
+
+@pytest.mark.django_db
+@pytest.mark.parametrize("identity_type", PersonIdentity.IdentityType)
+def test_is_verified(identity_type, a_year_ago, person):
+    identity = PersonIdentity(type=identity_type, verified_when=a_year_ago)
+    person.identities.add(identity, bulk=False)
+    assert person.is_verified
+
+
+@pytest.mark.django_db
+def test_is_verified_multiple_identities(person, feide_id, passport):
+    person.identities.add(feide_id, passport, bulk=False)
+    assert person.is_verified
+
+
+@pytest.mark.django_db
+def is_verified_when_identity_is_unverified(person, unverified_passport):
+    person.identities.add(unverified_passport, bulk=False)
+    assert not person.is_verified
+
+
+@pytest.mark.django_db
+def is_verified_mixed_verified_and_unverified_identities(
+    person,
+    feide_id,
+    unverified_passport,
+    future_identity,
+):
+    person.identities.add(feide_id, unverified_passport, future_identity, bulk=False)
+    assert person.is_verified
+
+
+@pytest.mark.django_db
+def is_verified_in_future(person, future_identity):
+    person.identities.add(future_identity, bulk=False)
+    assert not person.is_verified