diff --git a/app/public/locales/de/k3k.ftl b/app/public/locales/de/k3k.ftl
index d7208bf7ddecfa64561b7ec7c7c4ab02025f78ad..60142579f7b09838f3719d419c52bff9f18b4365 100644
--- a/app/public/locales/de/k3k.ftl
+++ b/app/public/locales/de/k3k.ftl
@@ -949,7 +949,7 @@ dashboard-meeting-recurrence-daily = Täglich
 dashboard-meeting-recurrence-weekly = Wöchentlich
 dashboard-meeting-recurrence-bi-weekly = 14-Tägig
 dashboard-meeting-recurrence-monthly = Monatlich
-dashboard-meeting-recurrence-custom = Benutzerdefiniert ...
+dashboard-meeting-recurrence-custom = Benutzerdefiniert
 
 dashboard-recurrence-dialog-title = Benutzerdefinierte Meeting-Wiederholung
 dashboard-recurrence-dialog-frequency-label = Wiederholen alle
@@ -978,6 +978,20 @@ dashboard-recurrence-dialog-end-option-on = Am
 dashboard-recurrence-dialog-save-button = Speichern
 dashboard-recurrence-dialog-close-button = Abbrechen
 
+dashboard-meeting-training-participation-report-switch = Teilnahmebericht für Schulungen
+dashboard-meeting-training-participation-report-option-every-thirty-min = Alle 30 Min. nachfragen
+dashboard-meeting-training-participation-report-option-every-sixty-min = Alle 60 Min. nachfragen
+dashboard-meeting-training-participation-report-option-thirty-to-sixty-min = Variabel alle 30 - 60 Min. nachfragen
+dashboard-meeting-training-participation-report-option-ninety-to-hundred-twenty-min = Variabel alle 90 - 120 Min. nachfragen
+dashboard-meeting-training-participation-report-option-custom = Benutzerdefiniert
+
+dashboard-custom-training-participation-report-dialog-title = Benutzerdefinierte Abfragezeiten
+dashboard-custom-training-participation-report-dialog-initial-timeout = Erste Bestätigungsabfrage variabel nach
+dashboard-custom-training-participation-report-dialog-interval-duration = Nachfolgende Abfragen variabel nach
+dashboard-custom-training-participation-report-dialog-to = bis
+dashboard-custom-training-participation-report-dialog-minutes = Minuten
+dashboard-custom-training-participation-report-dialog-error = Der Wert muss größer als {$min} sein
+
 dashboard-payment-status-downgraded = Achtung: Für Ihren Account ist derzeit keine gültige Zahlmethode hinterlegt.<br /> Die Nutzung ist derzeit auf den {$tariffName}-Tarif eingeschränkt.
 dashboard-add-payment-button = Jetzt auswählen
 
@@ -1216,8 +1230,8 @@ timer-not-done-icon-title = Nicht fertig
 participation-confirmation-dialog-title = Bestätigung der Schulungs-Teilnahme
 participation-confirmation-dialog-description = Sie nehmen an einer Schulung oder einem Kurs teil. Für den Teilnahmebericht wird die Anwesenheit wiederholt abgefragt.
 participation-confirmation-dialog-confirm-button = Anwesenheit bestätigen
-training-participation-logging-disable-button = Schulungs-Teilnahmeprotokollierung aktivieren
-training-participation-logging-enable-button = Schulungs-Teilnahmeprotokollierung deaktivieren
+training-participation-logging-disable-button = Schulungs-Teilnahmeprotokollierung deaktivieren
+training-participation-logging-enable-button = Schulungs-Teilnahmeprotokollierung aktivieren
 training-participation-report-pdf-asset-notification = <messageContainer>Der Teilnahmebericht wurde exportiert. Die entsprechende Datei befindet sich im Dashboard unter den <messageLink>Meeting Details</messageLink>.</messageContainer>
 presence-logging-enabled-notification = Schulungs-Teilnahmeprotokollierung aktiviert
 presence-logging-disabled-notification = Schulungs-Teilnahmeprotokollierung deaktiviert
diff --git a/app/public/locales/en/k3k.ftl b/app/public/locales/en/k3k.ftl
index 3c032e6079cc6f80911bdd67a853f0c93ef47ca9..70e0164d47d8e61265a6410cf300f8ac0b363b36 100644
--- a/app/public/locales/en/k3k.ftl
+++ b/app/public/locales/en/k3k.ftl
@@ -949,7 +949,7 @@ dashboard-meeting-recurrence-daily = Daily
 dashboard-meeting-recurrence-weekly = Weekly
 dashboard-meeting-recurrence-bi-weekly = Bi-Weekly
 dashboard-meeting-recurrence-monthly = Monthly
