diff --git a/src/lib/server/mail.js b/src/lib/server/mail.ts
similarity index 71%
rename from src/lib/server/mail.js
rename to src/lib/server/mail.ts
index 716043d30a10baaaa6b381755c6d68a4fd4ab684..109a116b4def709354b86db6154f20b7d4a2c650 100644
--- a/src/lib/server/mail.js
+++ b/src/lib/server/mail.ts
@@ -1,4 +1,5 @@
 import nodemailer from "nodemailer";
+import type { Address } from "nodemailer/lib/mailer";
 
 const transporter = nodemailer.createTransport({
 	host: process.env.MAIL_HOST,
@@ -19,17 +20,29 @@ const transporter = nodemailer.createTransport({
  * @param {boolean} [mailOptions.html=false] `true` if `text` is HTML, `false` if `text` is plain text
  * @returns 
  */
-export async function sendMail({to, subject, text, html=false}) {
-	const content = html ? {html: text} : {text};
+
+type MailOptions = {
+	to?: Address | string | Array<Address | string>,
+	cc?: Address | string | Array<Address | string>,
+	bcc?: Address | string | Array<Address | string>,
+	subject: string,
+	text: string,
+	html?: string
+}
+
+export async function sendMail({to, cc, bcc, subject, text, html}: MailOptions) {
 	const subjectPrefix = process.env.MAIL_SUBJECT_PREFIX ?? "";
 	return transporter.sendMail({
 		subject: subjectPrefix + subject,
 		to,
+		cc,
+		bcc,
 		from: {
 			address: process.env.MAIL_FROM_ADDRESS,
 			name: process.env.MAIL_FROM_NAME
 		},
 		replyTo: process.env.MAIL_REPLY_TO,
-		...content
+		text,
+		html
 	});
 }
diff --git a/src/lib/server/notifications/emailHandler.ts b/src/lib/server/notifications/emailHandler.ts
index b06fc3c68e2e69551b831050e9af1c37be6d1ff3..2b69aa65b8dc23edc0a453e050e4d32815fe2c62 100644
--- a/src/lib/server/notifications/emailHandler.ts
+++ b/src/lib/server/notifications/emailHandler.ts
@@ -1,12 +1,13 @@
 import { sendMail } from "../mail";
-import { formatMessage, getMessageTitle } from "./formatter";
+import { formatMessage, formatMessageToHtml, getMessageTitle } from "./formatter";
 import type { HandlerReturnType, NotificationData, NotificationType, PossibleNotificationType } from "../../notifications/types";
 
 export async function handleEmail<T extends PossibleNotificationType>(recipient: string, type: NotificationType<T>, data: NotificationData<T>): Promise<HandlerReturnType> {
 	return sendMail({
 		to: recipient,
 		subject: getMessageTitle(type),
-		text: formatMessage(type, data)
+		text: formatMessage(type, data),
+		html: formatMessageToHtml(type, data)
 	}).then(res=>{
 		if(res.accepted.length > 0) return { status: "success" };
 		else if(res.pending.length > 0) throw "Email is pending";
diff --git a/src/lib/server/notifications/formatter.ts b/src/lib/server/notifications/formatter.ts
index 6c117aafc45231b96b4f1b0e59920545de8e9b45..24fbfc974c245a956be24f33cac0d017649b2a7c 100644
--- a/src/lib/server/notifications/formatter.ts
+++ b/src/lib/server/notifications/formatter.ts
@@ -76,3 +76,62 @@ Dein neuer Kontostand beträgt ${(balanceAfter/100).toFixed(2)}€.`;
 		}
 	}
 }
+
+export function formatMessageToHtml<T extends PossibleNotificationType>(type: NotificationType<T>, data: NotificationData<T>): string {
+	switch (type.name) {
+		case "buy": {
+			const { total, items, balanceBefore, balanceAfter } = data as NotificationData<"buy">;
+			const premiums = items.filter(item => item.premium && item.premium > 0).map(item => item.premium).reduce((a, b) => a + b, 0);
+			return `<p>Du hast folgende ${items.length} Artikel gekauft:</p>
+<ul>${items.map(item => `<li>${escapeHtml(item.name)} (${escapeHtml(item.code)}) - ${(item.price / 100).toFixed(2)}€` + (item.premium && item.premium > 0 ? ` (+${(item.premium / 100).toFixed(2)}€)` : "") + `</li>`).join("")}</ul>
+<p>Gesamt: ${(total / 100).toFixed(2)}€` + (premiums > 0 ? ` (davon ${(premiums / 100).toFixed(2)}€ Negativaufschlag)` : "") + `</p>
+<p>Dein neuer Kontostand beträgt ${(balanceAfter / 100).toFixed(2)}€.</p>`;
+		}
+		case "refund": {
+			const { refund, item, balanceBefore, balanceAfter, timeBought } = data as NotificationData<"refund">;
+			const premiumMessage = refund.premium && refund.premium > 0 ? ` (+${(refund.premium / 100).toFixed(2)}€)` : "";
+			return `<p>Dir wurden ${(refund.price / 100).toFixed(2)}€${premiumMessage} für ${escapeHtml(item.name)} (${escapeHtml(item.code)}) erstattet, gekauft am ${timeBought.toLocaleString()}.</p>
+<p>Dein neuer Kontostand beträgt ${(balanceAfter / 100).toFixed(2)}€.</p>`
+		}
+		case "deposit": {
+			const { amount, balanceBefore, balanceAfter } = data as NotificationData<"deposit">;
+			return `<p>Du hast ${(amount / 100).toFixed(2)}€ eingezahlt.</p>
+<p>Dein neuer Kontostand beträgt ${(balanceAfter / 100).toFixed(2)}€.</p>`;
+		}
+		case "withdraw": {
+			const { amount, balanceBefore, balanceAfter } = data as NotificationData<"withdraw">;
+			return `<p>Du hast ${(amount / 100).toFixed(2)}€ ausgezahlt.</p>
+<p>Dein neuer Kontostand beträgt ${(balanceAfter / 100).toFixed(2)}€.</p>`;
+		}
+		case "use-voucher": {
+			const { voucher, balanceBefore, balanceAfter } = data as NotificationData<"use-voucher">;
+			return `<p>Du hast einen Gutschein im Wert von ${(voucher.value / 100).toFixed(2)}€ eingelöst.</p>
+<p>Dein neuer Kontostand beträgt ${(balanceAfter / 100).toFixed(2)}€.</p>`;
+		}
+		case "send-transfer": {
+			const { amount, balanceBefore, balanceAfter, receiver } = data as NotificationData<"send-transfer">;
+			return `<p>Du hast ${(amount / 100).toFixed(2)}€ an ${escapeHtml(receiver.name)} überwiesen.</p>
+<p>Dein neuer Kontostand beträgt ${(balanceAfter / 100).toFixed(2)}€.</p>`;
+		}
+		case "receive-transfer": {
+			const { amount, balanceBefore, balanceAfter, sender } = data as NotificationData<"receive-transfer">;
+			return `<p>Du hast ${(amount / 100).toFixed(2)}€ von ${escapeHtml(sender.name)} erhalten.</p>
+<p>Dein neuer Kontostand beträgt ${(balanceAfter / 100).toFixed(2)}€.</p>`;
+		}
+		default: {
+			console.error(`Unknown notification type`, type);
+			return `Unknown notification type ${escapeHtml(JSON.stringify(type))}`;
+		}
+	}
+}
+
+function escapeHtml(unsafe: string): string{
+	const lookup = {
+		"&": "&amp;",
+		"<": "&lt;",
+		">": "&gt;",
+		'"': "&quot;",
+		"'": "&#39;"
+	};
+	return unsafe.replace(/[&<>"']/g, c => lookup[c]);
+}