The Problem
On Monday you tested the 3 prompts in ChatGPT. You drafted a statement, tailored it for different audiences, and planned follow-ups. That's great for one crisis. But here's reality: when you're dealing with a data breach at 2am, you can't spend 2 hours copy-pasting messages across 6 channels. By the time you're done, Twitter's already on fire and your customers are panicking.
See It Work
Watch the 3 prompts chain together automatically. This is what you'll build - from crisis detection to multi-channel publishing in 30 seconds.
The Code
Three levels: start simple, add approval workflows, then scale to real-time monitoring. Pick where you are.
Level 1: Simple Multi-Channel Publishing
Good for: 1-5 crises/year | Setup time: 30 minutes
// Simple Multi-Channel Publishing (1-5 crises/year) import Anthropic from '@anthropic-ai/sdk'; import Twitter from 'twitter-api-v2'; import { Client as LinkedInClient } from 'linkedin-api-client'; import sgMail from '@sendgrid/mail'; import twilio from 'twilio'; interface CrisisMessage { twitter: string; linkedin: string; email_subject: string; email_body: string; sms: string; } async function generateCrisisMessages( crisisDescription: string ): Promise<CrisisMessage> { const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, }); const prompt = `You are a crisis communication expert. Given this crisis situation, generate appropriate messages for different channels. Crisis: ${crisisDescription} Generate messages for: 1. Twitter (under 280 chars, direct and factual) 2. LinkedIn (professional, detailed, 200-300 words) 3. Email subject line (clear, urgent, under 60 chars) 4. Email body (detailed, empathetic, actionable) 5. SMS (under 160 chars, essential info only) Output as JSON with keys: twitter, linkedin, email_subject, email_body, sms Tone: Transparent, factual, empathetic. No corporate jargon.`; const response = await anthropic.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 2000, messages: [{ role: 'user', content: prompt }], }); const content = response.content[0]; if (content.type !== 'text') throw new Error('Invalid response'); return JSON.parse(content.text); } async function publishToAllChannels(messages: CrisisMessage) { // Twitter const twitterClient = new Twitter({ appKey: process.env.TWITTER_API_KEY!, appSecret: process.env.TWITTER_API_SECRET!, accessToken: process.env.TWITTER_ACCESS_TOKEN!, accessSecret: process.env.TWITTER_ACCESS_SECRET!, }); const tweet = await twitterClient.v2.tweet(messages.twitter); console.log(`Published to Twitter: ${tweet.data.id}`); // LinkedIn const linkedInClient = new LinkedInClient({ clientId: process.env.LINKEDIN_CLIENT_ID!, clientSecret: process.env.LINKEDIN_CLIENT_SECRET!, redirectUrl: process.env.LINKEDIN_REDIRECT_URL!, }); const linkedInPost = await linkedInClient.createShare({ owner: `urn:li:person:${process.env.LINKEDIN_PERSON_ID}`, text: messages.linkedin, }); console.log(`Published to LinkedIn: ${linkedInPost.id}`); // Email (SendGrid) sgMail.setApiKey(process.env.SENDGRID_API_KEY!); const affectedCustomers = await getAffectedCustomers(); // Your DB query await sgMail.sendMultiple({ to: affectedCustomers.map((c) => c.email), from: 'security@company.com', subject: messages.email_subject, text: messages.email_body, html: messages.email_body.replace(/\n/g, '<br>'), }); console.log(`Sent ${affectedCustomers.length} emails`); // SMS (Twilio) const twilioClient = twilio( process.env.TWILIO_ACCOUNT_SID!, process.env.TWILIO_AUTH_TOKEN! ); const smsPromises = affectedCustomers.map((customer) => twilioClient.messages.create({ body: messages.sms, from: process.env.TWILIO_PHONE_NUMBER!, to: customer.phone, }) ); await Promise.all(smsPromises); console.log(`Sent ${affectedCustomers.length} SMS messages`); } // Usage const crisisDescription = `Payment system breach, 12K cards exposed...`; const messages = await generateCrisisMessages(crisisDescription); await publishToAllChannels(messages); // Helper function (implement based on your DB) async function getAffectedCustomers() { // Query your database for affected customers return [ { email: 'customer1@example.com', phone: '+1234567890' }, // ... more customers ]; }
Level 2: With Approval Workflow & Validation
Good for: 5-20 crises/year | Setup time: 2 hours
# With Approval Workflow & Validation (5-20 crises/year) import anthropic import asyncio from typing import Dict, List import smtplib from email.mime.text import MIMEText from dataclasses import dataclass import json @dataclass class Approver: role: str email: str required: bool approved: bool = False class CrisisResponseSystem: def __init__(self): self.anthropic = anthropic.Anthropic( api_key=os.environ.get("ANTHROPIC_API_KEY") ) self.pending_approvals = {} async def generate_crisis_response(self, crisis_description: str) -> Dict: """Generate messages and determine approval requirements""" # Step 1: Analyze crisis severity severity_prompt = f"""Analyze this crisis and determine: 1. Severity level (low/medium/high/critical) 2. Required approvers (CEO, Legal, PR, etc.) 3. Legal review needed? (yes/no) 4. Immediate publication recommended? (yes/no) Crisis: {crisis_description} Output as JSON.""" severity_response = self.anthropic.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[{"role": "user", "content": severity_prompt}] ) severity_data = json.loads(severity_response.content[0].text) # Step 2: Generate messages message_prompt = f"""Generate crisis communication messages. Crisis: {crisis_description} Severity: {severity_data['severity']} Generate: 1. Twitter (under 280 chars) 2. LinkedIn (200-300 words, professional) 3. Email subject + body (detailed, actionable) 4. SMS (under 160 chars) Output as JSON with keys: twitter, linkedin, email_subject, email_body, sms""" message_response = self.anthropic.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=2000, messages=[{"role": "user", "content": message_prompt}] ) messages = json.loads(message_response.content[0].text) return { "severity": severity_data, "messages": messages, "approval_id": self._create_approval_request(severity_data, messages) } def _create_approval_request(self, severity: Dict, messages: Dict) -> str: """Create approval request and notify approvers""" approval_id = f"crisis_{int(time.time())}" # Determine required approvers based on severity approvers = [] if severity['severity'] in ['high', 'critical']: approvers.append(Approver('CEO', 'ceo@company.com', True)) approvers.append(Approver('General Counsel', 'legal@company.com', True)) if severity['legal_review_needed']: approvers.append(Approver('Legal Team', 'legal@company.com', True)) approvers.append(Approver('Head of PR', 'pr@company.com', False)) self.pending_approvals[approval_id] = { 'approvers': approvers, 'messages': messages, 'created_at': time.time(), 'status': 'pending' } # Send approval requests via email self._send_approval_emails(approval_id, approvers, messages) return approval_id def _send_approval_emails(self, approval_id: str, approvers: List[Approver], messages: Dict): """Send approval request emails with preview""" smtp_server = smtplib.SMTP('smtp.gmail.com', 587) smtp_server.starttls() smtp_server.login( os.environ.get('EMAIL_USER'), os.environ.get('EMAIL_PASSWORD') ) for approver in approvers: email_body = f"""URGENT: Crisis Communication Approval Required Role: {approver.role} Required: {'YES' if approver.required else 'NO'} Proposed Messages: Twitter: {messages['twitter']} LinkedIn: {messages['linkedin']} Email Subject: {messages['email_subject']} Approve: https://company.com/approve/{approval_id}?approver={approver.email} Reject: https://company.com/reject/{approval_id}?approver={approver.email} This requires immediate attention.""" msg = MIMEText(email_body) msg['Subject'] = f'URGENT: Crisis Communication Approval - {approval_id}' msg['From'] = 'crisis-system@company.com' msg['To'] = approver.email smtp_server.send_message(msg) smtp_server.quit() async def check_approval_status(self, approval_id: str) -> Dict: """Check if all required approvals received""" approval_data = self.pending_approvals.get(approval_id) if not approval_data: return {'status': 'not_found'} required_approvers = [a for a in approval_data['approvers'] if a.required] all_approved = all(a.approved for a in required_approvers) if all_approved: # Auto-publish if all required approvals received await self._publish_messages(approval_data['messages']) approval_data['status'] = 'published' return {'status': 'published', 'published_at': time.time()} return { 'status': 'pending', 'approvals': [ {'role': a.role, 'approved': a.approved, 'required': a.required} for a in approval_data['approvers'] ] } async def _publish_messages(self, messages: Dict): """Publish to all channels""" # Implementation similar to Level 1 example # Twitter, LinkedIn, Email, SMS publishing pass # Usage system = CrisisResponseSystem() # Generate and request approval crisis = "Payment breach, 12K cards exposed..." response = await system.generate_crisis_response(crisis) print(f"Approval ID: {response['approval_id']}") # Check status (would be called by webhook when approvers click links) status = await system.check_approval_status(response['approval_id']) print(f"Status: {status['status']}")
Level 3: Production with Real-Time Monitoring
Good for: 20+ crises/year | Setup time: 1 day
// Production with Real-Time Monitoring (20+ crises/year) import Anthropic from '@anthropic-ai/sdk'; import { Queue, Worker } from 'bullmq'; import Redis from 'ioredis'; import { WebClient as SlackClient } from '@slack/web-api'; import axios from 'axios'; interface CrisisEvent { id: string; description: string; severity: 'low' | 'medium' | 'high' | 'critical'; detected_at: Date; channels: string[]; impact_metrics?: ImpactMetrics; } interface ImpactMetrics { social_reach: number; sentiment_score: number; media_mentions: number; support_tickets: number; customer_calls: number; } class ProductionCrisisSystem { private anthropic: Anthropic; private redis: Redis; private crisisQueue: Queue; private monitoringQueue: Queue; private slack: SlackClient; constructor() { this.anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, }); this.redis = new Redis(process.env.REDIS_URL!); this.crisisQueue = new Queue('crisis-response', { connection: this.redis, }); this.monitoringQueue = new Queue('crisis-monitoring', { connection: this.redis, }); this.slack = new SlackClient(process.env.SLACK_BOT_TOKEN!); this.initializeWorkers(); } private initializeWorkers() { // Worker 1: Process crisis events new Worker( 'crisis-response', async (job) => { const crisis: CrisisEvent = job.data; await this.processCrisis(crisis); }, { connection: this.redis } ); // Worker 2: Monitor impact metrics new Worker( 'crisis-monitoring', async (job) => { const crisisId: string = job.data.crisisId; await this.monitorImpact(crisisId); }, { connection: this.redis } ); } async detectAndRespond(crisisDescription: string): Promise<string> { // Step 1: Analyze and classify crisis const analysis = await this.analyzeCrisis(crisisDescription); // Step 2: Create crisis event const crisisEvent: CrisisEvent = { id: `crisis_${Date.now()}`, description: crisisDescription, severity: analysis.severity, detected_at: new Date(), channels: this.determineChannels(analysis.severity), }; // Step 3: Store in Redis await this.redis.set( `crisis:${crisisEvent.id}`, JSON.stringify(crisisEvent), 'EX', 86400 * 7 // 7 days ); // Step 4: Queue for processing await this.crisisQueue.add('process', crisisEvent, { priority: this.getPriority(crisisEvent.severity), }); // Step 5: Start monitoring await this.monitoringQueue.add( 'monitor', { crisisId: crisisEvent.id }, { repeat: { every: 300000 }, // Every 5 minutes } ); // Step 6: Alert crisis team await this.alertTeam(crisisEvent); return crisisEvent.id; } private async analyzeCrisis(description: string) { const prompt = `Analyze this crisis situation: ${description} Provide: 1. Severity (low/medium/high/critical) 2. Crisis type (data_breach, service_outage, pr_issue, etc.) 3. Estimated impact (number of affected users) 4. Recommended response time (immediate/1hour/4hours/24hours) 5. Legal review required (yes/no) Output as JSON.`; const response = await this.anthropic.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 1024, messages: [{ role: 'user', content: prompt }], }); const content = response.content[0]; if (content.type !== 'text') throw new Error('Invalid response'); return JSON.parse(content.text); } private async processCrisis(crisis: CrisisEvent) { // Generate messages const messages = await this.generateMessages(crisis.description); // Route for approval if needed if (crisis.severity === 'high' || crisis.severity === 'critical') { const approvalId = await this.requestApproval(crisis, messages); await this.redis.set( `approval:${crisis.id}`, approvalId, 'EX', 3600 ); return; } // Auto-publish for low/medium severity await this.publishMessages(crisis.id, messages); } private async generateMessages(description: string) { const prompt = `Generate crisis messages for all channels. Crisis: ${description} Generate: 1. Twitter (under 280 chars, include status page link) 2. LinkedIn (professional, 200-300 words) 3. Email subject + body (detailed, empathetic) 4. SMS (under 160 chars) 5. Slack internal update (for team) 6. Status page update (technical details) Output as JSON.`; const response = await this.anthropic.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 3000, messages: [{ role: 'user', content: prompt }], }); const content = response.content[0]; if (content.type !== 'text') throw new Error('Invalid response'); return JSON.parse(content.text); } private async publishMessages(crisisId: string, messages: any) { const results = await Promise.allSettled([ this.publishToTwitter(messages.twitter), this.publishToLinkedIn(messages.linkedin), this.sendEmails(messages.email_subject, messages.email_body), this.sendSMS(messages.sms), this.updateStatusPage(messages.status_page), this.notifySlack(messages.slack), ]); // Log results const publishLog = { crisis_id: crisisId, published_at: new Date(), channels: results.map((r, i) => ({ channel: ['twitter', 'linkedin', 'email', 'sms', 'statuspage', 'slack'][i], status: r.status, error: r.status === 'rejected' ? (r as PromiseRejectedResult).reason : null, })), }; await this.redis.lpush( `crisis:${crisisId}:publish_log`, JSON.stringify(publishLog) ); return publishLog; } private async monitorImpact(crisisId: string) { // Fetch current metrics from various sources const metrics = await Promise.all([ this.getSocialMediaReach(crisisId), this.analyzeSentiment(crisisId), this.getMediaMentions(crisisId), this.getSupportTicketCount(crisisId), ]); const impactMetrics: ImpactMetrics = { social_reach: metrics[0], sentiment_score: metrics[1], media_mentions: metrics[2], support_tickets: metrics[3], customer_calls: await this.getCallVolume(), }; // Update crisis record const crisisData = await this.redis.get(`crisis:${crisisId}`); if (crisisData) { const crisis = JSON.parse(crisisData); crisis.impact_metrics = impactMetrics; await this.redis.set( `crisis:${crisisId}`, JSON.stringify(crisis), 'KEEPTTL' ); } // Alert if metrics exceed thresholds if (impactMetrics.sentiment_score < -0.5) { await this.alertTeam({ id: crisisId, description: 'Negative sentiment detected', severity: 'high', detected_at: new Date(), channels: [], impact_metrics: impactMetrics, }); } return impactMetrics; } private async getSocialMediaReach(crisisId: string): Promise<number> { // Aggregate reach across Twitter, LinkedIn, etc. // Implementation depends on your social media APIs return 125000; // Example } private async analyzeSentiment(crisisId: string): Promise<number> { // Use sentiment analysis API to analyze mentions // Return score from -1 (very negative) to 1 (very positive) return -0.3; // Example: slightly negative } private async getMediaMentions(crisisId: string): Promise<number> { // Query media monitoring service return 23; // Example } private async getSupportTicketCount(crisisId: string): Promise<number> { // Query support ticket system return 456; // Example } private async getCallVolume(): Promise<number> { // Query phone system for call volume return 89; // Example } private async alertTeam(crisis: CrisisEvent) { const message = `šØ CRISIS ALERT šØ\n\nSeverity: ${crisis.severity.toUpperCase()}\nID: ${crisis.id}\nDescription: ${crisis.description}\n\nDashboard: https://crisis-dashboard.company.com/${crisis.id}`; await this.slack.chat.postMessage({ channel: process.env.SLACK_CRISIS_CHANNEL!, text: message, attachments: crisis.impact_metrics ? [ { color: crisis.severity === 'critical' ? 'danger' : 'warning', fields: [ { title: 'Social Reach', value: crisis.impact_metrics.social_reach.toLocaleString(), short: true, }, { title: 'Sentiment', value: crisis.impact_metrics.sentiment_score.toFixed(2), short: true, }, { title: 'Media Mentions', value: crisis.impact_metrics.media_mentions.toString(), short: true, }, { title: 'Support Tickets', value: crisis.impact_metrics.support_tickets.toString(), short: true, }, ], }, ] : [], }); } private determineChannels(severity: string): string[] { const baseChannels = ['twitter', 'statuspage']; if (severity === 'high' || severity === 'critical') { return [...baseChannels, 'linkedin', 'email', 'sms']; } return baseChannels; } private getPriority(severity: string): number { const priorities = { critical: 1, high: 2, medium: 3, low: 4 }; return priorities[severity as keyof typeof priorities] || 5; } // Placeholder methods for actual publishing private async publishToTwitter(message: string) { // Implementation } private async publishToLinkedIn(message: string) { // Implementation } private async sendEmails(subject: string, body: string) { // Implementation } private async sendSMS(message: string) { // Implementation } private async updateStatusPage(message: string) { // Implementation } private async notifySlack(message: string) { // Implementation } private async requestApproval(crisis: CrisisEvent, messages: any) { // Implementation return 'approval_123'; } } // Usage const system = new ProductionCrisisSystem(); const crisisId = await system.detectAndRespond( 'Payment breach, 12K cards exposed...' ); console.log(`Crisis ${crisisId} being processed`);
When to Level Up
Start: Simple Multi-Channel
1-5 crises/year
- Basic multi-channel publishing (Twitter, LinkedIn, Email, SMS)
- Manual approval via email or Slack
- Sequential publishing (one channel at a time)
- Basic logging with console.log
Scale: Add Approval Workflows
5-20 crises/year
- Automated approval routing based on severity
- Email-based approval with one-click links
- Validation checks (legal review, factual accuracy)
- Auto-publish after all required approvals
- Approval timeout handling (escalate after 30 mins)
Production: Real-Time Monitoring
20-50 crises/year
- Queue-based processing with BullMQ (handles spikes)
- Real-time impact monitoring (sentiment, reach, tickets)
- Automated follow-up scheduling (4hr, 24hr, 7day updates)
- Redis for state management (no data loss on crashes)
- Slack alerts with live metrics dashboard
Enterprise: Multi-Region System
50+ crises/year
- Multi-region deployment (publish from closest datacenter)
- AI-powered crisis detection (monitors social media, news, support tickets)
- Predictive impact modeling (forecast reach and sentiment trends)
- Multi-language support (auto-translate for global teams)
- Compliance automation (GDPR, SEC, industry-specific regulations)
- Integration with crisis simulation training systems
Leadership-Specific Gotchas
The code examples above work. But crisis communication has unique challenges you need to handle.
Approval Timeout Handling - Don't Wait Forever
If CEO doesn't approve within 30 minutes during a critical crisis, you need escalation rules. Auto-escalate to COO or Board member. Don't let a crisis spiral because someone's in a meeting.
async function handleApprovalTimeout(approvalId: string) { const approval = await getApproval(approvalId); const elapsed = Date.now() - approval.created_at; // If 30 minutes passed and CEO hasn't approved if (elapsed > 30 * 60 * 1000 && !approval.ceo_approved) { // Escalate to COO await sendUrgentApprovalRequest({ to: 'coo@company.com', subject: 'URGENT: Crisis Approval Escalation', body: `CEO hasn't responded to crisis approval in 30 mins.\n\nCrisis: ${approval.description}\n\nApprove now: ${approval.approve_link}`, priority: 'urgent' }); // Also call COO's cell phone await makePhoneCall({ to: process.env.COO_CELL_PHONE!, message: 'Urgent crisis approval needed. Check email immediately.' }); // Log escalation await logEscalation(approvalId, 'CEO_timeout', 'escalated_to_COO'); } // If 1 hour passed and still no approval, auto-publish with disclaimer if (elapsed > 60 * 60 * 1000 && !approval.any_approved) { await publishWithDisclaimer(approval.messages, { note: 'Published without full executive approval due to time-critical nature' }); // Alert board await alertBoard(approvalId, 'auto_published_after_timeout'); } }
Rate Limiting Across Multiple Social Platforms
Twitter allows 300 tweets/3hrs. LinkedIn has different limits. If you're publishing updates every hour for 24 hours, you'll hit limits. Implement smart queuing and batching.
from ratelimit import limits, sleep_and_retry import time class SocialMediaPublisher: def __init__(self): self.twitter_calls = 0 self.linkedin_calls = 0 self.last_twitter_reset = time.time() self.last_linkedin_reset = time.time() @sleep_and_retry @limits(calls=300, period=10800) # 300 calls per 3 hours def publish_to_twitter(self, message: str): """Rate-limited Twitter publishing""" # Actual Twitter API call twitter_client.tweet(message) self.twitter_calls += 1 # Log rate limit usage print(f"Twitter: {self.twitter_calls}/300 calls used") @sleep_and_retry @limits(calls=100, period=86400) # 100 calls per day def publish_to_linkedin(self, message: str): """Rate-limited LinkedIn publishing""" linkedin_client.create_share(message) self.linkedin_calls += 1 print(f"LinkedIn: {self.linkedin_calls}/100 calls used") async def publish_with_retry(self, message: str, platform: str): """Retry with exponential backoff if rate limited""" max_retries = 3 for attempt in range(max_retries): try: if platform == 'twitter': self.publish_to_twitter(message) elif platform == 'linkedin': self.publish_to_linkedin(message) return True except RateLimitException: if attempt < max_retries - 1: wait_time = 2 ** attempt * 60 # 1min, 2min, 4min print(f"Rate limited. Waiting {wait_time}s...") await asyncio.sleep(wait_time) else: # Queue for later await self.queue_for_later(message, platform) return False
Sentiment Analysis During Active Crisis
Social media sentiment changes fast during a crisis. You need real-time monitoring to know if your response is working or making things worse. Use streaming APIs and sentiment analysis.
import { TwitterApi } from 'twitter-api-v2'; import Anthropic from '@anthropic-ai/sdk'; class CrisisSentimentMonitor { private twitter: TwitterApi; private anthropic: Anthropic; private sentimentHistory: Array<{ timestamp: Date; score: number }> = []; async monitorHashtag(hashtag: string, crisisId: string) { // Start streaming mentions const stream = await this.twitter.v2.searchStream({ 'tweet.fields': ['created_at', 'text', 'public_metrics'], expansions: ['author_id'], }); stream.on('data', async (tweet) => { if (tweet.data.text.includes(hashtag)) { // Analyze sentiment const sentiment = await this.analyzeSentiment(tweet.data.text); this.sentimentHistory.push({ timestamp: new Date(), score: sentiment.score, }); // Check if sentiment is deteriorating if (this.isGettingWorse()) { await this.alertLeadership({ crisis_id: crisisId, message: 'Sentiment declining - consider revised messaging', current_score: sentiment.score, trend: 'negative', sample_tweets: await this.getMostNegativeTweets(5), }); } } }); } private async analyzeSentiment(text: string) { const response = await this.anthropic.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 256, messages: [ { role: 'user', content: `Analyze sentiment of this tweet about our crisis response. Score from -1 (very negative) to 1 (very positive). Output JSON: {score: number, reasoning: string}\n\nTweet: ${text}`, }, ], }); const content = response.content[0]; if (content.type !== 'text') throw new Error('Invalid response'); return JSON.parse(content.text); } private isGettingWorse(): boolean { if (this.sentimentHistory.length < 10) return false; // Compare last 5 tweets to previous 5 const recent = this.sentimentHistory.slice(-5); const previous = this.sentimentHistory.slice(-10, -5); const recentAvg = recent.reduce((sum, s) => sum + s.score, 0) / 5; const previousAvg = previous.reduce((sum, s) => sum + s.score, 0) / 5; // Alert if sentiment dropped by more than 0.2 return recentAvg < previousAvg - 0.2; } private async getMostNegativeTweets(count: number) { // Return most negative tweets for context return this.sentimentHistory .sort((a, b) => a.score - b.score) .slice(0, count); } private async alertLeadership(alert: any) { // Send to Slack crisis channel await fetch(process.env.SLACK_WEBHOOK_URL!, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: `ā ļø Sentiment Alert: ${alert.message}`, attachments: [ { color: 'danger', fields: [ { title: 'Current Score', value: alert.current_score.toFixed(2), short: true }, { title: 'Trend', value: alert.trend, short: true }, ], }, ], }), }); } }
Legal Compliance for Public Statements
Public companies have SEC disclosure requirements. Healthcare has HIPAA. Finance has specific regulations. Your crisis messages must comply. Build compliance checks into your approval workflow.
from typing import List, Dict import re class ComplianceChecker: def __init__(self, industry: str): self.industry = industry self.violations = [] async def check_compliance(self, message: str, severity: str) -> Dict: """Run all compliance checks based on industry""" self.violations = [] if self.industry == 'public_company': await self.check_sec_compliance(message, severity) elif self.industry == 'healthcare': await self.check_hipaa_compliance(message) elif self.industry == 'finance': await self.check_finra_compliance(message) # Universal checks await self.check_for_promises(message) await self.check_for_speculation(message) return { 'compliant': len(self.violations) == 0, 'violations': self.violations, 'requires_legal_review': len(self.violations) > 0 or severity == 'critical' } async def check_sec_compliance(self, message: str, severity: str): """SEC disclosure requirements for public companies""" # Material events must be disclosed via 8-K filing material_keywords = ['breach', 'lawsuit', 'investigation', 'fraud', 'bankruptcy'] if any(keyword in message.lower() for keyword in material_keywords): self.violations.append({ 'type': 'SEC_8K_REQUIRED', 'message': 'Material event requires 8-K filing before public disclosure', 'severity': 'critical', 'action': 'File 8-K with SEC before publishing' }) # Forward-looking statements need safe harbor language future_keywords = ['will', 'expect', 'anticipate', 'plan', 'forecast'] if any(keyword in message.lower() for keyword in future_keywords): if 'forward-looking' not in message.lower(): self.violations.append({ 'type': 'SAFE_HARBOR_MISSING', 'message': 'Forward-looking statements need safe harbor disclaimer', 'severity': 'medium', 'action': 'Add: "These are forward-looking statements subject to risks..."' }) async def check_hipaa_compliance(self, message: str): """HIPAA compliance for healthcare""" # Check for PHI (Protected Health Information) phi_patterns = [ r'\b\d{3}-\d{2}-\d{4}\b', # SSN r'\b[A-Z][a-z]+ [A-Z][a-z]+\b', # Names r'\b\d{1,5}\s\w+\s(Street|St|Avenue|Ave|Road|Rd)\b', # Addresses ] for pattern in phi_patterns: if re.search(pattern, message): self.violations.append({ 'type': 'PHI_DETECTED', 'message': 'Potential PHI found in message', 'severity': 'critical', 'action': 'Remove or redact PHI before publishing' }) async def check_for_promises(self, message: str): """Don't make promises you can't keep""" promise_phrases = [ 'guarantee', 'promise', 'will never happen again', 'completely resolved', 'fully secure' ] for phrase in promise_phrases: if phrase in message.lower(): self.violations.append({ 'type': 'OVERPROMISE', 'message': f'Phrase "{phrase}" makes absolute promise', 'severity': 'medium', 'action': 'Use "working to" or "committed to" instead' }) async def check_for_speculation(self, message: str): """Avoid speculation about causes or responsibility""" speculation_phrases = [ 'we believe the cause was', 'likely caused by', 'probably due to' ] for phrase in speculation_phrases: if phrase in message.lower(): self.violations.append({ 'type': 'SPECULATION', 'message': 'Message contains speculation about cause', 'severity': 'high', 'action': 'Wait for investigation results before stating cause' }) # Usage checker = ComplianceChecker(industry='public_company') result = await checker.check_compliance(message, severity='high') if not result['compliant']: # Block publication and route to legal await route_to_legal(result['violations']) else: # Proceed with approval workflow await request_approvals(message)
Multi-Language Support for Global Teams
If you operate globally, your crisis affects teams in different countries. Auto-translate your crisis messages to local languages, but have native speakers review before publishing.
import Anthropic from '@anthropic-ai/sdk'; interface TranslationRequest { text: string; target_languages: string[]; requires_review: boolean; } class GlobalCrisisPublisher { private anthropic: Anthropic; private native_reviewers: Map<string, string[]>; constructor() { this.anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, }); // Map of language -> reviewer emails this.native_reviewers = new Map([ ['es', ['maria@company.com', 'carlos@company.com']], ['fr', ['pierre@company.com']], ['de', ['hans@company.com']], ['ja', ['yuki@company.com']], ['zh', ['wei@company.com']], ]); } async translateAndPublish(request: TranslationRequest) { const translations = new Map<string, string>(); // Translate to all target languages for (const lang of request.target_languages) { const translated = await this.translateMessage(request.text, lang); translations.set(lang, translated); } if (request.requires_review) { // Send to native speakers for review const reviewIds = await this.requestNativeReview(translations); // Wait for reviews (with timeout) const approved = await this.waitForReviews(reviewIds, 3600000); // 1 hour if (!approved) { // Publish English only if reviews timeout await this.publishEnglishOnly(request.text); return { status: 'partial', published_languages: ['en'] }; } } // Publish all translations await this.publishAllLanguages(translations); return { status: 'complete', published_languages: Array.from(translations.keys()) }; } private async translateMessage(text: string, targetLang: string): Promise<string> { const langNames: Record<string, string> = { es: 'Spanish', fr: 'French', de: 'German', ja: 'Japanese', zh: 'Chinese', }; const prompt = `Translate this crisis communication message to ${langNames[targetLang]}. Maintain the tone (urgent but professional) and ensure cultural appropriateness.\n\nOriginal message:\n${text}\n\nTranslated message:`; const response = await this.anthropic.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 1024, messages: [{ role: 'user', content: prompt }], }); const content = response.content[0]; if (content.type !== 'text') throw new Error('Invalid response'); return content.text; } private async requestNativeReview( translations: Map<string, string> ): Promise<string[]> { const reviewIds: string[] = []; for (const [lang, text] of translations.entries()) { const reviewers = this.native_reviewers.get(lang) || []; for (const reviewer of reviewers) { const reviewId = `review_${Date.now()}_${lang}_${reviewer}`; await this.sendReviewRequest({ reviewId, reviewer, language: lang, text, deadline: new Date(Date.now() + 3600000), // 1 hour }); reviewIds.push(reviewId); } } return reviewIds; } private async sendReviewRequest(request: any) { // Send email to native speaker const emailBody = `URGENT: Crisis Message Translation Review Language: ${request.language} Deadline: ${request.deadline.toISOString()} Translated message: ${request.text} Approve: https://crisis.company.com/review/${request.reviewId}/approve Request changes: https://crisis.company.com/review/${request.reviewId}/edit This is time-sensitive. Please review within 1 hour.`; // Send via email API await sendEmail({ to: request.reviewer, subject: `URGENT: ${request.language.toUpperCase()} Translation Review`, body: emailBody, priority: 'urgent', }); } private async waitForReviews( reviewIds: string[], timeout: number ): Promise<boolean> { const startTime = Date.now(); while (Date.now() - startTime < timeout) { const allApproved = await this.checkAllReviewsApproved(reviewIds); if (allApproved) return true; await new Promise((resolve) => setTimeout(resolve, 10000)); // Check every 10s } return false; // Timeout } private async checkAllReviewsApproved(reviewIds: string[]): Promise<boolean> { // Check database for review status // Implementation depends on your DB return false; } private async publishEnglishOnly(text: string) { // Fallback: publish English version only await this.publishToAllChannels({ en: text }); } private async publishAllLanguages(translations: Map<string, string>) { // Publish to regional social media accounts const promises = []; for (const [lang, text] of translations.entries()) { promises.push(this.publishToRegionalChannels(lang, text)); } await Promise.all(promises); } private async publishToRegionalChannels(lang: string, text: string) { // Publish to language-specific Twitter/LinkedIn accounts // e.g., @CompanyES for Spanish, @CompanyFR for French } private async publishToAllChannels(translations: Record<string, string>) { // Implementation } } // Usage const publisher = new GlobalCrisisPublisher(); await publisher.translateAndPublish({ text: 'We experienced a security incident...', target_languages: ['es', 'fr', 'de', 'ja'], requires_review: true, });
Cost Calculator
Manual Crisis Response
Limitations:
- ⢠2-4 hour response time (too slow)
- ⢠Inconsistent messaging across channels
- ⢠No real-time impact tracking
- ⢠Approval bottlenecks
- ⢠Can't scale beyond 5-10 crises/year
Automated Crisis System
Benefits:
- ā 30-second to 5-minute response time
- ā Consistent messaging across all channels
- ā Real-time sentiment tracking
- ā Automated approval workflows
- ā Scales to 50+ crises/year easily
- ā Detailed impact analytics