-dashboard-meeting-recurrence-custom = Custom ...
+dashboard-meeting-recurrence-custom = Custom
 
 dashboard-recurrence-dialog-title = Custom meeting repetition
 dashboard-recurrence-dialog-frequency-label = Repeat every
@@ -978,6 +978,20 @@ dashboard-recurrence-dialog-end-option-on = On
 dashboard-recurrence-dialog-save-button = Save
 dashboard-recurrence-dialog-close-button = Cancel
 
+dashboard-meeting-training-participation-report-switch = Training participation report
+dashboard-meeting-training-participation-report-option-every-thirty-min = Ask every 30 min
+dashboard-meeting-training-participation-report-option-every-sixty-min = Ask every 60 min
+dashboard-meeting-training-participation-report-option-thirty-to-sixty-min = Variable ask every 30 - 60 min
+dashboard-meeting-training-participation-report-option-ninety-to-hundred-twenty-min = Variable ask every 90 - 120 min
+dashboard-meeting-training-participation-report-option-custom = Custom
+
+dashboard-custom-training-participation-report-dialog-title = Custom query times
+dashboard-custom-training-participation-report-dialog-initial-timeout = First confirmation query variable after
+dashboard-custom-training-participation-report-dialog-interval-duration = Subsequent queries variable after
+dashboard-custom-training-participation-report-dialog-to = to
+dashboard-custom-training-participation-report-dialog-minutes = minutes
+dashboard-custom-training-participation-report-dialog-error = The value must be greater than {$min}
+
 dashboard-payment-status-downgraded = Attention: There is currently no valid payment method stored for your account.<br /> Currently you are restricted to the {$tariffName} plan.
 dashboard-add-payment-button = Add payment
 
diff --git a/app/src/api/types/outgoing/trainingParticipationReport.ts b/app/src/api/types/outgoing/trainingParticipationReport.ts
index 11486f30a46a063f8fc80f5acc9aeb782d9a5fe5..f2ecc586be7b361b9909d76d8cfb1d60f3e90d4b 100644
--- a/app/src/api/types/outgoing/trainingParticipationReport.ts
+++ b/app/src/api/types/outgoing/trainingParticipationReport.ts
@@ -1,6 +1,8 @@
 // SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
 //
 // SPDX-License-Identifier: EUPL-1.2
+import { TrainingParticipationReportParameterSet } from '@opentalk/rest-api-rtk-query/src/types/event';
+
 import type { RootState } from '../../../store';
 import { createModule, Namespaced } from '../../../types';
 import { createSignalingApiCall } from '../../createSignalingApiCall';
@@ -20,32 +22,12 @@ export enum ParticipationLoggingState {
    */
   WaitingForConfirmation = 'waiting_for_confirmation',
 }
-
 export type ParticipationLogging = {
   state: ParticipationLoggingState;
 };
 
