diff --git a/src/core/management/commands/create_conference.py b/src/core/management/commands/create_conference.py
index 60ca31482bbd1df8b99e6c3246820f572d983512..e7dccc788fa2fc474d1a34bbbb53724ea23175f8 100644
--- a/src/core/management/commands/create_conference.py
+++ b/src/core/management/commands/create_conference.py
@@ -1,11 +1,16 @@
+from argparse import ArgumentTypeError, BooleanOptionalAction
 from datetime import datetime
 
 from zoneinfo import ZoneInfo
 
+from django.contrib.auth.models import Group
 from django.core.management import call_command
-from django.core.management.base import BaseCommand
+from django.core.management.base import BaseCommand, CommandError
+from django.utils.text import slugify
 
-from core.models import Assembly, Conference, StaticPage, StaticPageNamespace
+from core.models.assemblies import Assembly
+from core.models.conference import Conference
+from core.models.pages import StaticPage, StaticPageNamespace
 
 NS_INTRO = '_intro_'
 INTRO_PAGES = {
@@ -72,7 +77,7 @@ INTRO_PAGES = {
 }
 
 
-def seed_conference(conf: Conference, year: int = 0, force: bool = False):
+def seed_conference(conf: Conference, year: int = 0):
     if Assembly.objects.filter(conference=conf).exists():
         raise ValueError('Conference already has assemblies?!')
 
@@ -328,27 +333,93 @@ def seed_conference(conf: Conference, year: int = 0, force: bool = False):
         )
 
 
+def _validate_date(s: str) -> datetime:
+    try:
+        return datetime.strptime(s, '%Y')  # noqa: DTZ007
+    except ValueError as exc:
+        raise ArgumentTypeError(f"Not a valid date: {s!r}. Use format 'YYYY'") from exc
+
+
 class Command(BaseCommand):
     def add_arguments(self, parser):
-        parser.add_argument('slug', type=str, nargs='?')
-        parser.add_argument('--name', type=str)
-        parser.add_argument('--year', type=int)
-        parser.add_argument('--bootstrap', action='store_true')
+        parser.add_argument(
+            'name',
+            type=str,
+            nargs='?',
+            help='The name of the new conference',
+        )
+        parser.add_argument(
+            '-s',
+            '--slug',
+            type=str,
+            help='The url slug of the new conference. If not given, it will be slugified from the name.',
+            default=None,
+        )
+        parser.add_argument(
+            '-y',
+            '--year',
+            type=_validate_date,
+            help=(
+                'The year of the CCC congress. If not given, it will use the current year.'
+                ' Setting this to 0 will skip any year-specific setup (e.g., nav entries).'
+            ),
+            default=None,
+        )
+        parser.add_argument(
+            '--bootstrap',
+            action=BooleanOptionalAction,
+            help=("Initialize the conference permission groups (e.g. 'Assembly-Teams'). If not given, execution is determined by group presence in database."),
+        )
+        parser.add_argument('-i', '--interactive', action='store_true', help='Ask for missing arguments', default=False)
 
     def handle(self, *args, **options):
-        if not (slug := options.get('slug')):
-            slug = input('SLUG of the new conference, e.g. "42c3": ')
+        interactive = options.get('interactive', False)
+        bootstrap = options.get('bootstrap')
         if not (name := options.get('name')):
+            if not interactive:
+                self.print_help('manage.py', 'create_conference')
+                raise CommandError('Missing required argument: name. Use --interactive to be asked for missing arguments.')
             name = input('NAME of the new conference (long title): ')
-        if not (year := options.get('year')):
-            year = input("YEAR of the CCC congress (leave empty if it's something else): ")
-        year = int(year or '0')
-
+        if not (slug := options.get('slug')) and interactive:
+            slug = input('SLUG of the new conference, e.g. "42c3": ')
+        elif not (slug := options.get('slug')):
+            slug = slugify(name)
+        if not (year := options.get('year')) and interactive:
+            year_valid = False
+            while not year_valid:
+                year = input("YEAR of the CCC congress (leave empty if it's something else): ")
+                try:
+                    year = datetime.strptime(year, '%Y')  # noqa: DTZ007
+                    year_valid = True
+                except ValueError:
+                    self.stderr.write('Invalid year format. Please use YYYY.')
+        elif not (year := options.get('year')):
+            year = datetime.now()  # noqa: DTZ005
+        year = year.year
         print(f'Creating conference {slug} ({name}) for year {year}')
 
+        # check if the conference already exists
+        if Conference.objects.filter(slug=slug).exists():
+            raise CommandError(f'Conference with slug "{slug}" already exists.')
+
+        if bootstrap and Group.objects.exists():
+            answer = None
+            while answer not in ['y', 'n', '']:
+                answer = input('Groups are already present in the database. Do you still want to bootstrap? (y/[n]) ')
+
+            bootstrap = answer.lower() == 'y'
+            if bootstrap:
+                self.stdout.write(self.style.WARNING('Groups will be loaded and may be duplicated!'))
+            else:
+                self.stdout.write(self.style.WARNING('Bootstrap will not be applied.'))
+
         # load our bootstrap fixtures
-        if options.get('bootstrap'):
+        if bootstrap or (bootstrap is None and not Group.objects.exists()):
+            self.stdout.write('Loading bootstrap data...')
             call_command('loaddata', 'bootstrap_auth_groups.json')
+            self.stdout.write(self.style.SUCCESS('Bootstrap data loaded.'))
+        elif bootstrap is None and Group.objects.exists():
+            self.stdout.write(self.style.SUCCESS('Groups are already present in the database.'))
 
         # create the new conference
         conf = Conference.objects.create(