diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
index 7f96c23967989239d6440071ed1a6fc55d4d1052..02349f01add8910e22f6cdfdfb16d99ad303ffb5 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue
@@ -12,11 +12,13 @@ import PersonalNotes from "../personal_notes/PersonalNotes.vue";
 import ExtraMarkChip from "../../extra_marks/ExtraMarkChip.vue";
 import TardinessChip from "./TardinessChip.vue";
 import TardinessField from "./TardinessField.vue";
+import ExtraMarkButtons from "../../extra_marks/ExtraMarkButtons.vue";
 
 export default {
   name: "ManageStudentsDialog",
   extends: MobileFullscreenDialog,
   components: {
+    ExtraMarkButtons,
     TardinessChip,
     ExtraMarkChip,
     AbsenceReasonChip,
@@ -57,9 +59,9 @@ export default {
     },
   },
   methods: {
-    handleMultipleAction(absenceReasonId) {
+    handleMultipleAction(field, id) {
       this.loadSelected = true;
-      this.sendToServer(this.selected, "absenceReason", absenceReasonId);
+      this.sendToServer(this.selected, field, id);
       this.$once("save", this.resetMultipleAction);
     },
     resetMultipleAction() {
@@ -209,11 +211,17 @@ export default {
     <template #actions>
       <v-scroll-y-reverse-transition>
         <div v-show="selected.length > 0" class="full-width">
+          <h4>{{ $t("alsijil.coursebook.participation_status") }}</h4>
           <absence-reason-buttons
+            class="mb-1"
             allow-empty
             empty-value="present"
             :custom-absence-reasons="absenceReasons"
-            @input="handleMultipleAction"
+            @input="handleMultipleAction('absenceReason', $event)"
+          />
+          <h4>{{ $t("alsijil.extra_marks.title_plural") }}</h4>
+          <extra-mark-buttons
+            @input="handleMultipleAction('extraMark', $event)"
           />
         </div>
       </v-scroll-y-reverse-transition>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/sendToServerMixin.js b/aleksis/apps/alsijil/frontend/components/coursebook/absences/sendToServerMixin.js
index d75f72173b80315705e6cf3f86fe614caeafe051..55cae9e6e8d69b0f4e5d299e9869228ccf9c2702 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/sendToServerMixin.js
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/sendToServerMixin.js
@@ -1,6 +1,7 @@
 /**
  * Mixin to provide shared functionality needed to send updated participation data to the server
  */
+import { createPersonalNotes } from "../personal_notes/personal_notes.graphql";
 import { updateParticipationStatuses } from "./participationStatus.graphql";
 import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
 
@@ -18,6 +19,10 @@ export default {
         fieldValue = {
           tardiness: value,
         };
+      } else if (field === "extraMark") {
+        // Too much different logic → own method
+        this.addExtraMarks(participations, value);
+        return;
       } else {
         console.error(`Wrong field '${field}' for sendToServer`);
         return;
@@ -46,6 +51,42 @@ export default {
             participationStatus.isOptimistic = newStatus.isOptimistic;
           });
 
+          return storedDocumentations;
+        },
+      );
+    },
+    addExtraMarks(participations, extraMarkId) {
+      // Get all participation statuses without this extra mark and get the respective person ids
+      const participants = participations
+        .filter(
+          (participation) =>
+            !participation.notesWithExtraMark.some(
+              (note) => note.extraMark.id === extraMarkId,
+            ),
+        )
+        .map((participation) => participation.person.id);
+
+      // CREATE new personal note
+      this.mutate(
+        createPersonalNotes,
+        {
+          input: participants.map((person) => ({
+            documentation: this.documentation.id,
+            person: person,
+            extraMark: extraMarkId,
+          })),
+        },
+        (storedDocumentations, incomingPersonalNotes) => {
+          const documentation = storedDocumentations.find(
+            (doc) => doc.id === this.documentation.id,
+          );
+          incomingPersonalNotes.forEach((note, index) => {
+            const participationStatus = documentation.participations.find(
+              (part) => part.person.id === participants[index],
+            );
+            participationStatus.notesWithExtraMark.push(note);
+          });
+
           return storedDocumentations;
         },
       );
diff --git a/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkButtons.vue b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkButtons.vue
new file mode 100644
index 0000000000000000000000000000000000000000..919749ac85ed97f937d5b862a6aa37d199529b30
--- /dev/null
+++ b/aleksis/apps/alsijil/frontend/components/extra_marks/ExtraMarkButtons.vue
@@ -0,0 +1,73 @@
+<script>
+import { extraMarks } from "./extra_marks.graphql";
+
+export default {
+  name: "ExtraMarkButtons",
+  data() {
+    return {
+      extraMarks: [],
+    };
+  },
+  apollo: {
+    extraMarks: {
+      query: extraMarks,
+      update: (data) => data.items,
+      skip() {
+        return this.customExtraMarks > 0;
+      },
+    },
+  },
+  props: {
+    customExtraMarks: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
+  },
+  computed: {
+    innerExtraMarks() {
+      if (this.customExtraMarks.length > 0) {
+        return this.customExtraMarks;
+      } else {
+        return this.extraMarks;
+      }
+    },
+  },
+  methods: {
+    emit(value) {
+      this.$emit("input", value);
+      this.$emit("click", value);
+    },
+  },
+};
+</script>
+
+<template>
+  <div class="d-flex flex-wrap" style="gap: 0.5em">
+    <v-skeleton-loader
+      class="full-width d-flex flex-wrap child-flex-grow-1"
+      style="gap: 0.5em"
+      v-if="$apollo.queries.extraMarks.loading"
+      type="button@4"
+    />
+    <template v-else>
+      <v-btn
+        v-for="extraMark in innerExtraMarks"
+        :key="'extra-mark-' + extraMark.id"
+        :color="extraMark.colourBg"
+        :style="{ color: extraMark.colourFg }"
+        class="flex-grow-1"
+        depressed
+        @click="emit(extraMark.id)"
+      >
+        {{ extraMark.name }}
+      </v-btn>
+    </template>
+  </div>
+</template>
+
+<style>
+.child-flex-grow-1 > * {
+  flex-grow: 1;
+}
+</style>
diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json
index 2ab6f3647fb2ff6d659862ff6b045747b725ec57..d1a039d4caa7b94b15fdeb36a396b6ec745eef70 100644
--- a/aleksis/apps/alsijil/frontend/messages/en.json
+++ b/aleksis/apps/alsijil/frontend/messages/en.json
@@ -55,6 +55,7 @@
         "cancelled": "Lesson cancelled",
         "pending": "Lesson pending"
       },
+      "participation_status": "Participation Status",
       "summary": {
         "topic": {
           "label": "Topic",
diff --git a/aleksis/apps/alsijil/schema/participation_status.py b/aleksis/apps/alsijil/schema/participation_status.py
index 335f5bc8d1c22682725563d2c3c83b88032fbc38..951ab4419341641cc26059a18f04f91890749f1c 100644
--- a/aleksis/apps/alsijil/schema/participation_status.py
+++ b/aleksis/apps/alsijil/schema/participation_status.py
@@ -47,7 +47,7 @@ class ParticipationStatusType(
             person=root.person,
             documentation=root.related_documentation,
             note__isnull=False,
-        )
+        ).exclude(note="")
 
 
 class ParticipationStatusBatchPatchMutation(BaseBatchPatchMutation):