-export interface TimeRange {
-  /**
-   * The earliest number of seconds after which the checkpoint can be created. Must be a strictly positive number.
-   */
-  after: number;
-  /**
-   * The number of seconds within which the checkpoint can be created after the after value. Must be 0 or greater.
-   */
-  within: number;
-}
-
-export interface EnablePresenceLogging {
+export interface EnablePresenceLogging extends Partial<TrainingParticipationReportParameterSet> {
   action: 'enable_presence_logging';
-  /**
-   * default: { "after": 600, "within": 1200 }
-   */
-  initialCheckpointDelay?: TimeRange;
-  /**
-   * default: { "after": 6300, "within": 1800 }
-   */
-  checkpointInterval?: TimeRange;
 }
 
 export interface DisablePresenceLogging {
diff --git a/app/src/components/CreateOrUpdateMeetingForm/CreateOrUpdateMeetingForm.tsx b/app/src/components/CreateOrUpdateMeetingForm/CreateOrUpdateMeetingForm.tsx
index 4daa120069c899536bf3f6e1ee18d804d0faa9e6..8add860387a011c0eaa432fe00e28d0dd65376ac 100644
--- a/app/src/components/CreateOrUpdateMeetingForm/CreateOrUpdateMeetingForm.tsx
+++ b/app/src/components/CreateOrUpdateMeetingForm/CreateOrUpdateMeetingForm.tsx
@@ -18,7 +18,7 @@ import {
 import { Interval, addMinutes, areIntervalsOverlapping, formatRFC3339 } from 'date-fns';
 import { useFormik } from 'formik';
 import { FormikValues } from 'formik/dist/types';
-import { isEmpty } from 'lodash';
+import { isEmpty, isEqual } from 'lodash';
 import { useMemo, useRef, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import { Link, useNavigate } from 'react-router-dom';
@@ -48,6 +48,7 @@ import { CreateOrUpdateMeetingFormikValues, DashboardDateTimePicker } from './fr
 import EventConflictDialog from './fragments/EventConflictDialog';
 import MeetingFormSwitch from './fragments/MeetingFormSwitch';
 import StreamingOptions from './fragments/StreamingOptions';
+import { TrainingParticipationReportSelect } from './fragments/TrainingParticipationReportSelect/TrainingParticipationReportSelect';
 
 interface CreateOrUpdateMeetingFormProps {
   existingEvent?: Event;
@@ -86,6 +87,7 @@ const CreateOrUpdateMeetingForm = ({ existingEvent, onForwardButtonClick }: Crea
 
   const { data: tariff } = useGetMeTariffQuery();
   const isStreamingEnabled = tariff && isFeatureEnabledPredicate('stream', tariff.modules);
+  const isTrainingParticipationReportEnabled = tariff?.modules.trainingParticipationReport;
 
   const navigate = useNavigate();
 
@@ -173,6 +175,25 @@ const CreateOrUpdateMeetingForm = ({ existingEvent, onForwardButtonClick }: Crea
       }),
     }),
     e2eEncryption: yup.boolean().optional(),
+    trainingParticipationReport: yup.object().shape({
+      enabled: yup.boolean().required(),
+      parameter: yup.object().when('enabled', ([enabled]) => {
+        if (!enabled) {
+          return yup.object().optional();
+        }
+
+        return yup.object({
+          initialCheckpointDelay: yup.object().shape({
+            after: yup.number().min(0).required(),
+            within: yup.number().min(0).required(),
+          }),
+          checkpointInterval: yup.object().shape({
+            after: yup.number().min(60).required(),
+            within: yup.number().min(0).required(),
+          }),
+        });
+      }),
+    }),
   });
 
   const recurrenceFrequencyOptions: Array<FrequencyOption> = [
@@ -241,6 +262,27 @@ const CreateOrUpdateMeetingForm = ({ existingEvent, onForwardButtonClick }: Crea
         };
   };
 
+  // const getTPRInitialValue = () => {
+  //   console.debug('value in get initial: ', existingEvent?.trainingParticipationReport);
+  //   return existingEvent?.trainingParticipationReport
+  //     ? {
+  //         enabled: true,
+  //         parameter: existingEvent.trainingParticipationReport,
+  //       }
+  //     : {
+  //         enabled: false,
+  //       };
+  // };
+
+  const TPRValue = existingEvent?.trainingParticipationReport
+    ? {
+        enabled: true,
+        parameter: existingEvent.trainingParticipationReport,
+      }
+    : {
+        enabled: false,
+      };
+
   // We need to pass an empty array, if we want to disable streaming while updating an event
   const getStreamingPayload = (values: FormikValues) => {
     return values.streaming.enabled ? [values.streaming.platform] : [];
@@ -272,6 +314,8 @@ const CreateOrUpdateMeetingForm = ({ existingEvent, onForwardButtonClick }: Crea
       showMeetingDetails: getShowMeetingDetailsInitialValue(),
       streaming: getStreamingInitialValue(),
       e2eEncryption: existingEvent?.room.e2EEncryption || false,
+      // trainingParticipationReport: getTPRInitialValue(),
+      trainingParticipationReport: TPRValue,
     },
     validationSchema,
     validateOnChange: false,
@@ -286,6 +330,25 @@ const CreateOrUpdateMeetingForm = ({ existingEvent, onForwardButtonClick }: Crea
     },
   });
 
