diff --git a/src/routes/admin/reports/+page.svelte b/src/routes/admin/reports/+page.svelte
index 37a0537..e2ba02b 100644
--- a/src/routes/admin/reports/+page.svelte
+++ b/src/routes/admin/reports/+page.svelte
@@ -363,6 +363,7 @@
 			if (!e.detail.draft) $reportCount += 1;
 			currentPageReports = [e.detail, ...currentPageReports];
 			activeReport = currentPageReports[0];
+			newReportModal.close();
 		}}
 	/>
 </dialog>
diff --git a/src/routes/admin/users/+page.svelte b/src/routes/admin/users/+page.svelte
index 19743e2..ff9d630 100644
--- a/src/routes/admin/users/+page.svelte
+++ b/src/routes/admin/users/+page.svelte
@@ -1,6 +1,6 @@
 <script lang="ts">
 	import type { PageData } from './$types';
-	import { Check, NoSymbol, PencilSquare, Trash } from 'svelte-heros-v2';
+	import { Check, NoSymbol, PencilSquare, Plus, Trash } from 'svelte-heros-v2';
 	import Input from '$lib/components/Input/Input.svelte';
 	import Select from '$lib/components/Input/Select.svelte';
 	import { env } from '$env/dynamic/public';
@@ -10,6 +10,7 @@
 	import HeaderBar from './HeaderBar.svelte';
 	import SortableTr from '$lib/components/Table/SortableTr.svelte';
 	import SortableTh from '$lib/components/Table/SortableTh.svelte';
+	import NewUserModal from './NewUserModal.svelte';
 
 	export let data: PageData;
 
@@ -19,6 +20,7 @@
 	let userPage = 0;
 	let userFilter = { name: null, playertype: null };
 	let userTableContainerElement: HTMLDivElement;
