An engineer I know spent a month building a notification system. Email templates, push notifications, in-app feed, preference management, batching. Then the PM asked for Slack notifications too. He rewrote it all with Knock in one afternoon.
What Knock Offers for Free
Knock free tier:
- 10,000 notifications/month
- Unlimited channels — email, push, SMS, Slack, in-app
- Workflow builder — visual notification logic
- Preference management — users control what they receive
- Batching and digests — 'You have 5 new comments' instead of 5 emails
- In-app feed component — drop-in React component
- Idempotency — no duplicate notifications
Quick Start
npm install @knocklabs/node
const { Knock } = require('@knocklabs/node');
const knock = new Knock(process.env.KNOCK_API_KEY);
// Send a notification
await knock.workflows.trigger('new-comment', {
recipients: ['user_123'],
data: {
commenter: 'Alice',
comment: 'Great post!',
post_title: 'How to Build APIs',
post_url: 'https://example.com/post/123'
}
});
Define Workflows (Visual or API)
// A workflow can send to multiple channels with logic
await knock.workflows.trigger('order-update', {
recipients: ['user_456'],
data: {
order_id: 'ORD-789',
status: 'shipped',
tracking_url: 'https://track.example.com/789',
estimated_delivery: '2026-04-02'
}
});
// Knock workflow (configured in dashboard):
// 1. Send in-app notification immediately
// 2. Wait 5 minutes
// 3. If not read → send email
// 4. Wait 1 hour
// 5. If not read → send push notification
In-App Feed (React)
import { KnockProvider, KnockFeedProvider, NotificationIconButton, NotificationFeedPopover } from '@knocklabs/react';
import '@knocklabs/react/dist/index.css';
function App() {
const [isVisible, setIsVisible] = useState(false);
const buttonRef = useRef(null);
return (
<KnockProvider apiKey="pk_YOUR_PUBLIC_KEY" userId="user_123">
<KnockFeedProvider feedId="YOUR_FEED_ID">
<NotificationIconButton
ref={buttonRef}
onClick={() => setIsVisible(!isVisible)}
/>
<NotificationFeedPopover
buttonRef={buttonRef}
isVisible={isVisible}
onClose={() => setIsVisible(false)}
/>
</KnockFeedProvider>
</KnockProvider>
);
}
User Preferences
// Set user preferences
await knock.users.setPreferences('user_123', {
channel_types: {
email: true,
push: true,
sms: false // User opted out of SMS
},
workflows: {
'marketing-updates': { channel_types: { email: false } }, // No marketing emails
'order-updates': true // All channels for orders
}
});
// Get preferences
const prefs = await knock.users.getPreferences('user_123');
Batching (Digest Notifications)
// Instead of 10 separate 'new follower' notifications:
// Knock batches them into: 'Alice, Bob, and 8 others followed you'
// Just trigger normally — Knock handles batching in the workflow
await knock.workflows.trigger('new-follower', {
recipients: ['user_123'],
data: { follower_name: 'Alice', follower_avatar: 'https://...' }
});
// Configure batch window (e.g., 15 minutes) in the workflow builder
REST API
# Trigger a workflow
curl -X POST 'https://api.knock.app/v1/workflows/new-comment/trigger' \
-H 'Authorization: Bearer sk_YOUR_SECRET_KEY' \
-H 'Content-Type: application/json' \
-d '{
"recipients": ["user_123"],
"data": { "commenter": "Alice", "comment": "Nice work!" }
}'
# Get user's notifications
curl 'https://api.knock.app/v1/users/user_123/feeds/YOUR_FEED_ID' \
-H 'Authorization: Bearer sk_YOUR_SECRET_KEY'
# Mark notifications as read
curl -X POST 'https://api.knock.app/v1/users/user_123/feeds/YOUR_FEED_ID/read' \
-H 'Authorization: Bearer sk_YOUR_SECRET_KEY'
Perfect For
- SaaS apps — in-app + email notifications
- Marketplaces — order updates across channels
- Social platforms — follow/like/comment notifications with batching
- Developer tools — build alerts, CI/CD notifications
Need to monitor web changes and get notified? Check out my web scraping actors on Apify — automated monitoring and data collection.
Need a custom notification system? Email me at spinov001@gmail.com.
Top comments (0)