+  const createTrainingParticipationReportPayload = () => {
+    const localValue = formik.values.trainingParticipationReport;
+    //null will explicitly force the backend to remove the previous value
+    //undefined will ignore the field in case we do not need any update
+    if (!localValue.enabled) {
+      return existingEvent?.trainingParticipationReport ? null : undefined;
+    }
+
+    if (
+      existingEvent?.trainingParticipationReport &&
+      isEqual(existingEvent.trainingParticipationReport, localValue.parameter)
+    ) {
+      return undefined;
+    }
+
+    //sending the value will create/update accordingly
+    return localValue.parameter;
+  };
+
   const onChangeStartDate = async (date: Date | null) => {
     if (!date) {
       await formik.setFieldValue('startDate', '');
@@ -338,6 +401,7 @@ const CreateOrUpdateMeetingForm = ({ existingEvent, onForwardButtonClick }: Crea
       hasSharedFolder: values.sharedFolder || false,
       streamingTargets: getStreamingPayload(values),
       e2eEncryption: values.e2eEncryption || false,
+      trainingParticipationReport: createTrainingParticipationReportPayload(),
     };
 
     if (values.recurrencePattern) {
@@ -690,6 +754,8 @@ const CreateOrUpdateMeetingForm = ({ existingEvent, onForwardButtonClick }: Crea
 
           {isStreamingEnabled && <StreamingOptions formik={formik} />}
 
+          {isTrainingParticipationReportEnabled && <TrainingParticipationReportSelect formik={formik} />}
+
           {features.e2eEncryption && (
             <MeetingFormSwitch
               checked={formik.values.e2eEncryption}
diff --git a/app/src/components/CreateOrUpdateMeetingForm/fragments/DashboardDateTimePicker.tsx b/app/src/components/CreateOrUpdateMeetingForm/fragments/DashboardDateTimePicker.tsx
index b20433da9942bcfb311d55bbc8b4046dcb1ccc36..7da82f8461212cc8ed197c57198b691296b79b36 100644
--- a/app/src/components/CreateOrUpdateMeetingForm/fragments/DashboardDateTimePicker.tsx
+++ b/app/src/components/CreateOrUpdateMeetingForm/fragments/DashboardDateTimePicker.tsx
@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: EUPL-1.2
 import { Stack } from '@mui/material';
 import { RecurrencePattern, StreamingPlatform } from '@opentalk/rest-api-rtk-query';
+import { TrainingParticipationReportParameterSet } from '@opentalk/rest-api-rtk-query/src/types/event';
 import { FormikProps } from 'formik';
 import { useTranslation } from 'react-i18next';
 
@@ -15,6 +16,10 @@ interface Streaming {
   enabled: boolean;
   streamingTarget?: StreamingPlatform;
 }
+interface TrainingParticipationReport {
+  enabled: boolean;
+  parameter?: TrainingParticipationReportParameterSet;
+}
 export interface CreateOrUpdateMeetingFormikValues {
   title?: string;
   description?: string;
@@ -27,6 +32,7 @@ export interface CreateOrUpdateMeetingFormikValues {
   isAdhoc?: boolean;
   sharedFolder: boolean;
   streaming: Streaming;
+  trainingParticipationReport: TrainingParticipationReport;
   showMeetingDetails: boolean;
   e2eEncryption: boolean;
 }
diff --git a/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/CustomTrainingParticipationReportDialog.test.tsx b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/CustomTrainingParticipationReportDialog.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f5251b828937d91a453ff4ffe09e6a8fd04b7472
--- /dev/null
+++ b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/CustomTrainingParticipationReportDialog.test.tsx
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
+//
+// SPDX-License-Identifier: EUPL-1.2
+import { render, screen } from '@testing-library/react';
+
+import {
+  CustomTrainingParticipationReportDialog,
+  CustomTrainingParticipationReportDialogProps,
+} from './CustomTrainingParticipationReportDialog';
+
+const mockDialogProps: CustomTrainingParticipationReportDialogProps = {
+  closeDialog: jest.fn(),
+  saveOption: jest.fn(),
+  previousOption: {
+    initialCheckpointDelay: { after: 60, within: 60 },
+    checkpointInterval: { after: 120, within: 120 },
+  },
+};
+
+describe('Custom Recurrence Dialog', () => {
+  test('Dialog renders correctly', () => {
+    render(<CustomTrainingParticipationReportDialog {...mockDialogProps} />);
+
+    expect(screen.getByTestId('custom-training-participation-report-dialog')).toBeInTheDocument();
+  });
+});
diff --git a/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/CustomTrainingParticipationReportDialog.tsx b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/CustomTrainingParticipationReportDialog.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..4b7575a6a8dbd1c5b2501614f71bf2f99a0d3164
--- /dev/null
+++ b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/CustomTrainingParticipationReportDialog.tsx
@@ -0,0 +1,168 @@
+// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
+//
+// SPDX-License-Identifier: EUPL-1.2
+import {
+  Button,
+  Dialog,
+  DialogActions,
+  DialogContent as MuiDialogContent,
+  DialogProps,
+  DialogTitle,
+  Stack,
+  styled,
+  Typography,
+} from '@mui/material';
+import { TrainingParticipationReportParameterSet } from '@opentalk/rest-api-rtk-query/src/types/event';
+import { minutesToSeconds, secondsToMinutes } from 'date-fns';
+import { useFormik } from 'formik';
+import { useTranslation } from 'react-i18next';
+
+import { CommonTextField, ErrorFormMessage } from '../../../../commonComponents';
+import { formikNumberFieldProps } from '../../../../utils/formikUtils';
+import yup from '../../../../utils/yupUtils';
+
+export interface CustomTrainingParticipationReportDialogProps extends Omit<DialogProps, 'open'> {
+  closeDialog: () => void;
+  previousOption: TrainingParticipationReportParameterSet;
+  saveOption: (option: TrainingParticipationReportParameterSet) => void;
+}
+
+const CUSTOM_TRAINING_PARTICIPATION_REPORT_DIALOG_LABEL_ID = 'custom-training-participation-report-dialog-title';
+
+const NumberInput = styled(CommonTextField)({
+  maxWidth: '4rem',
+  '& input': {
+    paddingRight: 0,
+    textAlign: 'center',
+  },
+});
+
+const DialogContent = styled(MuiDialogContent)(({ theme }) => ({
+  [theme.breakpoints.up('md')]: {
+    minWidth: theme.typography.pxToRem(500),
+  },
+}));
+
+export const CustomTrainingParticipationReportDialog = ({
+  previousOption,
+  closeDialog,
+  saveOption,
+  ...props
+}: CustomTrainingParticipationReportDialogProps) => {
+  const { t } = useTranslation();
+
+  const validationSchema = yup.object({
+    initialCheckpointDelay: yup.object().shape({
+      from: yup.number().min(0).required(),
+      to: yup.number().when('from', ([from]) => {
+        return yup.number().min(from, t('dashboard-custom-training-participation-report-dialog-error', { min: from }));
+      }),
+    }),
+    checkpointInterval: yup.object().shape({
+      from: yup.number().min(1).required(),
+      to: yup.number().when('from', ([from]) => {
+        return yup.number().min(from, t('dashboard-custom-training-participation-report-dialog-error', { min: from }));
+      }),
+    }),
+  });
+
+  const convertedPreviousOption = {
+    initialCheckpointDelay: {
+      from: secondsToMinutes(previousOption.initialCheckpointDelay.after),
+      to: secondsToMinutes(previousOption.initialCheckpointDelay.after + previousOption.initialCheckpointDelay.within),
+    },
+    checkpointInterval: {
+      from: secondsToMinutes(previousOption.checkpointInterval.after),
+      to: secondsToMinutes(previousOption.checkpointInterval.after + previousOption.checkpointInterval.within),
+    },
+  };
+
+  const formik = useFormik({
+    initialValues: convertedPreviousOption,
+    validationSchema,
+    validateOnChange: true,
+    validateOnBlur: false,
+    onSubmit: ({ initialCheckpointDelay, checkpointInterval }) => {
+      const initialCheckpointDelayAfter = minutesToSeconds(initialCheckpointDelay.from);
+      const initialCheckpointDelayWithin = minutesToSeconds(initialCheckpointDelay.to - initialCheckpointDelay.from);
+      const checkpointIntervalAfter = minutesToSeconds(checkpointInterval.from);
+      const checkpointIntervalWithin = minutesToSeconds(checkpointInterval.to - checkpointInterval.from);
+
+      saveOption({
+        initialCheckpointDelay: { after: initialCheckpointDelayAfter, within: initialCheckpointDelayWithin },
+        checkpointInterval: { after: checkpointIntervalAfter, within: checkpointIntervalWithin },
+      });
+
+      closeDialog();
+    },
+  });
+
+  const closeWithReset = () => {
+    closeDialog();
+    formik.resetForm();
+  };
+
+  return (
+    <Dialog
+      {...props}
+      open
+      onClose={closeWithReset}
+      aria-labelledby={CUSTOM_TRAINING_PARTICIPATION_REPORT_DIALOG_LABEL_ID}
+      data-testid="custom-training-participation-report-dialog"
+    >
+      <DialogTitle id={CUSTOM_TRAINING_PARTICIPATION_REPORT_DIALOG_LABEL_ID}>
+        {t('dashboard-custom-training-participation-report-dialog-title')}
+      </DialogTitle>
+      <DialogContent>
+        <Stack marginBottom={2}>
+          <Typography>{t('dashboard-custom-training-participation-report-dialog-initial-timeout')}</Typography>
+          <Stack direction="row" spacing={1} alignItems="center">
+            <NumberInput
+              {...formikNumberFieldProps('initialCheckpointDelay.from', formik)}
+              type="number"
+              slotProps={{ htmlInput: { min: 0 } }}
+            />
+            <Typography>{t('dashboard-custom-training-participation-report-dialog-to')}</Typography>
+            <NumberInput
+              {...formikNumberFieldProps('initialCheckpointDelay.to', formik)}
+              type="number"
+              slotProps={{ htmlInput: { min: formik.values.initialCheckpointDelay.from } }}
+            />
+            <Typography>{t('dashboard-custom-training-participation-report-dialog-minutes')}</Typography>
+          </Stack>
+          {formik.errors.initialCheckpointDelay?.to && (
+            <ErrorFormMessage helperText={formik.errors.initialCheckpointDelay.to} />
+          )}
+        </Stack>
+        <Stack>
+          <Typography>{t('dashboard-custom-training-participation-report-dialog-interval-duration')}</Typography>
+          <Stack direction="row" spacing={1} alignItems="center">
+            <NumberInput
+              {...formikNumberFieldProps('checkpointInterval.from', formik)}
+              type="number"
+              slotProps={{ htmlInput: { min: 1 } }}
+            />
+            <Typography>{t('dashboard-custom-training-participation-report-dialog-to')}</Typography>
+            <NumberInput
+              {...formikNumberFieldProps('checkpointInterval.to', formik)}
+              type="number"
+              slotProps={{ htmlInput: { min: formik.values.checkpointInterval.from } }}
+            />
+            <Typography>{t('dashboard-custom-training-participation-report-dialog-minutes')}</Typography>
+          </Stack>
+          {formik.errors.checkpointInterval?.to && (
+            <ErrorFormMessage helperText={formik.errors.checkpointInterval.to} />
+          )}
+        </Stack>
+      </DialogContent>
+      <DialogActions>
+        <Button variant="contained" color="secondary" onClick={closeWithReset}>
+          {t('global-close')}
+        </Button>
+        <Button variant="contained" onClick={() => formik.handleSubmit()}>
+          {t('global-save')}
+        </Button>
+      </DialogActions>
+    </Dialog>
+  );
+};
diff --git a/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/TrainingParticipationReportSelect.test.tsx b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/TrainingParticipationReportSelect.test.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..783303a36b26d831d147e3583ac5c4d0ce280c66
--- /dev/null
+++ b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/TrainingParticipationReportSelect.test.tsx
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
+//
+// SPDX-License-Identifier: EUPL-1.2
+import { RecurrencePattern } from '@opentalk/rest-api-rtk-query';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { Formik } from 'formik';
+
+import { TrainingParticipationReportSelect } from './TrainingParticipationReportSelect';
+
+const initialValues = {
+  trainingParticipationReport: {
+    enabled: true,
+  },
+  waitingRoom: false,
+  isTimeDependent: false,
+  startDate: '',
+  endDate: '',
+  recurrencePattern: '' as RecurrencePattern,
+  sharedFolder: false,
+  streaming: {
+    enabled: false,
+  },
+  showMeetingDetails: false,
+  e2eEncryption: false,
+};
+
+describe('Training participation report select', () => {
+  test('Select is not rendered when not enabled', async () => {
+    render(
+      <Formik initialValues={{ ...initialValues, trainingParticipationReport: { enabled: false } }} onSubmit={() => {}}>
+        {(formikProps) => <TrainingParticipationReportSelect formik={formikProps} />}
+      </Formik>
+    );
+
+    const selectButton = screen.queryByRole('combobox');
+    expect(selectButton).not.toBeInTheDocument();
+  });
+
+  test('Select is rendered and usable when enabled', () => {
+    render(
+      <Formik initialValues={initialValues} onSubmit={() => {}}>
+        {(formikProps) => <TrainingParticipationReportSelect formik={formikProps} />}
+      </Formik>
+    );
+
+    const selectButton = screen.getByRole('combobox');
+    expect(selectButton).toBeInTheDocument();
+
+    selectButton && fireEvent.mouseDown(selectButton);
+
+    const listbox = screen.getByRole('listbox');
+    expect(listbox).toBeInTheDocument();
+
+    const sixtyMinOption = screen.getByRole('option', {
+      name: 'dashboard-meeting-training-participation-report-option-every-sixty-min',
+    });
+
+    fireEvent.click(sixtyMinOption);
+
+    const select = screen.getByTestId('parameter-select').textContent?.replace(/\u200B/g, '');
+    expect(select).toBe('dashboard-meeting-training-participation-report-option-every-sixty-min');
+  });
+});
diff --git a/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/TrainingParticipationReportSelect.tsx b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/TrainingParticipationReportSelect.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..963abc09d7f17f2dbc314afdbb30fa4788be8e94
--- /dev/null
+++ b/app/src/components/CreateOrUpdateMeetingForm/fragments/TrainingParticipationReportSelect/TrainingParticipationReportSelect.tsx
@@ -0,0 +1,129 @@
+// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
+//
+// SPDX-License-Identifier: EUPL-1.2
+import { Collapse, MenuItem, Stack } from '@mui/material';
+import { TrainingParticipationReportParameterSet } from '@opentalk/rest-api-rtk-query/src/types/event';
+import { FormikProps } from 'formik';
+import { isEqual } from 'lodash';
+import { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { CommonTextField } from '../../../../commonComponents';
+import { formikMinimalProps } from '../../../../utils/formikUtils';
+import { CreateOrUpdateMeetingFormikValues } from '../DashboardDateTimePicker';
+import MeetingFormSwitch from '../MeetingFormSwitch';
+import { CustomTrainingParticipationReportDialog } from './CustomTrainingParticipationReportDialog';
+
+interface TrainingParticipationReportSelectProps {
+  formik: FormikProps<CreateOrUpdateMeetingFormikValues>;
+}
+
+enum TrainingParticipationReportConfigOptions {
+  EveryThirtyMin = 'every-thirty-min',
+  EverySixtyMin = 'every-sixty-min',
+  ThirtyToSixtyMin = 'thirty-to-sixty-min',
+  NinetyToOneHundredTwentyMin = 'ninety-to-hundred-twenty-min',
+  Custom = 'custom',
+}
+
+export const TrainingParticipationReportSelect = ({ formik }: TrainingParticipationReportSelectProps) => {
+  const { t } = useTranslation();
+  const { enabled, parameter } = formik.values.trainingParticipationReport;
+  const [isCustomDialogOpen, setIsCustomDialogOpen] = useState(false);
+  const [customOption, setCustomOption] = useState({
+    initialCheckpointDelay: { after: 600, within: 1200 },
+    checkpointInterval: { after: 2700, within: 900 },
+  });
+
+  const options: Record<TrainingParticipationReportConfigOptions, TrainingParticipationReportParameterSet> = {
+    [TrainingParticipationReportConfigOptions.EveryThirtyMin]: {
+      initialCheckpointDelay: { after: 1800, within: 0 },
+      checkpointInterval: { after: 1800, within: 0 },
+    },
+    [TrainingParticipationReportConfigOptions.EverySixtyMin]: {
+      initialCheckpointDelay: { after: 3600, within: 0 },
+      checkpointInterval: { after: 3600, within: 0 },
+    },
+    [TrainingParticipationReportConfigOptions.ThirtyToSixtyMin]: {
+      initialCheckpointDelay: { after: 1800, within: 1800 },
+      checkpointInterval: { after: 1800, within: 1800 },
+    },
+    [TrainingParticipationReportConfigOptions.NinetyToOneHundredTwentyMin]: {
+      initialCheckpointDelay: { after: 5400, within: 1800 },
+      checkpointInterval: { after: 5400, within: 1800 },
+    },
+    [TrainingParticipationReportConfigOptions.Custom]: customOption,
+  };
+
+  const [selectedOption, setSelectedOption] = useState(TrainingParticipationReportConfigOptions.EveryThirtyMin);
+
+  useEffect(() => {
+    if (!enabled) {
+      return;
+    }
+
+    if (parameter) {
+      const existingOptionKey = (Object.keys(options) as Array<TrainingParticipationReportConfigOptions>).find(
+        (option) => isEqual(parameter, options[option])
+      );
+      if (existingOptionKey) {
+        setSelectedOption(existingOptionKey);
+      } else {
+        setSelectedOption(TrainingParticipationReportConfigOptions.Custom);
+        setCustomOption(parameter);
+      }
+      return;
+    }
+
+    formik.setFieldValue(
+      'trainingParticipationReport.parameter',
+      options[TrainingParticipationReportConfigOptions.EveryThirtyMin]
+    );
+  }, [enabled]);
+
+  const handleSelect = (option: TrainingParticipationReportConfigOptions) => {
+    if (option === TrainingParticipationReportConfigOptions.Custom) {
+      setIsCustomDialogOpen(true);
+    }
+
+    setSelectedOption(option);
+    formik.setFieldValue('trainingParticipationReport.parameter', options[option]);
+  };
+
+  const handleSave = (value: TrainingParticipationReportParameterSet) => {
+    setCustomOption(value);
+    formik.setFieldValue('trainingParticipationReport.parameter', value);
+    setIsCustomDialogOpen(false);
+  };
+
+  return (
+    <Stack spacing={2}>
+      <MeetingFormSwitch
+        switchProps={formikMinimalProps('trainingParticipationReport.enabled', formik)}
+        checked={enabled}
+        switchValueLabel={t('dashboard-meeting-training-participation-report-switch')}
+      />
+      <Collapse orientation="vertical" in={enabled} unmountOnExit mountOnEnter>
+        <CommonTextField data-testid="parameter-select" select value={selectedOption}>
+          {Object.keys(options).map((option) => (
+            <MenuItem
+              key={option}
+              value={option}
+              onClick={() => handleSelect(option as TrainingParticipationReportConfigOptions)}
+            >
+              {t(`dashboard-meeting-training-participation-report-option-${option}`)}
+            </MenuItem>
+          ))}
+        </CommonTextField>
+      </Collapse>
+
+      {isCustomDialogOpen && (
+        <CustomTrainingParticipationReportDialog
+          closeDialog={() => setIsCustomDialogOpen(false)}
+          previousOption={customOption}
+          saveOption={handleSave}
+        />
+      )}
+    </Stack>
+  );
+};
diff --git a/app/src/utils/formikUtils.ts b/app/src/utils/formikUtils.ts
index c4391266757dd1239258a9aa6c9f90d51ff15741..3b4f19424bb90a2dd86975c281f12bd0eba238eb 100644
--- a/app/src/utils/formikUtils.ts
+++ b/app/src/utils/formikUtils.ts
@@ -171,3 +171,33 @@ export function formikDurationFieldProps<Values>(
     helperText: (hasError && (errorMessage as string)) || undefined,
   };
 }
+
+export function formikNumberFieldProps<Values>(
+  fieldName: string,
+  formik: FormikProps<Values>,
+  /**
+   * Duration value in minutes
+   *
+   * Default: 1
+   */
+  defaultValue?: number
+) {
+  const { values, handleBlur, handleChange } = formik;
+
+  const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    const nextValue = parseInt(event.target.value);
+    if (Number.isNaN(nextValue)) {
+      handleChange({ ...event, target: { ...event.target, value: defaultValue } });
+      return;
+    }
+
+    handleChange(event);
+  };
+
+  return {
+    name: fieldName,
+    onChange: onChange,
+    onBlur: handleBlur,
+    value: get(values, fieldName, defaultValue ?? 1)?.toString(),
+  };
+}
diff --git a/packages/rtk-rest-api/src/types/event.ts b/packages/rtk-rest-api/src/types/event.ts
index a669e8b148a42b6f17af05a54fb33056a351a610..b000bae64854fdcc6a8a53f94c32af81c8f7fb13 100644
--- a/packages/rtk-rest-api/src/types/event.ts
+++ b/packages/rtk-rest-api/src/types/event.ts
@@ -34,6 +34,27 @@ export interface SharedFolderData {
   readWrite?: SharedFolderCredentials;
 }
 
+export interface TimeRange {
+  /**
+   * The earliest number of seconds after which the checkpoint can be created. Must be 0 or greater.
+   */
+  after: number;
+  /**
+   * The number of seconds within which the checkpoint can be created after the after value. Must be 0 or greater.
+   */
+  within: number;
+}
+export interface TrainingParticipationReportParameterSet {
+  /**
+   * default: { "after": 600, "within": 1200 }
+   */
+  initialCheckpointDelay: TimeRange;
+  /**
+   * default: { "after": 6300, "within": 1800 }
+   */
+  checkpointInterval: TimeRange;
+}
+
 /**
  * EventRoomInfo in an Event object
  */
@@ -83,6 +104,7 @@ export interface CreateBaseEventPayload {
   showMeetingDetails?: boolean;
   hasSharedFolder?: boolean;
   streamingTargets?: Array<StreamingPlatform>;
+  trainingParticipationReport?: TrainingParticipationReportParameterSet;
 }
 
 /**
@@ -156,6 +178,7 @@ export interface UpdateEventPayload {
   showMeetingDetails?: boolean;
   hasSharedFolder?: boolean;
   streamingTargets?: Array<StreamingPlatform>;
+  trainingParticipationReport?: TrainingParticipationReportParameterSet | null;
 }
 
 /**
@@ -228,6 +251,7 @@ interface AbstractEvent extends BaseEvent {
   sharedFolder?: SharedFolderData;
   showMeetingDetails?: boolean;
   streamingTargets?: Array<StreamingPlatform>;
+  trainingParticipationReport?: TrainingParticipationReportParameterSet;
 }
 
 /**