Gamma Data Handling
Overview
Data handling, privacy controls, and compliance for Gamma API integrations. Gamma processes user-submitted content through AI to generate presentations -- understand what data flows where and how to handle PII, retention, and GDPR requirements.
Prerequisites
- Understanding of data privacy regulations (GDPR, CCPA)
- Completed
gamma-install-authsetup - Data classification policies defined
Data Flow Map
User Input (content, prompts)
│
▼
┌──────────────┐
│ Your App │ ← PII may be in content (names, company data)
│ (API key) │
└──────┬───────┘
│ POST /v1.0/generations
▼
┌──────────────┐
│ Gamma API │ ← Content processed by AI
│ (gamma.app) │ ← Images generated
└──────┬───────┘
│ gammaUrl + exportUrl
▼
┌──────────────┐
│ Generated │ ← Presentation stored in Gamma workspace
│ Content │ ← Export files (PDF/PPTX/PNG) temporary
└──────────────┘
Data Classification
| Data Type | Classification | Where Stored | Retention | |-----------|---------------|--------------|-----------| | API key | Secret | Your env vars | Active use only | | Content/prompts | May contain PII | Gamma servers (during generation) | Gamma's policy | | Generated gammas | User data | Gamma workspace | User-controlled | | Export files (PDF/PPTX) | User data | Temporary URLs | Download promptly, URLs expire | | User prompts in logs | PII risk | Your infrastructure | Your policy (sanitize!) | | Credit usage | Billing data | Gamma | Per Gamma ToS |
Instructions
Step 1: Sanitize Content Before Sending
// src/gamma/sanitize.ts
// Remove PII from content before sending to Gamma if not needed
interface SanitizeOptions {
removeEmails: boolean;
removePhones: boolean;
maskNames: boolean;
}
function sanitizeContent(content: string, opts: SanitizeOptions): string {
let sanitized = content;
if (opts.removeEmails) {
sanitized = sanitized.replace(/[\w.-]+@[\w.-]+\.\w+/g, "[email]");
}
if (opts.removePhones) {
sanitized = sanitized.replace(/\+?[\d\s()-]{10,}/g, "[phone]");
}
if (opts.maskNames) {
// Only mask if you have a list of known names
// Generic regex would be too aggressive
}
return sanitized;
}
// Usage: sanitize before generation
const safeContent = sanitizeContent(userContent, {
removeEmails: true,
removePhones: true,
maskNames: false,
});
await gamma.generate({
content: safeContent,
outputFormat: "presentation",
});
Step 2: Sanitize Logs
// src/gamma/logging.ts
// Never log raw content or API keys
function logGeneration(request: any, result: any) {
console.log(JSON.stringify({
event: "gamma_generation",
timestamp: new Date().toISOString(),
generationId: result.generationId,
outputFormat: request.outputFormat,
contentLength: request.content?.length,
// NEVER log: content (may have PII), apiKey
status: result.status,
creditsUsed: result.creditsUsed,
}));
}
Step 3: Export File Handling
// src/gamma/exports.ts
// Export URLs are temporary — download and store securely
import { writeFile } from "node:fs/promises";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
async function archiveExport(
exportUrl: string,
metadata: { generationId: string; userId: string }
) {
// Download immediately — URLs expire
const res = await fetch(exportUrl);
if (!res.ok) throw new Error(`Export download failed: ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
// Store with encryption
const s3 = new S3Client({ region: "us-east-1" });
const key = `gamma-exports/${metadata.userId}/${metadata.generationId}.pdf`;
await s3.send(new PutObjectCommand({
Bucket: process.env.EXPORTS_BUCKET!,
Key: key,
Body: buffer,
ContentType: "application/pdf",
ServerSideEncryption: "aws:kms",
Metadata: {
generationId: metadata.generationId,
archivedAt: new Date().toISOString(),
},
}));
console.log(`Archived: s3://${process.env.EXPORTS_BUCKET}/${key}`);
}
Step 4: Data Retention Policy
// src/gamma/retention.ts
interface RetentionPolicy {
exportMaxDays: number; // Delete local export copies
logRetentionDays: number; // Anonymize generation logs
promptRetentionDays: number; // Delete stored prompts
}
const POLICY: RetentionPolicy = {
exportMaxDays: 90, // Keep exports 90 days
logRetentionDays: 30, // Anonymize logs after 30 days
promptRetentionDays: 7, // Delete prompts after 7 days
};
async function enforceRetention() {
const cutoff = new Date();
// Delete old exports from S3
cutoff.setDate(cutoff.getDate() - POLICY.exportMaxDays);
await deleteOldExports(cutoff);
// Anonymize old logs
cutoff.setDate(cutoff.getDate() + POLICY.exportMaxDays - POLICY.logRetentionDays);
await anonymizeLogs(cutoff);
// Delete stored prompts
cutoff.setDate(cutoff.getDate() + POLICY.logRetentionDays - POLICY.promptRetentionDays);
await deletePrompts(cutoff);
}
Step 5: GDPR Request Handling
// Handle data subject access/erasure requests
async function handleGdprRequest(
type: "access" | "erasure",
userId: string
) {
if (type === "access") {
// Return all data we store about this user
return {
generations: await db.generations.findMany({ where: { userId } }),
exports: await listS3Objects(`gamma-exports/${userId}/`),
// Note: data stored IN Gamma's workspace is Gamma's responsibility
// Direct user to gamma.app to access/delete their workspace data
};
}
if (type === "erasure") {
// Delete from our systems
await db.generations.deleteMany({ where: { userId } });
await deleteS3Prefix(`gamma-exports/${userId}/`);
// Instruct user to delete Gamma workspace data at gamma.app
return { deleted: true, note: "Delete Gamma workspace data at gamma.app" };
}
}
Compliance Checklist
- [ ] Content sanitized before sending to Gamma API (PII removed if not needed)
- [ ] API keys never logged
- [ ] Export URLs downloaded promptly and stored encrypted
- [ ] Retention policies defined and enforced
- [ ] GDPR access/erasure request process documented
- [ ] User consent obtained for AI processing of their content
- [ ] Gamma DPA signed (if required by your jurisdiction)
- [ ] Logs sanitized (no raw content or PII)
Error Handling
| Error | Cause | Solution | |-------|-------|----------| | Export URL expired | Downloaded too late | Download immediately on generation completion | | PII in logs | Missing sanitization | Add log sanitization middleware | | Retention job failed | Scheduler stopped | Monitor cron job health | | GDPR request incomplete | Gamma workspace not addressed | Direct user to gamma.app for workspace data |
Resources
Next Steps
Proceed to gamma-enterprise-rbac for access control.