Loading...
Loading...
Set up @personize/signal — a smart notification engine that decides IF, WHAT, WHEN, and HOW to notify each person using Personize memory and governance. Guides you through connecting event sources, configuring delivery channels, setting up governance rules, and testing the decision engine. Use this skill whenever the user wants to build smart notifications, AI-powered alerts, notification fatigue prevention, daily/weekly digests, personalized messaging, or intelligent notification routing. Also trigger when they mention @personize/signal, notification scoring, quiet hours, deduplication, channel routing (email vs Slack vs in-app vs SMS), or want notifications that know when to stay silent.
npx skill4agent add personizeai/personize-skills signal| Action | When to Use |
|---|---|
| ASSESS | Understand their stack, events, channels, and recipients |
| CONFIGURE | Generate Signal initialization code |
| CONNECT | Scaffold event hooks for their framework |
| GOVERN | Set up governance rules for notification guidelines |
| TEST | Run a dry-run evaluation and verify the decision |
user.signupusage.dropimport { Personize } from '@personize/sdk';
import { Signal, ConsoleChannel, ManualSource } from '@personize/signal';
const client = new Personize({ secretKey: process.env.PERSONIZE_SECRET_KEY! });
const manual = new ManualSource();
const signal = new Signal({
client,
channels: [new ConsoleChannel()],
sources: [manual],
});
await signal.start();import { Personize } from '@personize/sdk';
import {
Signal,
ManualSource,
SesChannel, // or SendGridChannel
SlackChannel,
InAppChannel,
} from '@personize/signal';
const client = new Personize({ secretKey: process.env.PERSONIZE_SECRET_KEY! });
const manual = new ManualSource();
const signal = new Signal({
client,
channels: [
new SesChannel({ sourceEmail: 'notifications@yourapp.com' }),
new SlackChannel({ webhookUrl: process.env.SLACK_WEBHOOK! }),
new InAppChannel(async (recipient, payload) => {
// Bridge to your existing notification UI
await yourNotificationService.create({
userId: recipient.userId,
title: payload.subject,
body: payload.body,
});
return { success: true, channel: 'in-app', timestamp: new Date().toISOString() };
}),
],
sources: [manual],
engine: {
dailyCap: 5, // max per user per day
deduplicationWindowMs: 6 * 60 * 60 * 1000, // 6 hours
memorize: true, // feedback loop
workspaceUpdates: true, // track in workspace
},
});
await signal.start();
// Schedule daily digests (weekdays 9 AM)
signal.schedule('daily-digest', '0 9 * * 1-5', async () => {
const users = await getActiveUsers();
await signal.digest.runBatch(users);
});| Option | Default | Description |
|---|---|---|
| 5 | Max notifications per email per day |
| 6h | Skip same event type within window |
| true | Record sent notifications in memory |
| false | Create workspace entries on SEND/DEFER |
| 5 | Max parallel evaluations |
| 20 | Rate limit for batch processing |
// event-hooks.ts — fire-and-forget, never throws, never blocks
import { ManualSource } from '@personize/signal';
const manual = new ManualSource(); // same instance used in Signal config
export const EventHooks = {
async onUserSignup(email: string, data?: Record<string, unknown>) {
manual.emit({
id: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
type: 'user.signup',
email,
data: data || {},
timestamp: new Date().toISOString(),
metadata: { team: 'product' },
});
},
async onUsageDrop(email: string, data: { metric: string; dropPercent: number }) {
manual.emit({
id: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
type: 'usage.drop',
email,
data,
timestamp: new Date().toISOString(),
metadata: { team: 'product' },
});
},
};// In your signup controller
router.post('/signup', async (req, res) => {
const user = await createUser(req.body);
EventHooks.onUserSignup(user.email, { plan: user.plan }).catch(() => {});
res.json(user);
});import { WebhookSource } from '@personize/signal';
const webhookSource = new WebhookSource({
path: '/webhooks/signal',
secret: process.env.WEBHOOK_SECRET,
parser: (body) => ({
id: body.id || `wh_${Date.now()}`,
type: body.event || 'webhook.received',
email: body.email || body.user_email,
data: body.data || body,
timestamp: body.timestamp || new Date().toISOString(),
}),
});
// Mount on Express
app.use(webhookSource.middleware());notificationsNotification Policy:
- Never notify about events the user has already seen in the product
- Prioritize actionable insights over informational updates
- Match tone to the urgency: critical = direct, informational = friendly
- Always include a specific next step, not generic "check it out"
- Maximum 3 notifications per user per day (engine enforces 5 as hard cap)notificationsChannel Selection:
- Slack: For internal team alerts (sales, support, ops)
- Email: For user-facing notifications that need persistence
- In-app: For real-time product notifications when user is active
- Digest: For low-priority updates that can wait for weekly summarycommunicationsCommunication Frequency:
- New users (< 30 days): Max 1 notification per day. Focus on onboarding milestones.
- Active users: Max 3 per week. Only high-signal events.
- At-risk users: Increase frequency for re-engagement. Max 1 per day.
- Churned users: Do not notify. Only send win-back campaigns via marketing.smartGuidelines()smartDigest()const result = await signal.trigger({
id: 'test_001',
type: 'user.signup',
email: 'test@example.com',
data: { plan: 'trial', source: 'website' },
timestamp: new Date().toISOString(),
});
console.log('Action:', result.action); // SEND | DEFER | SKIP
console.log('Score:', result.score); // 0-100
console.log('Reasoning:', result.reasoning);
console.log('SDK calls:', result.sdkCallsUsed);
console.log('Duration:', result.durationMs, 'ms');client.memory.recall({ query: 'signal:sent', email: '...' })ConsoleChannelconst signal = new Signal({
client,
channels: [new ConsoleChannel()], // logs to stdout
sources: [manual],
});| Resource | Contents |
|---|---|
| Full Signal documentation — architecture, channels, sources, workspace, digest, cost controls |
| AI tool instructions for Signal package |
| Minimal setup example |
| Signup → nurture → convert sequence |
| Usage drop alerts to sales team |
| Deferred items → compiled digest |
| Product + Sales + Marketing on same records |
| How to build a new channel |
| How to build a new source |
| Minimal Signal setup recipe |
| Multi-team configuration recipe |