diff --git a/scipost_django/submissions/admin.py b/scipost_django/submissions/admin.py
index 6786a414361fb95dc9e649d29dd06087c2a3153e..558d75c0f1f0081e334993bee0e7bf89cd6e8667 100644
--- a/scipost_django/submissions/admin.py
+++ b/scipost_django/submissions/admin.py
@@ -27,6 +27,7 @@ from submissions.models import (
     Qualification,
     Readiness,
     PreprintServer,
+    RefereeIndication,
 )
 from scipost.models import Contributor
 from colleges.models import Fellowship
@@ -46,8 +47,6 @@ class PreprintServerAdmin(admin.ModelAdmin):
     autocomplete_fields = ["acad_fields"]
 
 
-
-
 @admin.register(iThenticateReport)
 class iThenticateReportAdmin(admin.ModelAdmin):
     list_display = ["doc_id", "to_submission", "status"]
@@ -57,8 +56,6 @@ class iThenticateReportAdmin(admin.ModelAdmin):
     ]
 
 
-
-
 class InternalPlagiarismAssessmentInline(admin.StackedInline):
     model = InternalPlagiarismAssessment
 
@@ -211,7 +208,7 @@ class SubmissionAdmin(GuardedModelAdmin):
                     "specialties",
                     "approaches",
                     "proceedings",
-                    "code_metadata"
+                    "code_metadata",
                 ),
             },
         ),
@@ -285,8 +282,6 @@ class SubmissionAdmin(GuardedModelAdmin):
     )
 
 
-
-
 @admin.register(EditorialAssignment)
 class EditorialAssignmentAdmin(admin.ModelAdmin):
     search_fields = [
@@ -311,8 +306,6 @@ class EditorialAssignmentAdmin(admin.ModelAdmin):
     ]
 
 
-
-
 @admin.register(RefereeInvitation)
 class RefereeInvitationAdmin(admin.ModelAdmin):
     search_fields = [
@@ -339,8 +332,6 @@ class RefereeInvitationAdmin(admin.ModelAdmin):
     ]
 
 
-
-
 @admin.register(Report)
 class ReportAdmin(admin.ModelAdmin):
     search_fields = ["author__user__last_name", "submission__title"]
@@ -362,16 +353,12 @@ class ReportAdmin(admin.ModelAdmin):
     ]
 
 
-
-
 @admin.register(EditorialCommunication)
 class EditorialCommunicationAdmin(admin.ModelAdmin):
     search_fields = ["submission__title", "referee__user__last_name", "text"]
     autocomplete_fields = ["submission", "referee"]
 
 
-
-
 class AlternativeRecommendationInline(admin.StackedInline):
     model = AlternativeRecommendation
     extra = 0
@@ -408,8 +395,6 @@ class EICRecommendationAdmin(admin.ModelAdmin):
     ]
 
 
-
-
 @admin.register(EditorialDecision)
 class EditorialDecisionAdmin(admin.ModelAdmin):
     search_fields = [
@@ -436,8 +421,6 @@ class EditorialDecisionAdmin(admin.ModelAdmin):
     ]
 
 
-
-
 @admin.register(SubmissionEvent)
 class SubmissionEventAdmin(admin.ModelAdmin):
     autocomplete_fields = [
@@ -445,3 +428,33 @@ class SubmissionEventAdmin(admin.ModelAdmin):
     ]
 
 
+@admin.register(RefereeIndication)
+class RefereeIndicationAdmin(admin.ModelAdmin):
+    search_fields = [
+        "submission__title",
+        "submission__preprint__identifier_w_vn_nr",
+        "referee__first_name",
+        "referee__last_name",
+        "first_name",
+        "last_name",
+        "email_address",
+    ]
+    list_display = (
+        "submission",
+        "indicated_by",
+        "indication",
+        "referee_name",
+    )
+    list_filter = ("indication",)
+    autocomplete_fields = [
+        "submission",
+        "indicated_by",
+        "referee",
+    ]
+
+    def referee_name(self, obj):
+        return (
+            obj.referee.full_name
+            if obj.referee
+            else f"{obj.first_name} {obj.last_name}"
+        )
diff --git a/scipost_django/submissions/forms/__init__.py b/scipost_django/submissions/forms/__init__.py
index 52309f3dcbff4d97b34d5f0b3d3b59fb08385d6b..9f59a2977a7d84845a01759a3a68f2da6d719bcb 100644
--- a/scipost_django/submissions/forms/__init__.py
+++ b/scipost_django/submissions/forms/__init__.py
@@ -72,6 +72,7 @@ from ..models import (
     PlagiarismAssessment,
     iThenticateReport,
     EditorialCommunication,
+    RefereeIndication,
 )
 from ..regexes import CHEMRXIV_DOI_PATTERN
 
@@ -3736,3 +3737,83 @@ class iThenticateReportForm(forms.ModelForm):
                 self.add_error(None, msg)
             return None
         return data
