diff --git a/docs/decisions/0015-default-enrollments.rst b/docs/decisions/0015-default-enrollments.rst
new file mode 100644
index 000000000..4b1c899a0
--- /dev/null
+++ b/docs/decisions/0015-default-enrollments.rst
@@ -0,0 +1,87 @@
+==============================
+Default Enterprise Enrollments
+==============================
+
+Status
+======
+Proposed - October 2024
+
+Context
+=======
+Enterprise needs a solution for managing automated enrollments into "default" courses or specific course runs
+for enterprise learners. Importantly, we need this solution to work for learners where we have no information
+about the identity of learner who will eventually be associated with an ``EnterpriseCustomer``. For that reason,
+the solution described below does not involve ``PendingEnterpriseCourseEnrollments`` or anything similar -
+that model/domain depends on knowing the email address of learners prior to their becoming associated with the customer.
+The solution also needs to support customer-specific enrollment configurations
+without tightly coupling these configurations to a specific subsidy type, i.e. we should be able in the future
+to manage default enrollments via both learner credit and subscription subsidy types.
+
+Core requirements
+-----------------
+1. Internal staff should be able to configure one or more default enrollments with either a course
+   or a specific course run for automatic enrollment. In the case of specifying a course,
+   the default enrollment flow should cause the "realization" of default enrollments for learners
+   into the currently-advertised, enrollable run for a course.
+2. Default Enrollments should be loosely coupled to subsidy type.
+3. Unenrollment: If a learner chooses to unenroll from a default course, they should not be automatically re-enrolled.
+4. Graceful handling of license revocation: Upon license revocation, we currently downgrade the learner’s
+   enrollment mode to ``audit``. This fact should be visible from any new APIs exposed
+   in the domain of default enrollments.
+5. Non-enrollable courses: If a course becomes unenrollable, our intent is that default enrollments for such
+   a course no longer are processed. Ideally this happens in a way that is observable to operators of the system.
+
+Decision
+========
+We will implement two new models:
+* ``DefaultEnterpriseEnrollmentIntention`` to represent the course/runs that
+  learners should be automatically enrolled into, post-logistration, for a given enterprise.
+* ``DefaultEnterpriseEnrollmentRealization``which represents the mapping between the intention
+  and actual, **realized** enrollment record(s) for the learner/customer.
+
+Qualities
+---------
+1. Flexibility: The ``DefaultEnterpriseEnrollmentIntention`` model will allow specification of either a course
+   or course run.
+2. Business logic: The API for this domain (future ADR) will implement the business logic around choosing
+   the appropriate course run, for answering which if any catalogs are applicable to the course,
+   and the enrollability of the course (the last of which takes into account the state of existing enrollment records).
+3. Non-Tightly Coupled to subsidy type: Nothing in the domain of default enrollments will persist data
+   related to a subsidy (although a license or transaction identifier will ultimately become associated with
+   an ``EnterpriseCourseEnrollment`` record during realization).
+
+Flexible content keys on the intention model
+--------------------------------------------
+The ``content_key`` on ``DefaultEnterpriseEnrollmentIntention`` is either a top-level course key
+or a course run key during configuration to remain flexible for internal operators;
+however, we will always discern the correct course run key to use for enrollment based on the provided ``content_key``.
+
+Post-enrollment related models (e.g., ``EnterpriseCourseEnrollment`` and ``DefaultEnterpriseEnrollmentRealization``)
+will always primarily be associated with the course run associated with the ``DefaultEnterpriseEnrollmentIntention``:
+* If content_key is a top-level course, the course run key used when enrolling
+  (converting to ``EnterpriseCourseEnrollment`` and ``DefaultEnterpriseEnrollmentRealization``)
+  is the currently advertised course run.
+* If the content_key is a specific course run, we'll always try to enroll in the explicitly
+  configured course run key (not the advertised course run).
+
+This content_key will be passed to the ``EnterpriseCatalogApiClient().get_content_metadata_content_identifier``
+method. When passing a course run key to this endpoint, it'll actually return the top-level *course* metadata
+associated with the course run, not just the specified course run's metadata
+(this is primarily due to catalogs containing courses, not course runs, and we generally say that
+if the top-level course is in a customer's catalog, all of its course runs are, too).
+
+If the ``content_key`` configured for a ``DefaultEnterpriseEnrollmentIntention`` is a top-level course,
+there is a chance the currently advertised course run used for future enrollment intentions might
+change over time from previous redeemed/enrolled ``DefaultEnterpriseEnrollmentIntentions``.
+However, this is mitigated in that the ``DefaultEnterpriseEnrollmentRealization``
+ensures the resulting, converted enrollment is still related to the original ``DefaultEnterpriseEnrollmentIntention``,
+despite a potential content_key vs. enrollment course run key mismatch.
+
+Consequences
+============
+1. It's a flexible design.
+2. It relies on a network call(s) to enterprise-catalog to fetch content metadata and understand which if any customer
+   catalog are applicable to the indended course (we can use caching to make this efficient).
+3. We're introducing more complexity in terms of how subsidized enterprise enrollments
+   can come into existence.
+4. The realization model makes the provenance of default enrollments explicit and easy to examine.
diff --git a/enterprise/admin/__init__.py b/enterprise/admin/__init__.py
index 0376f0e9b..70058ffb5 100644
--- a/enterprise/admin/__init__.py
+++ b/enterprise/admin/__init__.py
@@ -1316,3 +1316,34 @@ class LearnerCreditEnterpriseCourseEnrollmentAdmin(admin.ModelAdmin):
     class Meta:
         fields = '__all__'
         model = models.LearnerCreditEnterpriseCourseEnrollment
+
+
+@admin.register(models.DefaultEnterpriseEnrollmentIntention)
+class DefaultEnterpriseEnrollmentIntentionAdmin(admin.ModelAdmin):
+    """
+    Django admin model for DefaultEnterpriseEnrollmentIntentions.
+    """
+    list_display = (
+        'uuid',
+        'enterprise_customer',
+        'content_type',
+        'content_key',
+    )
+
+    readonly_fields = (
+        'current_course_run_key',
+        'current_course_run_enrollable',
+        'current_course_run_enroll_by_date',
+    )
+
+    search_fields = (
+        'uuid',
+        'enterprise_customer__uuid',
+        'content_key',
+    )
+
+    ordering = ('-modified',)
+
+    class Meta:
+        fields = '__all__'
+        model = models.DefaultEnterpriseEnrollmentIntention
diff --git a/enterprise/migrations/0223_default_enrollments.py b/enterprise/migrations/0223_default_enrollments.py
new file mode 100644
index 000000000..9863b3f34
--- /dev/null
+++ b/enterprise/migrations/0223_default_enrollments.py
@@ -0,0 +1,99 @@
+# Generated by Django 4.2.15 on 2024-10-10 15:02
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import model_utils.fields
+import simple_history.models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('enterprise', '0222_alter_enterprisegroup_group_type_and_more'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='DefaultEnterpriseEnrollmentIntention',
+            fields=[
+                ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
+                ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
+                ('is_removed', models.BooleanField(default=False)),
+                ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+                ('content_type', models.CharField(choices=[('course', 'Course'), ('course_run', 'Course Run')], help_text='The type of content (e.g. a course vs. a course run).', max_length=127)),
+                ('content_key', models.CharField(help_text='A course or course run that related users should be automatically enrolled into.', max_length=255)),
+                ('enterprise_customer', models.ForeignKey(help_text='The customer for which this default enrollment will be realized.', on_delete=django.db.models.deletion.CASCADE, related_name='default_course_enrollments', to='enterprise.enterprisecustomer')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='HistoricalDefaultEnterpriseEnrollmentRealization',
+            fields=[
+                ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
+                ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
+                ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
+                ('history_id', models.AutoField(primary_key=True, serialize=False)),
+                ('history_date', models.DateTimeField()),
+                ('history_change_reason', models.CharField(max_length=100, null=True)),
+                ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
+                ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
+                ('intended_enrollment', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='enterprise.defaultenterpriseenrollmentintention')),
+                ('realized_enrollment', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='enterprise.enterprisecourseenrollment')),
+            ],
+            options={
+                'verbose_name': 'historical default enterprise enrollment realization',
+                'verbose_name_plural': 'historical default enterprise enrollment realizations',
+                'ordering': ('-history_date', '-history_id'),
+                'get_latest_by': ('history_date', 'history_id'),
+            },
+            bases=(simple_history.models.HistoricalChanges, models.Model),
+        ),
+        migrations.CreateModel(
+            name='HistoricalDefaultEnterpriseEnrollmentIntention',
+            fields=[
+                ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
+                ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
+                ('is_removed', models.BooleanField(default=False)),
+                ('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)),
+                ('content_type', models.CharField(choices=[('course', 'Course'), ('course_run', 'Course Run')], help_text='The type of content (e.g. a course vs. a course run).', max_length=127)),
+                ('content_key', models.CharField(help_text='A course or course run that related users should be automatically enrolled into.', max_length=255)),
+                ('history_id', models.AutoField(primary_key=True, serialize=False)),
+                ('history_date', models.DateTimeField()),
+                ('history_change_reason', models.CharField(max_length=100, null=True)),
+                ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
+                ('enterprise_customer', models.ForeignKey(blank=True, db_constraint=False, help_text='The customer for which this default enrollment will be realized.', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='enterprise.enterprisecustomer')),
+                ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'verbose_name': 'historical default enterprise enrollment intention',
+                'verbose_name_plural': 'historical default enterprise enrollment intentions',
+                'ordering': ('-history_date', '-history_id'),
+                'get_latest_by': ('history_date', 'history_id'),
+            },
+            bases=(simple_history.models.HistoricalChanges, models.Model),
+        ),
+        migrations.CreateModel(
+            name='DefaultEnterpriseEnrollmentRealization',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
+                ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
+                ('intended_enrollment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enterprise.defaultenterpriseenrollmentintention')),
+                ('realized_enrollment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='enterprise.enterprisecourseenrollment')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.AddField(
+            model_name='defaultenterpriseenrollmentintention',
+            name='realized_enrollments',
+            field=models.ManyToManyField(through='enterprise.DefaultEnterpriseEnrollmentRealization', to='enterprise.enterprisecourseenrollment'),
+        ),
+    ]
diff --git a/enterprise/models.py b/enterprise/models.py
index b1c5adebe..d421ed834 100644
--- a/enterprise/models.py
+++ b/enterprise/models.py
@@ -2461,6 +2461,103 @@ class LicensedEnterpriseCourseEnrollment(EnterpriseFulfillmentSource):
     )
 
 