+	let newUserModal: HTMLDialogElement;
 
 	function fetchPageUsers(page: number) {
 		if (!browser) return;
@@ -208,6 +210,16 @@
 							</tr>
 						{/each}
 					{/await}
+					<tr>
+						<td colspan="100">
+							<div class="flex justify-center items-center">
+								<button class="btn btn-sm" on:click={() => newUserModal.show()}>
+									<Plus />
+									<span>Neuer Spieler</span>
+								</button>
+							</div>
+						</td>
+					</tr>
 				{/key}
 			</tbody>
 		</table>
@@ -227,3 +239,12 @@
 		</div>
 	</div>
 </div>
+
+<dialog class="modal" bind:this={newUserModal}>
+	<NewUserModal
+		on:submit={(e) => {
+			currentPageUsers = [...currentPageUsers, e.detail];
+			newUserModal.close();
+		}}
+	/>
+</dialog>
diff --git a/src/routes/admin/users/+server.ts b/src/routes/admin/users/+server.ts
index 863d354..6f996de 100644
--- a/src/routes/admin/users/+server.ts
+++ b/src/routes/admin/users/+server.ts
@@ -1,8 +1,9 @@
 import { getSession } from '$lib/server/session';
 import { Permissions } from '$lib/permissions';
-import type { RequestHandler } from '@sveltejs/kit';
+import { error, type RequestHandler } from '@sveltejs/kit';
 import { User } from '$lib/server/database';
 import { type Attributes, Op } from 'sequelize';
+import { ApiError, getJavaUuid, getNoAuthUuid, UserNotFoundError } from '$lib/server/minecraft';
 
 export const POST = (async ({ request, cookies }) => {
 	if (getSession(cookies, { permissions: [Permissions.UserRead] }) == null) {
@@ -88,6 +89,90 @@ export const PATCH = (async ({ request, cookies }) => {
 	return new Response();
 }) satisfies RequestHandler;
 
+export const PUT = (async ({ request, cookies }) => {
+	if (getSession(cookies, { permissions: [Permissions.UserWrite] }) == null) {
+		return new Response(null, {
+			status: 401
+		});
+	}
+
+	const data: {
+		firstname: string;
+		lastname: string;
+
+		birthday: string;
+		telephone: string;
+
+		username: string;
+		playertype: string;
+	} = await request.json();
+
+	if (
+		data.firstname == null ||
+		data.lastname == null ||
+		data.birthday == null ||
+		data.username == null ||
+		data.playertype == null
+	) {
+		return new Response(null, { status: 400 });
+	}
+
+	let uuid: string | null;
+	try {
+		switch (data.playertype) {
+			case 'java':
+				uuid = await getJavaUuid(data.username);
+				break;
+			case 'bedrock':
+				uuid = null;
+				// uuid = await getBedrockUuid(username);
+				break;
+			case 'noauth':
+				uuid = getNoAuthUuid(data.username);
+				break;
+			default:
+				throw new Error(`invalid player type (${data.playertype})`);
+		}
+	} catch (e) {
+		if (e instanceof UserNotFoundError) {
+			throw error(400, `Der Spielername ${data.username} existiert nicht`);
+		} else if (e instanceof ApiError) {
+			console.error((e as Error).message);
+			uuid = null;
+		} else {
+			console.error((e as Error).message);
+			throw error(500);
+		}
+	}
+
+	if (uuid && (await User.findOne({ where: { uuid: uuid } }))) {
+		throw error(400, 'Dieser Minecraft-Account wurde bereits registriert');
+	} else if (
+		await User.findOne({
+			where: {
+				firstname: data.firstname,
+				lastname: data.lastname,
+				birthday: new Date(data.birthday).toUTCString()
+			}
+		})
+	) {
+		throw error(400, 'Ein Nutzer mit demselben Namen und Geburtstag wurde bereits registriert');
+	}
+
+	await User.create({
+		firstname: data.firstname,
+		lastname: data.lastname,
+		birthday: new Date(data.birthday).toUTCString(),
+		telephone: data.telephone,
+		username: data.username,
+		playertype: data.playertype,
+		password: null,
+		uuid: uuid
+	});
+
+	return new Response();
+}) satisfies RequestHandler;
+
 export const DELETE = (async ({ request, cookies }) => {
 	if (getSession(cookies, { permissions: [Permissions.UserWrite] }) == null) {
 		return new Response(null, {
diff --git a/src/routes/admin/users/NewUserModal.svelte b/src/routes/admin/users/NewUserModal.svelte
new file mode 100644
index 0000000..2aa7a53
--- /dev/null
+++ b/src/routes/admin/users/NewUserModal.svelte
@@ -0,0 +1,114 @@
+<script lang="ts">
+	import Input from '$lib/components/Input/Input.svelte';
+	import { env } from '$env/dynamic/public';
+	import Select from '$lib/components/Input/Select.svelte';
+	import { errorMessage } from '$lib/stores';
+	import { createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	let firstname: string;
+	let lastname: string;
+	let birthday: string;
+	let phone: string;
+	let username: string;
+	let playertype = 'java';
+
+	async function newUser() {
+		const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
+			method: 'PUT',
+			body: JSON.stringify({
+				firstname: firstname,
+				lastname: lastname,
+				birthday: birthday,
+				telephone: phone,
+				username: username,
+				playertype: playertype
+			})
+		});
+		if (response.ok) {
+			dispatch('submit', {
+				firstname: firstname,
+				lastname: lastname,
+				birthday: birthday,
+				telephone: phone,
+				username: username,
+				playertype: playertype
+			});
+			globalCloseForm.submit();
+		} else {
+			$errorMessage = (await response.json()).message;
+		}
+	}
+
+	let globalCloseForm: HTMLFormElement;
+
+	let reportForm: HTMLFormElement;
+	let confirmDialog: HTMLDialogElement;
+</script>
+
+<form method="dialog" class="modal-box" bind:this={reportForm}>
+	<button
+		class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
+		on:click|preventDefault={() => globalCloseForm.submit()}>✕</button
+	>
+	<h3 class="font-roboto text-xl">Neuer Spieler</h3>
+	<div class="grid grid-cols-2 gap-4">
+		<Input type="text" required bind:value={firstname}>
+			<span slot="label">Vorname</span>
+		</Input>
+		<Input type="text" required bind:value={lastname}>
+			<span slot="label">Nachname</span>
+		</Input>
+		<Input type="date" required bind:value={birthday}>
+			<span slot="label">Geburtstag</span>
+		</Input>
+		<Input type="tel" bind:value={phone}>
+			<span slot="label">Telefonnummer</span>
+		</Input>
+		<Input type="text" required bind:value={username}>
+			<span slot="label">Minecraft-Spielername</span>
+		</Input>
+		<Select required label="Edition" bind:value={playertype}>
+			<option value="java">Java Edition</option>
+			<option value="bedrock">Bedrock Edition</option>
+		</Select>
+	</div>
+	<div class="flex flex-row space-x-2 mt-6">
+		<Input
+			type="submit"
+			value="Hinzufügen"
+			on:click={(e) => {
+				if (reportForm.checkValidity()) {
+					e.detail.preventDefault();
+					confirmDialog.show();
+				}
+			}}
+		/>
+		<Input
+			type="submit"
+			value="Abbrechen"
+			on:click={(e) => {
+				e.detail.preventDefault();
+				globalCloseForm.submit();
+			}}
+		/>
+	</div>
+</form>
+<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]" bind:this={globalCloseForm}>
+	<button>close</button>
+</form>
+
+<dialog class="modal" bind:this={confirmDialog}>
+	<form method="dialog" class="modal-box">
+		<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
+		<h3 class="font-roboto text-xl mb-2">Spieler hinzufügen?</h3>
+		<div class="flex flex-row space-x-2 mt-6">
+			<Input type="submit" value="Hinzufügen" on:click={newUser} />
+			<Input type="submit" value="Abbrechen" />
+		</div>
+	</form>
+	<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
+		<button>close</button>
+	</form>
+</dialog>