+
+
+class RefereeIndicationForm(forms.ModelForm):
+    class Meta:
+        model = RefereeIndication
+        exclude = ["submission", "indicated_by"]
+
+    referee = forms.ModelChoiceField(
+        queryset=Profile.objects.all(),
+        widget=autocomplete.ModelSelect2(url="/profiles/profile-autocomplete"),
+        required=False,
+        help_text="Preferably select a referee from the list. If not found, fill in the other fields.",
+    )
+    reason = forms.CharField(
+        widget=forms.Textarea(attrs={"rows": 4, "maxlength": 255}),
+        help_text="Short reason for this indication; <strong>mandatory when advising against</strong>.",
+        required=False,
+    )
+
+    def __init__(self, *args, **kwargs):
+        self.submission = kwargs.pop("submission")
+        self.user = kwargs.pop("user")
+        super().__init__(*args, **kwargs)
+
+        self.helper = FormHelper()
+        # self.helper.form_tag = False
+        self.helper.layout = Layout(
+            Div(
+                Div(
+                    Div(
+                        Div(Field("indication"), css_class="col-2"),
+                        Div(Field("referee"), css_class="col-10"),
+                        Div(Field("first_name"), css_class="col-6 col-md-2"),
+                        Div(Field("last_name"), css_class="col-6 col-md-2"),
+                        Div(Field("email_address"), css_class="col-6 col-md-4"),
+                        Div(Field("affiliation"), css_class="col-6 col-md-4"),
+                        css_class="row",
+                    ),
+                    css_class="col",
+                ),
+                Div(Field("reason"), css_class="col-12 col-xl-3 h-100"),
+                css_class="row",
+            )
+        )
+
+    def clean(self):
+        cleaned_data = super().clean()
+
+        referee_info_fields = [
+            cleaned_data.get("first_name"),
+            cleaned_data.get("last_name"),
+            cleaned_data.get("email_address"),
+        ]
+
+        if cleaned_data.get("referee") is None and not all(referee_info_fields):
+            self.add_error(
+                None,
+                "If you don't select a referee, you must provide all the necessary information.",
+            )
+
+        if cleaned_data.get("indication") == RefereeIndication.INDICATION_AGAINST:
+            if reason := cleaned_data.get("reason"):
+                self.add_error(
+                    "reason",
+                    "You must provide a reason when indicating against a referee.",
+                )
+            elif len(reason) < 10:
+                self.add_error(
+                    "reason",
+                    "The reason is too short, please provide a more detailed explanation.",
+                )
+
+        return cleaned_data
+
+    def save(self):
+        indication = super().save(commit=False)
+        indication.submission = self.submission
+        indication.indicated_by = self.user.profile
+        indication.save()
+        return indication
diff --git a/scipost_django/submissions/migrations/0156_refereeindication.py b/scipost_django/submissions/migrations/0156_refereeindication.py
new file mode 100644
index 0000000000000000000000000000000000000000..af1b24ab1bdf6a0bceacd1e948709bcedf9e06ff
--- /dev/null
+++ b/scipost_django/submissions/migrations/0156_refereeindication.py
@@ -0,0 +1,78 @@
+# Generated by Django 4.2.10 on 2024-06-21 11:50
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("profiles", "0040_profile_first_name_original_and_more"),
+        ("submissions", "0155_alter_editorialcommunication_comtype"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="RefereeIndication",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "indication",
+                    models.CharField(
+                        choices=[
+                            ("suggest", "Suggest"),
+                            ("advise_against", "Advise against"),
+                        ],
+                        max_length=256,
+                    ),
+                ),
+                ("first_name", models.CharField(blank=True, max_length=64, null=True)),
+                ("last_name", models.CharField(blank=True, max_length=64, null=True)),
+                (
+                    "email_address",
+                    models.EmailField(blank=True, max_length=256, null=True),
+                ),
+                (
+                    "affiliation",
+                    models.CharField(blank=True, max_length=256, null=True),
+                ),
+                ("reason", models.TextField(blank=True, null=True)),
+                (
+                    "indicated_by",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="referee_indications_made",
+                        to="profiles.profile",
+                    ),
+                ),
+                (
+                    "referee",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="referee_indications_received",
+                        to="profiles.profile",
+                    ),
+                ),
+                (
+                    "submission",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="referee_indications",
+                        to="submissions.submission",
+                    ),
+                ),
+            ],
+            options={
+                "unique_together": {("submission", "referee")},
+            },
+        ),
+    ]
diff --git a/scipost_django/submissions/models/__init__.py b/scipost_django/submissions/models/__init__.py
index 778b3ad837a14e8e599f7affd8f26832ba68cfb6..a6dce4b50a95228c1a32a15e88aebd73fcaabb83 100644
--- a/scipost_django/submissions/models/__init__.py
+++ b/scipost_django/submissions/models/__init__.py
@@ -34,3 +34,5 @@ from .report import Report
 from .recommendation import EICRecommendation, AlternativeRecommendation
 
 from .decision import EditorialDecision
