--- name: assemblyai-security-basics description: 'Apply AssemblyAI security best practices for API keys, PII, and access control. Use when securing API keys, implementing PII redaction, or configuring temporary tokens for browser-side streaming. Trigger with phrases like "assemblyai security", "assemblyai secrets", "secure assemblyai", "assemblyai API key security", "assemblyai PII". ' allowed-tools: Read, Write, Grep version: 1.0.0 license: MIT author: Jeremy Longshore tags: - saas - ai - speech-to-text - assemblyai - transcription - security compatibility: Designed for Claude Code --- # AssemblyAI Security Basics ## Overview Security best practices for AssemblyAI: API key management, temporary tokens for browser clients, PII redaction, and data retention policies. ## Prerequisites - `assemblyai` package installed - Understanding of environment variables - AssemblyAI dashboard access ## Instructions ### Step 1: API Key Management ```bash # .env (NEVER commit) ASSEMBLYAI_API_KEY=your-api-key-here # .gitignore .env .env.local .env.*.local ``` ```typescript // Never hardcode API keys // BAD: const client = new AssemblyAI({ apiKey: 'sk_abc123...' }); // GOOD: import { AssemblyAI } from 'assemblyai'; const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY!, }); ``` ### Step 2: Temporary Tokens for Browser Streaming Never expose your API key in frontend code. Use temporary tokens for browser-side streaming: ```typescript // Server-side: /api/assemblyai-token.ts import { AssemblyAI } from 'assemblyai'; const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY!, }); export async function GET() { // Token expires after 5 minutes const token = await client.streaming.createTemporaryToken({ expires_in_seconds: 300, }); return Response.json({ token }); } // Client-side: use the temporary token // const { token } = await fetch('/api/assemblyai-token').then(r => r.json()); // const transcriber = new StreamingTranscriber({ token }); ``` ### Step 3: PII Redaction in Transcripts ```typescript const transcript = await client.transcripts.transcribe({ audio: audioUrl, redact_pii: true, redact_pii_policies: [ 'email_address', 'phone_number', 'person_name', 'credit_card_number', 'social_security_number', 'date_of_birth', 'medical_condition', 'banking_information', 'us_social_security_number', ], redact_pii_sub: 'entity_name', // or 'hash' // 'entity_name': "My name is [PERSON_NAME]" // 'hash': "My name is ####" }); // Also redact the audio itself const transcriptWithRedactedAudio = await client.transcripts.transcribe({ audio: audioUrl, redact_pii: true, redact_pii_policies: ['person_name', 'phone_number'], redact_pii_audio: true, // Generates audio with PII beeped out }); ``` ### Step 4: Data Retention and Deletion ```typescript // Delete transcript data for GDPR/privacy compliance await client.transcripts.delete(transcriptId); // This permanently removes the transcript text and metadata // The audio file at your source URL is NOT deleted (you manage that) // List and bulk-delete old transcripts const page = await client.transcripts.list({ limit: 100 }); for (const t of page.transcripts) { const createdDate = new Date(t.created); const daysOld = (Date.now() - createdDate.getTime()) / (1000 * 60 * 60 * 24); if (daysOld > 30) { await client.transcripts.delete(t.id); console.log(`Deleted transcript ${t.id} (${daysOld.toFixed(0)} days old)`); } } ``` ### Step 5: Content Safety Detection ```typescript // Detect sensitive content before it reaches your users const transcript = await client.transcripts.transcribe({ audio: audioUrl, content_safety: true, }); const safetyResults = transcript.content_safety_labels?.results ?? []; for (const result of safetyResults) { for (const label of result.labels) { if (label.confidence > 0.8) { console.warn(`Content safety flag: ${label.label} (${(label.confidence * 100).toFixed(0)}%)`); // Labels include: hate_speech, violence, profanity, etc. } } } // Get overall severity summary const summary = transcript.content_safety_labels?.summary ?? {}; for (const [category, severity] of Object.entries(summary)) { console.log(`${category}: severity ${severity}`); } ``` ### Step 6: Security Checklist - [ ] API key stored in environment variable, never in code - [ ] `.env` files listed in `.gitignore` - [ ] Separate API keys for dev/staging/prod environments - [ ] Temporary tokens used for browser streaming (not raw API key) - [ ] PII redaction enabled for sensitive audio - [ ] Old transcripts deleted per retention policy - [ ] Content safety enabled for user-generated audio - [ ] Webhook endpoints validate payload authenticity - [ ] CI/CD secrets stored in platform secrets manager (not env files) ## Output - Secure API key storage pattern - Temporary token endpoint for browser streaming - PII redaction with configurable policies - Data retention automation - Content safety detection ## Error Handling | Security Issue | Detection | Mitigation | |----------------|-----------|------------| | API key in source code | Git scanning / secrets detection | Rotate key immediately at dashboard | | API key in browser JS | Network tab inspection | Use temporary tokens | | PII in transcripts | Manual review or automated scan | Enable `redact_pii` | | Old transcripts retained | Audit transcript list | Automate deletion schedule | ## Resources - [PII Redaction Guide](https://www.assemblyai.com/docs/audio-intelligence/pii-redaction) - [Content Safety Detection](https://www.assemblyai.com/docs/audio-intelligence/content-moderation) - [Streaming Temporary Tokens](https://www.assemblyai.com/docs/getting-started/transcribe-streaming-audio) - [API Key Dashboard](https://www.assemblyai.com/app/account) ## Next Steps For production deployment, see `assemblyai-prod-checklist`.