diff --git a/src/lib/google.ts b/src/lib/google.ts
new file mode 100644
index 0000000..8048080
--- /dev/null
+++ b/src/lib/google.ts
@@ -0,0 +1,58 @@
+import { google } from 'googleapis';
+import { env } from '$env/dynamic/private';
+
+const {
+ GOOGLE_CLIENT_ID,
+ GOOGLE_CLIENT_SECRET
+} = env;
+
+/* NEW REDIRECT — must match Google Cloud OAuth settings */
+const REDIRECT_URI = 'http://localhost:5173/private/api/gmail';
+
+export const scopes = ['https://www.googleapis.com/auth/gmail.send'];
+
+export function getOAuthClient() {
+ return new google.auth.OAuth2(
+ GOOGLE_CLIENT_ID,
+ GOOGLE_CLIENT_SECRET,
+ REDIRECT_URI
+ );
+}
+
+export function createAuthUrl() {
+ return getOAuthClient().generateAuthUrl({
+ access_type: 'offline',
+ prompt: 'consent',
+ scope: scopes
+ });
+}
+
+export async function exchangeCodeForTokens(code: string) {
+ const { tokens } = await getOAuthClient().getToken(code);
+ if (!tokens.refresh_token) throw new Error('No refresh_token returned');
+ return tokens.refresh_token;
+}
+
+export async function sendGmail(
+ refreshToken: string,
+ { to, subject, text }: { to: string; subject: string; text: string }
+) {
+ const oauth = getOAuthClient();
+ oauth.setCredentials({ refresh_token: refreshToken });
+
+ const gmail = google.gmail({ version: 'v1', auth: oauth });
+ const raw = Buffer
+ .from(
+ [`To: ${to}`,
+ 'Content-Type: text/plain; charset="UTF-8"',
+ 'Content-Transfer-Encoding: 7bit',
+ `Subject: ${subject}`,
+ '',
+ text].join('\n'))
+ .toString('base64url');
+
+ await gmail.users.messages.send({
+ userId: 'me',
+ requestBody: { raw }
+ });
+}
diff --git a/src/routes/private/api/gmail/+server.ts b/src/routes/private/api/gmail/+server.ts
new file mode 100644
index 0000000..aa76b34
--- /dev/null
+++ b/src/routes/private/api/gmail/+server.ts
@@ -0,0 +1,64 @@
+import type { RequestHandler } from './$types';
+import { json, redirect } from '@sveltejs/kit';
+import {
+ createAuthUrl,
+ exchangeCodeForTokens,
+ sendGmail,
+ getOAuthClient
+} from '$lib/google';
+
+/* ───────────── GET ───────────── */
+export const GET: RequestHandler = async ({ url }) => {
+ /* 1. /private/api/gmail?action=auth → 302 to Google */
+ if (url.searchParams.get('action') === 'auth') {
+ throw redirect(302, createAuthUrl());
+ }
+
+ /* 2. Google callback /private/api/gmail?code=XXXX */
+ const code = url.searchParams.get('code');
+ if (code) {
+ try {
+ const refreshToken = await exchangeCodeForTokens(code);
+
+ const html = `
+ `;
+ return new Response(html, { headers: { 'Content-Type': 'text/html' } });
+ } catch (err) {
+ return new Response((err as Error).message, { status: 500 });
+ }
+ }
+
+ return new Response('Bad request', { status: 400 });
+};
+
+/* ───────────── POST ───────────── */
+export const POST: RequestHandler = async ({ request }) => {
+ const { action, refreshToken, to, subject, text } = await request.json();
+
+ /* send e-mail */
+ if (action === 'send') {
+ if (!refreshToken) return new Response('Missing token', { status: 401 });
+ try {
+ await sendGmail(refreshToken, { to, subject, text });
+ return json({ ok: true });
+ } catch (err) {
+ return new Response((err as Error).message, { status: 500 });
+ }
+ }
+
+ /* revoke token */
+ if (action === 'revoke') {
+ if (!refreshToken) return new Response('Missing token', { status: 401 });
+ try {
+ await getOAuthClient().revokeToken(refreshToken);
+ return json({ ok: true });
+ } catch (err) {
+ return new Response((err as Error).message, { status: 500 });
+ }
+ }
+
+ return new Response('Bad request', { status: 400 });
+};
diff --git a/src/routes/private/creator/+page.svelte b/src/routes/private/creator/+page.svelte
new file mode 100644
index 0000000..42a156a
--- /dev/null
+++ b/src/routes/private/creator/+page.svelte
@@ -0,0 +1,63 @@
+
+
+{#if !authorized}
+ You haven’t connected your Google account yet.