+class DefaultEnterpriseEnrollmentIntention(TimeStampedModel, SoftDeletableModel):
+    """
+    Specific to an enterprise customer, this model defines a course or course run
+    that should be auto-enrolled for any enterprise customer user linked to the customer.
+
+    .. no_pii:
+    """
+    DEFAULT_ENROLLMENT_CONTENT_TYPE_CHOICES = [
+        ('course', 'Course'),
+        ('course_run', 'Course Run'),
+    ]
+    uuid = models.UUIDField(
+        primary_key=True,
+        default=uuid4,
+        editable=False,
+    )
+    enterprise_customer = models.ForeignKey(
+        EnterpriseCustomer,
+        blank=False,
+        null=False,
+        related_name="default_course_enrollments",
+        on_delete=models.deletion.CASCADE,
+        help_text=_(
+            "The customer for which this default enrollment will be realized.",
+        )
+    )
+    content_type = models.CharField(
+        max_length=127,
+        blank=False,
+        null=False,
+        choices=DEFAULT_ENROLLMENT_CONTENT_TYPE_CHOICES,
+        help_text=_(
+            "The type of content (e.g. a course vs. a course run)."
+        ),
+    )
+    content_key = models.CharField(
+        max_length=255,
+        blank=False,
+        null=False,
+        help_text=_(
+            "A course or course run that related users should be automatically enrolled into."
+        ),
+    )
+    realized_enrollments = models.ManyToManyField(
+        EnterpriseCourseEnrollment,
+        through='DefaultEnterpriseEnrollmentRealization',
+        through_fields=("intended_enrollment", "realized_enrollment"),
+    )
+    history = HistoricalRecords()
+
+    @cached_property
+    def current_course_run(self):  # pragma: no cover
+        """
+        Metadata describing the current course run for this default enrollment intention.
+        """
+        return {}
+
+    @property
+    def current_course_run_key(self):  # pragma: no cover
+        """
+        The current course run key to use for realized course enrollments.
+        """
+        return self.current_course_run.get('key')
+
+    @property
+    def current_course_run_enrollable(self):  # pragma: no cover
+        """
+        Whether the current course run is enrollable.
+        """
+        return False
+
+    @property
+    def current_course_run_enroll_by_date(self):  # pragma: no cover
+        """
+        The enrollment deadline for this course.
+        """
+        return datetime.datetime.min
+
+
+class DefaultEnterpriseEnrollmentRealization(TimeStampedModel):
+    """
+    Represents the relationship between a `DefaultEnterpriseEnrollmentIntention`
+    and a realized course enrollment that exists because of that intention record.
+
+    .. no_pii:
+    """
+    intended_enrollment = models.ForeignKey(
+        DefaultEnterpriseEnrollmentIntention,
+        on_delete=models.CASCADE,
+    )
+    realized_enrollment = models.ForeignKey(
+        EnterpriseCourseEnrollment,
+        on_delete=models.CASCADE,
+    )
+    history = HistoricalRecords()
+
+
 class EnterpriseCatalogQuery(TimeStampedModel):
     """
     Stores a re-usable catalog query.
diff --git a/tests/test_utilities.py b/tests/test_utilities.py
index 6cd38d212..8acd1c564 100644
--- a/tests/test_utilities.py
+++ b/tests/test_utilities.py
@@ -182,6 +182,7 @@ def setUp(self):
                 "enable_one_academy",
                 "show_videos_in_learner_portal_search_results",
                 "groups",
+                "default_course_enrollments",
             ]
         ),
         (