⚡ You're viewing a live demo of ChimerAI. Data resets daily at midnight UTC.Get the CLI →

GDPR Compliance

ChimerAI includes a ready-to-use GDPR toolkit covering data export, right-to-erasure, consent management, and a cookie banner — all wired to your database.

What you get

  • Data export — Download all personal data as JSON (Art. 20)
  • Right to erasure — Delete account + anonymise data (Art. 17)
  • Consent management — Store and retrieve granular consent
  • Cookie banner — GDPR-compliant opt-in/opt-out with Consent API
  • Audit trail — Every GDPR action is logged for compliance

Quick setup

npx chimerai add gdpr

Scaffolds:

lib/gdpr.ts                      ← exportUserData, deleteUserData, recordConsent, getConsentStatus
app/gdpr/page.tsx                ← self-service portal
app/api/gdpr/export/route.ts     ← Art. 20 — data export (JSON download)
app/api/gdpr/delete/route.ts     ← Art. 17 — right to erasure
app/api/gdpr/consent/route.ts    ← Art. 7 — GET/POST consent record
components/CookieBanner.tsx      ← cookie consent UI

Prisma schema changes

model ConsentLog {
  id        String   @id @default(cuid())
  userId    String
  type      String   // 'analytics' | 'marketing' | custom
  granted   Boolean
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([type])
}

After installation run:

npx prisma migrate dev --name add-consent-log

lib/gdpr.ts functions

import { exportUserData, deleteUserData, recordConsent, getConsentStatus } from '@/lib/gdpr';

// Art. 15 / Art. 20 — Export all personal data (strips password, mfaSecret)
const data = await exportUserData(session.user.id);

// Art. 17 — Right to erasure: cascade-deletes ConsentLog → Sessions → Accounts
//            → APIKeys → Messages → Conversations → User
await deleteUserData(session.user.id);

// Art. 7 — Record a consent decision (creates immutable audit row with timestamp)
await recordConsent(userId, 'analytics', true);
await recordConsent(userId, 'marketing', false);

// Get latest decision per type
const status = await getConsentStatus(userId);
// { analytics: true, marketing: false }

Data export

// app/api/gdpr/export/route.ts
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { db } from '@/lib/db';

export async function GET() {
  const session = await getServerSession(authOptions);
  if (!session) return new Response('Unauthorized', { status: 401 });

  const user = await db.user.findUnique({
    where: { email: session.user!.email! },
    include: { messages: true, conversations: true },
  });

  const exportData = {
    exportedAt: new Date().toISOString(),
    profile: { name: user!.name, email: user!.email },
    conversations: user!.conversations,
    messages: user!.messages,
  };

  return new Response(JSON.stringify(exportData, null, 2), {
    headers: {
      'Content-Type': 'application/json',
      'Content-Disposition': `attachment; filename="my-data-${Date.now()}.json"`,
    },
  });
}

Right to erasure

// app/api/gdpr/delete/route.ts
import { db } from '@/lib/db';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';

export async function DELETE() {
  const session = await getServerSession(authOptions);
  if (!session) return new Response('Unauthorized', { status: 401 });

  // Anonymise instead of hard delete (preserves referential integrity)
  await db.user.update({
    where: { email: session.user!.email! },
    data: {
      name: 'Deleted User',
      email: `deleted-${Date.now()}@anonymised.invalid`,
      image: null,
      deletedAt: new Date(),
    },
  });

  return Response.json({ success: true });
}
// app/api/gdpr/consent/route.ts
import { db } from '@/lib/db';

export async function POST(req: Request) {
  const { userId, analytics, marketing } = await req.json();
  await db.consent.upsert({
    where: { userId },
    update: { analytics, marketing, updatedAt: new Date() },
    create: { userId, analytics, marketing },
  });
  return Response.json({ ok: true });
}

Further reading

ChimerAI Docs · Back to Demo