+
+from .referee_indication import RefereeIndication
diff --git a/scipost_django/submissions/models/referee_indication.py b/scipost_django/submissions/models/referee_indication.py
new file mode 100644
index 0000000000000000000000000000000000000000..5af0c17c95dcc619fd26c663b9090151d279248e
--- /dev/null
+++ b/scipost_django/submissions/models/referee_indication.py
@@ -0,0 +1,92 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+from typing import TYPE_CHECKING, Literal
+from django.db import models
+
+if TYPE_CHECKING:
+    from .submission import Submission
+    from ...profiles.models import Profile
+
+
+class RefereeIndication(models.Model):
+    """
+    Indication of a professional scientist to referee a Submission.
+
+    The indication may not always be positive, i.e. to suggest a scientist
+    but also to suggest *not* to invite a scientist to referee a Submission.
+
+    The indication must refer to either an existing Profile or a
+    collection of `first_name`, `last_name`, `affiliation` and `email_address` fields.
+    """
+
+    INDICATION_SUGGEST = "suggest"
+    INDICATION_AGAINST = "advise_against"
+    INDICATION_CHOICES = [
+        (INDICATION_SUGGEST, "Suggest"),
+        (INDICATION_AGAINST, "Advise against"),
+    ]
+
+    submission = models.ForeignKey["Submission"](
+        "submissions.Submission",
+        on_delete=models.CASCADE,
+        related_name="referee_indications",
+    )
+    indicated_by = models.ForeignKey["Profile"](
+        "profiles.Profile",
+        related_name="referee_indications_made",
+        on_delete=models.CASCADE,
+    )
+    indication = models.CharField(
+        max_length=256,
+        choices=INDICATION_CHOICES,
+    )
+
+    # Preferable to specify an existing Profile if possible
+    referee = models.ForeignKey["Profile"](
+        "profiles.Profile",
+        related_name="referee_indications_received",
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+    )
+    # if Profile does not exist, their details are stored in the following fields
+    first_name = models.CharField(max_length=64, blank=True, null=True)
+    last_name = models.CharField(max_length=64, blank=True, null=True)
+    email_address = models.EmailField(max_length=256, blank=True, null=True)
+    affiliation = models.CharField(max_length=256, blank=True, null=True)
+
+    # If the indication is negative, it is best to provide a reason
+    reason = models.TextField(blank=True, null=True)
+
+    class Meta:
+        unique_together = ("submission", "referee")
+
+    def __str__(self):
+        referee_name = (
+            self.referee.full_name
+            if self.referee
+            else f"{self.first_name} {self.last_name}"
+        )
+        return f"{self.indicated_by.full_name} to {self.get_indication_display().lower()} {referee_name} for {self.submission}"
+
+    @property
+    def indicated_by_role(self):
+        """
+        Return the role of the Profile that made the indication.
+        - "editor" if the Profile is the submission's Editor
+        - "fellow" if the Profile is a Fellow
+        - "author" if the Profile is an Author
+        - "referee" if the Profile is another (invited) Referee
+        - "other" if the Profile is not any of the above
+        """
+        if self.indicated_by == self.submission.editor_in_charge:
+            return "editor"
+        elif self.indicated_by in self.submission.authors.all():
+            return "author"
+        elif self.indicated_by in self.submission.referee_invitations.all():
+            return "referee"
+        elif self.indicated_by in self.submission.fellows.all():
+            return "fellow"
+        else:
+            return "other"
diff --git a/scipost_django/submissions/models/submission.py b/scipost_django/submissions/models/submission.py
index e21d69ac5e4bdaa7098e2bb3494a40eef5397294..e3766820385e5ff670cc944cd281b06f92f84e8b 100644
--- a/scipost_django/submissions/models/submission.py
+++ b/scipost_django/submissions/models/submission.py
@@ -44,12 +44,14 @@ from ..managers import SubmissionQuerySet, SubmissionEventQuerySet
 from ..refereeing_cycles import ShortCycle, DirectCycle, RegularCycle
 
 if TYPE_CHECKING:
+    from django.db.models.manager import RelatedManager
     from submissions.models import EditorialDecision
     from scipost.models import Contributor
     from journals.models import Journal, Publication
     from proceedings.models import Proceedings
     from iThenticate_report import iThenticateReport
     from ontology.models import AcademicField, Specialty, Topic
+    from ..models.referee_invitation import RefereeInvitation
 
 
 class SubmissionAuthorProfile(models.Model):
@@ -247,6 +249,9 @@ class Submission(models.Model):
     )
     STAGE_IN_REFEREEING_COMPLETED_STATUSES = STAGE_DECISIONMAKING + STAGE_DECIDED
 
+    # Related managers
+    referee_invitations: "RelatedManager[RefereeInvitation]"
+
     # Fields
     preprint = models.OneToOneField(
         "preprints.Preprint", on_delete=models.CASCADE, related_name="submission"