
Communication Systems
- 43 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
communication-systems is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- communication-systems
- AI & Agent Building
- AI-coding skill
Communication Systems by the numbers
- 43 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #7,965 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill communication-systemsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 43 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Communication Systems
Overview
Building email systems, push notifications, in-app messaging, and webhook integrations.
---
Email Systems
Transactional Email
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
interface EmailOptions {
to: string | string[];
subject: string;
html?: string;
text?: string;
template?: string;
data?: Record<string, any>;
attachments?: Array<{
filename: string;
content: Buffer | string;
}>;
}
async function sendEmail(options: EmailOptions) {
let html = options.html;
// Use template if specified
if (options.template) {
html = await renderTemplate(options.template, options.data);
}
const { data, error } = await resend.emails.send({
from: 'noreply@example.com',
to: options.to,
subject: options.subject,
html,
text: options.text,
attachments: options.attachments,
});
if (error) {
console.error('Email send failed:', error);
throw error;
}
// Log for tracking
await prisma.emailLog.create({
data: {
messageId: data.id,
to: Array.isArray(options.to) ? options.to.join(',') : options.to,
subject: options.subject,
template: options.template,
status: 'sent',
},
});
return data;
}
// Email templates with React Email
import { render } from '@react-email/render';
import { WelcomeEmail } from './templates/WelcomeEmail';
import { PasswordResetEmail } from './templates/PasswordResetEmail';
const templates = {
welcome: WelcomeEmail,
passwordReset: PasswordResetEmail,
};
async function renderTemplate(name: string, data: Record<string, any>) {
const Template = templates[name];
if (!Template) throw new Error(`Template ${name} not found`);
return render(<Template {...data} />);
}
// React Email template
import {
Html, Head, Body, Container, Text, Button, Img,
} from '@react-email/components';
function WelcomeEmail({ name, actionUrl }: { name: string; actionUrl: string }) {
return (
<Html>
<Head />
<Body style={{ fontFamily: 'Arial, sans-serif' }}>
<Container>
<Img src="https://example.com/logo.png" width="120" height="40" alt="Logo" />
<Text>Hi {name},</Text>
<Text>Welcome to our platform! Get started by setting up your account.</Text>
<Button
href={actionUrl}
style={{ background: '#007bff', color: '#fff', padding: '12px 24px' }}
>
Get Started
</Button>
</Container>
</Body>
</Html>
);
}Email Queue
import Bull from 'bull';
const emailQueue = new Bull('email', process.env.REDIS_URL);
// Add to queue
async function queueEmail(options: EmailOptions, delay?: number) {
return emailQueue.add('send', options, {
delay,
attempts: 3,
backoff: { type: 'exponential', delay: 60000 },
});
}
// Process queue
emailQueue.process('send', async (job) => {
await sendEmail(job.data);
});
// Handle failures
emailQueue.on('failed', async (job, error) => {
console.error(`Email job ${job.id} failed:`, error);
await prisma.emailLog.update({
where: { jobId: job.id },
data: { status: 'failed', error: error.message },
});
});
// Bulk email with rate limiting
async function sendBulkEmail(recipients: string[], template: string, data: Record<string, any>) {
const jobs = recipients.map((to, index) => ({
name: 'send',
data: { to, template, data },
opts: { delay: index * 100 }, // Stagger sends
}));
await emailQueue.addBulk(jobs);
}---
Push Notifications
Web Push
import webpush from 'web-push';
webpush.setVapidDetails(
'mailto:admin@example.com',
process.env.VAPID_PUBLIC_KEY!,
process.env.VAPID_PRIVATE_KEY!
);
interface PushSubscription {
endpoint: string;
keys: {
p256dh: string;
auth: string;
};
}
// Store subscription
async function saveSubscription(userId: string, subscription: PushSubscription) {
await prisma.pushSubscription.upsert({
where: { endpoint: subscription.endpoint },
update: { keys: subscription.keys },
create: {
userId,
endpoint: subscription.endpoint,
keys: subscription.keys,
},
});
}
// Send push notification
async function sendPush(userId: string, payload: {
title: string;
body: string;
icon?: string;
url?: string;
data?: Record<string, any>;
}) {
const subscriptions = await prisma.pushSubscription.findMany({
where: { userId },
});
const results = await Promise.allSettled(
subscriptions.map(async (sub) => {
try {
await webpush.sendNotification(
{ endpoint: sub.endpoint, keys: sub.keys },
JSON.stringify(payload)
);
} catch (error) {
if (error.statusCode === 410) {
// Subscription expired, remove it
await prisma.pushSubscription.delete({ where: { id: sub.id } });
}
throw error;
}
})
);
return results;
}
// Service worker handler
// public/sw.js
self.addEventListener('push', (event) => {
const data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: data.icon || '/icon-192.png',
data: data,
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.notification.data.url) {
event.waitUntil(clients.openWindow(event.notification.data.url));
}
});Mobile Push (FCM)
import admin from 'firebase-admin';
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
interface MobileNotification {
title: string;
body: string;
imageUrl?: string;
data?: Record<string, string>;
}
async function sendMobilePush(
tokens: string[],
notification: MobileNotification
) {
const message: admin.messaging.MulticastMessage = {
tokens,
notification: {
title: notification.title,
body: notification.body,
imageUrl: notification.imageUrl,
},
data: notification.data,
android: {
priority: 'high',
notification: {
sound: 'default',
clickAction: 'OPEN_ACTIVITY',
},
},
apns: {
payload: {
aps: {
sound: 'default',
badge: 1,
},
},
},
};
const response = await admin.messaging().sendEachForMulticast(message);
// Handle failures
response.responses.forEach((resp, idx) => {
if (!resp.success) {
const errorCode = resp.error?.code;
if (
errorCode === 'messaging/invalid-registration-token' ||
errorCode === 'messaging/registration-token-not-registered'
) {
// Remove invalid token
removeDeviceToken(tokens[idx]);
}
}
});
return response;
}---
In-App Notifications
interface Notification {
id: string;
userId: string;
type: string;
title: string;
message: string;
data?: Record<string, any>;
read: boolean;
createdAt: Date;
}
// Create notification
async function createNotification(params: {
userId: string;
type: string;
title: string;
message: string;
data?: Record<string, any>;
}) {
const notification = await prisma.notification.create({
data: {
...params,
read: false,
},
});
// Send real-time update
await pubsub.publish(`notifications:${params.userId}`, {
type: 'NEW_NOTIFICATION',
notification,
});
return notification;
}
// Get notifications with pagination
async function getNotifications(userId: string, options: {
page?: number;
limit?: number;
unreadOnly?: boolean;
}) {
const { page = 1, limit = 20, unreadOnly = false } = options;
const where = {
userId,
...(unreadOnly && { read: false }),
};
const [notifications, total, unreadCount] = await Promise.all([
prisma.notification.findMany({
where,
orderBy: { createdAt: 'desc' },
skip: (page - 1) * limit,
take: limit,
}),
prisma.notification.count({ where }),
prisma.notification.count({ where: { userId, read: false } }),
]);
return { notifications, total, unreadCount };
}
// Mark as read
async function markAsRead(userId: string, notificationIds: string[]) {
await prisma.notification.updateMany({
where: {
id: { in: notificationIds },
userId,
},
data: { read: true },
});
}
// React hook for notifications
function useNotifications() {
const [notifications, setNotifications] = useState<Notification[]>([]);
const [unreadCount, setUnreadCount] = useState(0);
useEffect(() => {
// Initial fetch
fetchNotifications().then(({ notifications, unreadCount }) => {
setNotifications(notifications);
setUnreadCount(unreadCount);
});
// Subscribe to real-time updates
const unsubscribe = subscribeToNotifications((notification) => {
setNotifications((prev) => [notification, ...prev]);
setUnreadCount((prev) => prev + 1);
});
return unsubscribe;
}, []);
return { notifications, unreadCount, markAsRead };
}---
Webhooks
interface Webhook {
id: string;
url: string;
secret: string;
events: string[];
active: boolean;
}
// Register webhook
async function registerWebhook(params: {
url: string;
events: string[];
}) {
const secret = crypto.randomBytes(32).toString('hex');
return prisma.webhook.create({
data: {
url: params.url,
events: params.events,
secret,
active: true,
},
});
}
// Send webhook
async function sendWebhook(webhookId: string, event: string, payload: any) {
const webhook = await prisma.webhook.findUnique({ where: { id: webhookId } });
if (!webhook || !webhook.active) return;
const timestamp = Date.now().toString();
const body = JSON.stringify({ event, data: payload, timestamp });
// Create signature
const signature = crypto
.createHmac('sha256', webhook.secret)
.update(body)
.digest('hex');
try {
const response = await fetch(webhook.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': signature,
'X-Webhook-Timestamp': timestamp,
},
body,
});
await prisma.webhookLog.create({
data: {
webhookId,
event,
payload,
responseStatus: response.status,
success: response.ok,
},
});
// Disable after multiple failures
if (!response.ok) {
await handleWebhookFailure(webhookId);
}
} catch (error) {
await prisma.webhookLog.create({
data: {
webhookId,
event,
payload,
error: error.message,
success: false,
},
});
await handleWebhookFailure(webhookId);
}
}
// Verify webhook signature (receiver side)
function verifyWebhookSignature(
body: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}---
Related Skills
- [[realtime-systems]] - Real-time messaging
- [[backend]] - API development
- [[reliability-engineering]] - Delivery guarantees
<!DOCTYPE html>
<!--
Responsive Email Template
Usage: Copy and customize for transactional/marketing emails
Compatible: Gmail, Outlook, Apple Mail, Yahoo
Variables to replace:
- {{preheader}} - Preview text
- {{logo_url}} - Company logo URL
- {{company_name}} - Company name
- {{headline}} - Main headline
- {{content}} - Main content
- {{cta_url}} - CTA button URL
- {{cta_text}} - CTA button text
- {{footer_address}} - Company address
- {{unsubscribe_url}} - Unsubscribe link
- {{current_year}} - Current year
-->
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="x-apple-disable-message-reformatting">
<meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no">
<title>{{company_name}}</title>
<!--[if mso]>
<noscript>
<xml>
<o:OfficeDocumentSettings>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
</noscript>
<![endif]-->
<style>
/* Reset styles */
body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
/* Base styles */
body {
margin: 0 !important;
padding: 0 !important;
width: 100% !important;
background-color: #f4f4f4;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
body, .email-bg { background-color: #1a1a1a !important; }
.email-container { background-color: #2d2d2d !important; }
h1, h2, h3, p, td { color: #ffffff !important; }
.footer-text { color: #999999 !important; }
}
/* Responsive styles */
@media screen and (max-width: 600px) {
.email-container { width: 100% !important; max-width: 100% !important; }
.fluid { max-width: 100% !important; height: auto !important; margin-left: auto !important; margin-right: auto !important; }
.stack-column { display: block !important; width: 100% !important; max-width: 100% !important; }
.stack-column-center { text-align: center !important; }
.center-on-narrow { text-align: center !important; display: block !important; margin-left: auto !important; margin-right: auto !important; float: none !important; }
table.center-on-narrow { display: inline-block !important; }
.padding-mobile { padding: 20px !important; }
}
</style>
</head>
<body style="margin: 0; padding: 0; background-color: #f4f4f4;">
<!-- Preheader (hidden preview text) -->
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">
{{preheader}}
͏‌ ͏‌ ͏‌ ͏‌ ͏‌
</div>
<!-- Email wrapper -->
<table role="presentation" class="email-bg" width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color: #f4f4f4;">
<tr>
<td align="center" style="padding: 20px 10px;">
<!-- Email container -->
<table role="presentation" class="email-container" width="600" cellpadding="0" cellspacing="0" border="0" style="background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
<!-- Header -->
<tr>
<td style="padding: 30px 40px; text-align: center; background-color: #ffffff;">
<a href="https://example.com" target="_blank">
<img src="{{logo_url}}" alt="{{company_name}}" width="150" style="max-width: 150px; height: auto;">
</a>
</td>
</tr>
<!-- Hero Section -->
<tr>
<td class="padding-mobile" style="padding: 20px 40px 30px 40px;">
<h1 style="margin: 0 0 20px 0; font-size: 28px; font-weight: 700; color: #1a1a1a; line-height: 1.3;">
{{headline}}
</h1>
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #4a4a4a;">
{{content}}
</p>
<!-- CTA Button -->
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" style="margin: 0 auto;">
<tr>
<td style="border-radius: 6px; background-color: #0066cc;">
<a href="{{cta_url}}" target="_blank" style="display: inline-block; padding: 14px 32px; font-size: 16px; font-weight: 600; color: #ffffff; text-decoration: none; border-radius: 6px;">
{{cta_text}}
</a>
</td>
</tr>
</table>
</td>
</tr>
<!-- Divider -->
<tr>
<td style="padding: 0 40px;">
<hr style="border: none; border-top: 1px solid #e5e5e5; margin: 0;">
</td>
</tr>
<!-- Footer -->
<tr>
<td class="padding-mobile" style="padding: 30px 40px;">
<p class="footer-text" style="margin: 0 0 10px 0; font-size: 13px; line-height: 1.5; color: #888888; text-align: center;">
{{footer_address}}
</p>
<p class="footer-text" style="margin: 0; font-size: 13px; line-height: 1.5; color: #888888; text-align: center;">
<a href="{{unsubscribe_url}}" style="color: #888888; text-decoration: underline;">Unsubscribe</a>
|
<a href="https://example.com/preferences" style="color: #888888; text-decoration: underline;">Email Preferences</a>
|
<a href="https://example.com/privacy" style="color: #888888; text-decoration: underline;">Privacy Policy</a>
</p>
<p class="footer-text" style="margin: 15px 0 0 0; font-size: 12px; color: #aaaaaa; text-align: center;">
© {{current_year}} {{company_name}}. All rights reserved.
</p>
</td>
</tr>
</table>
<!-- End email container -->
</td>
</tr>
</table>
<!-- End email wrapper -->
</body>
</html>
/**
* Notification System Types
* Usage: Core types for multi-channel notification systems
*/
// ===========================================
// Notification Core
// ===========================================
export interface Notification {
id: string;
type: NotificationType;
channel: NotificationChannel;
recipient: Recipient;
content: NotificationContent;
priority: Priority;
status: NotificationStatus;
scheduling: SchedulingConfig;
tracking: TrackingData;
metadata: Record<string, unknown>;
createdAt: Date;
sentAt?: Date;
deliveredAt?: Date;
readAt?: Date;
}
export type NotificationType =
| 'transactional' // Order confirmations, receipts
| 'marketing' // Promotions, newsletters
| 'system' // Security alerts, maintenance
| 'social' // Comments, mentions, follows
| 'reminder' // Appointments, deadlines
| 'digest'; // Daily/weekly summaries
export type NotificationChannel =
| 'email'
| 'sms'
| 'push'
| 'in_app'
| 'slack'
| 'webhook';
export type Priority = 'low' | 'normal' | 'high' | 'urgent';
export type NotificationStatus =
| 'pending'
| 'queued'
| 'sending'
| 'sent'
| 'delivered'
| 'failed'
| 'bounced'
| 'cancelled';
// ===========================================
// Recipient
// ===========================================
export interface Recipient {
id: string;
type: 'user' | 'group' | 'segment';
email?: string;
phone?: string;
deviceTokens?: string[];
preferences: NotificationPreferences;
timezone: string;
locale: string;
}
export interface NotificationPreferences {
channels: {
[K in NotificationChannel]?: boolean;
};
types: {
[K in NotificationType]?: boolean;
};
quietHours?: {
enabled: boolean;
start: string; // HH:mm
end: string; // HH:mm
};
frequency?: 'instant' | 'hourly' | 'daily' | 'weekly';
}
// ===========================================
// Content
// ===========================================
export interface NotificationContent {
template?: string;
subject?: string; // Email subject
title?: string; // Push/in-app title
body: string;
html?: string; // Rich HTML content
data?: Record<string, unknown>; // Dynamic data for templates
actions?: NotificationAction[];
attachments?: Attachment[];
}
export interface NotificationAction {
id: string;
label: string;
url?: string;
action?: string; // Custom action identifier
style?: 'primary' | 'secondary' | 'danger';
}
export interface Attachment {
filename: string;
content: string; // Base64 or URL
contentType: string;
size?: number;
}
// ===========================================
// Email Specific
// ===========================================
export interface EmailNotification extends Notification {
channel: 'email';
email: {
from: EmailAddress;
to: EmailAddress[];
cc?: EmailAddress[];
bcc?: EmailAddress[];
replyTo?: EmailAddress;
headers?: Record<string, string>;
trackOpens: boolean;
trackClicks: boolean;
};
}
export interface EmailAddress {
email: string;
name?: string;
}
// ===========================================
// Push Notification Specific
// ===========================================
export interface PushNotification extends Notification {
channel: 'push';
push: {
platform: 'ios' | 'android' | 'web';
deviceTokens: string[];
badge?: number;
sound?: string;
icon?: string;
image?: string;
clickAction?: string;
data?: Record<string, string>;
ttl?: number; // Time to live in seconds
collapseKey?: string;
};
}
// ===========================================
// SMS Specific
// ===========================================
export interface SMSNotification extends Notification {
channel: 'sms';
sms: {
from: string;
to: string;
provider?: 'twilio' | 'nexmo' | 'aws_sns';
maxSegments?: number;
};
}
// ===========================================
// Scheduling
// ===========================================
export interface SchedulingConfig {
sendAt?: Date;
timezone?: string;
respectQuietHours: boolean;
retryPolicy?: RetryPolicy;
expiresAt?: Date;
}
export interface RetryPolicy {
maxAttempts: number;
backoffMs: number;
backoffMultiplier: number;
}
// ===========================================
// Tracking & Analytics
// ===========================================
export interface TrackingData {
messageId: string;
providerId?: string;
attempts: DeliveryAttempt[];
events: TrackingEvent[];
}
export interface DeliveryAttempt {
attemptNumber: number;
timestamp: Date;
status: 'success' | 'failed';
error?: string;
provider?: string;
}
export interface TrackingEvent {
type: TrackingEventType;
timestamp: Date;
metadata?: Record<string, unknown>;
}
export type TrackingEventType =
| 'queued'
| 'sent'
| 'delivered'
| 'opened'
| 'clicked'
| 'bounced'
| 'complained'
| 'unsubscribed';
// ===========================================
// Templates
// ===========================================
export interface NotificationTemplate {
id: string;
name: string;
description?: string;
channel: NotificationChannel;
type: NotificationType;
subject?: string;
content: string;
variables: TemplateVariable[];
version: number;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
}
export interface TemplateVariable {
name: string;
type: 'string' | 'number' | 'date' | 'boolean' | 'object';
required: boolean;
defaultValue?: unknown;
description?: string;
}
// ===========================================
// Batch Operations
// ===========================================
export interface NotificationBatch {
id: string;
name: string;
template: string;
recipients: Recipient[];
content: Partial<NotificationContent>;
scheduling: SchedulingConfig;
status: BatchStatus;
stats: BatchStats;
createdAt: Date;
completedAt?: Date;
}
export type BatchStatus = 'draft' | 'scheduled' | 'processing' | 'completed' | 'cancelled';
export interface BatchStats {
total: number;
sent: number;
delivered: number;
failed: number;
opened: number;
clicked: number;
}
Communication Systems Templates
Templates for email and notification systems.
Files
| Template | Purpose |
|---|---|
email-template.html | Responsive email HTML |
notification-types.ts | Notification system types |
Usage
Email Template
# Copy template
cp templates/email-template.html ./emails/base.html
# Replace variables in your templating system
{{preheader}} # Preview text
{{logo_url}} # Company logo
{{headline}} # Main headline
{{content}} # Body content
{{cta_url}} # Button URL
{{cta_text}} # Button textNotification Types
# Copy types
cp templates/notification-types.ts ./src/types/notifications.ts
# Import
import type { Notification, EmailNotification } from './types/notifications';Email Template Features
| Feature | Support |
|---|---|
| Dark Mode | CSS prefers-color-scheme |
| Responsive | Mobile-first, 600px breakpoint |
| Outlook | VML fallbacks |
| Gmail | Inline styles |
| Accessibility | Semantic HTML, alt text |
Tested Clients
- Gmail (Web, iOS, Android)
- Apple Mail (macOS, iOS)
- Outlook (Desktop, Web)
- Yahoo Mail
- Samsung Mail
Notification System
Channels
| Channel | Use Case |
|---|---|
email | Transactional, marketing |
sms | Urgent alerts, 2FA |
push | Real-time updates |
in_app | Non-urgent notifications |
slack | Team notifications |
webhook | System integrations |
Priority Levels
type Priority = 'low' | 'normal' | 'high' | 'urgent';
// urgent: Bypass quiet hours, immediate delivery
// high: Important, may wake device
// normal: Standard delivery
// low: Can be batched/digestedNotification Types
| Type | Example |
|---|---|
transactional | Order confirmation |
marketing | Newsletter |
system | Security alert |
social | New follower |
reminder | Appointment |
digest | Weekly summary |
Email Example
const notification: EmailNotification = {
id: 'notif_123',
type: 'transactional',
channel: 'email',
recipient: {
id: 'user_456',
email: 'user@example.com',
preferences: { ... },
timezone: 'America/New_York',
locale: 'en-US',
},
content: {
template: 'order-confirmation',
subject: 'Your order #{{orderId}} is confirmed',
data: {
orderId: 'ORD-12345',
items: [...],
total: 99.99,
},
},
email: {
from: { email: 'orders@example.com', name: 'Example Store' },
to: [{ email: 'user@example.com' }],
trackOpens: true,
trackClicks: true,
},
priority: 'high',
status: 'pending',
scheduling: { respectQuietHours: false },
tracking: { messageId: 'msg_789', attempts: [], events: [] },
metadata: {},
createdAt: new Date(),
};Push Notification Example
const push: PushNotification = {
// ... common fields
channel: 'push',
content: {
title: 'New message',
body: 'You have a new message from John',
actions: [
{ id: 'reply', label: 'Reply', action: 'REPLY_ACTION' },
{ id: 'dismiss', label: 'Dismiss' },
],
},
push: {
platform: 'ios',
deviceTokens: ['token_abc'],
badge: 5,
sound: 'default',
ttl: 3600,
},
};User Preferences
const preferences: NotificationPreferences = {
channels: {
email: true,
push: true,
sms: false,
},
types: {
marketing: false, // User opted out
transactional: true,
social: true,
},
quietHours: {
enabled: true,
start: '22:00',
end: '08:00',
},
frequency: 'instant',
};Tracking Events
const events: TrackingEvent[] = [
{ type: 'queued', timestamp: new Date('2024-01-01T10:00:00Z') },
{ type: 'sent', timestamp: new Date('2024-01-01T10:00:01Z') },
{ type: 'delivered', timestamp: new Date('2024-01-01T10:00:02Z') },
{ type: 'opened', timestamp: new Date('2024-01-01T10:05:00Z') },
{ type: 'clicked', timestamp: new Date('2024-01-01T10:05:30Z'), metadata: { url: '...' } },
];Related skills
AI & Agent Buildingagents