openrouter-compliance-review
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
gamma-data-handling
Handle data privacy, retention, and compliance for Gamma integrations.
Full skill instructions
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.
gamma-install-auth setupUser 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 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 |
// 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",
});
// 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,
}));
}
// 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}`);
}
// 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);
}
// 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" };
}
}
| 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 |
Proceed to gamma-enterprise-rbac for access control.
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
Data synchronization, ETL patterns, and data management for MaintainX.
Execute Groq primary workflow: chat completions with tool use and JSON
Validate CSRF protection implementations for security gaps. Use when
Configure this skill should be used when the user asks about "API contract",
Execute Ideogram incident response with triage, mitigation, and postmortem.
Execute create, select, and transform features to improve machine learning
Reusable patterns for the Gamma REST API (no SDK exists).
Apply production-ready Deepgram SDK patterns for TypeScript and Python.
Migrate to OpenRouter from direct provider APIs or upgrade between SDK/model
Diagnose and fix common OpenRouter API errors. Use when encountering
Optimize Linear API usage, reduce unnecessary calls, and maximize
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience