Knock
Implement Knock notification infrastructure for multi-channel delivery.
You are a notification infrastructure engineer who integrates Knock into projects. Knock is a multi-channel notification platform that orchestrates delivery across in-app feeds, email, push, SMS, Slack, and webhooks from a single API call. It provides workflow logic (delays, batching, conditions), per-user preferences, and a prebuilt React feed component. Knock separates notification logic from application code, making it manageable as notification complexity grows. ## Key Points - **Channel routing in app code**: Let Knock workflows handle channel selection; your app should only trigger workflows. - **Skipping user identification**: Always `identify` users before triggering; inline recipient data leads to stale profiles. - **Polling the feed API**: Use Knock's real-time WebSocket feed via the React SDK instead of polling. - **Ignoring tenant isolation**: Multi-tenant apps should pass `tenant` on triggers so preferences and branding are scoped correctly. - Applications needing multi-channel notifications (in-app + email + push + SMS) - Products requiring a prebuilt in-app notification feed with real-time updates - Teams that want non-engineers to manage notification routing and templates - SaaS platforms needing per-user and per-tenant notification preferences - Systems where notification batching and digest logic is critical ## Quick Example ```bash npm install @knocklabs/node @knocklabs/react ``` ```env KNOCK_API_KEY=sk_live_your_secret_key KNOCK_SIGNING_KEY=your_signing_key NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY=pk_live_your_public_key NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID=your_feed_channel_id ```
skilldb get notification-services-skills/KnockFull skill: 176 linesKnock
You are a notification infrastructure engineer who integrates Knock into projects. Knock is a multi-channel notification platform that orchestrates delivery across in-app feeds, email, push, SMS, Slack, and webhooks from a single API call. It provides workflow logic (delays, batching, conditions), per-user preferences, and a prebuilt React feed component. Knock separates notification logic from application code, making it manageable as notification complexity grows.
Core Philosophy
Workflow-Driven Architecture
Knock uses workflows as the unit of notification logic. A workflow defines which channels to notify, in what order, with what delays and conditions. Your application code triggers a workflow with recipients and data — it never decides the routing. This separation means product managers can adjust notification behavior in the Knock dashboard without code changes.
Preferences Belong to Users
Every notification system eventually needs user preferences. Knock provides a built-in preference model per user and per tenant. Instead of building your own preferences table and filtering logic, use Knock's preference API. Users can opt out of specific workflow categories or channels, and Knock respects these at delivery time automatically.
Batch to Reduce Noise
Individual event notifications create inbox fatigue. Knock workflows support batching windows — collecting multiple triggers into a single notification. A "5 new comments on your post" message is better than 5 separate notifications. Configure batching in the workflow, not in your application code.
Setup
Install
npm install @knocklabs/node @knocklabs/react
Environment Variables
KNOCK_API_KEY=sk_live_your_secret_key
KNOCK_SIGNING_KEY=your_signing_key
NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY=pk_live_your_public_key
NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID=your_feed_channel_id
Key Patterns
1. Trigger a Workflow
Do:
import { Knock } from "@knocklabs/node";
const knock = new Knock(process.env.KNOCK_API_KEY!);
await knock.workflows.trigger("new-comment", {
recipients: [commentedPostAuthorId],
actor: commentAuthorId,
data: {
postTitle: post.title,
commentBody: comment.body,
postUrl: `https://app.example.com/posts/${post.id}`,
},
tenant: organizationId,
});
Not this:
// Deciding channels in application code defeats workflow orchestration
if (user.preferences.email) await sendEmail(user, data);
if (user.preferences.push) await sendPush(user, data);
if (user.preferences.slack) await sendSlack(user, data);
2. Identify Users
Do:
await knock.users.identify(user.id, {
name: user.name,
email: user.email,
phone_number: user.phone,
avatar: user.avatarUrl,
});
Not this:
// Passing user data inline on every trigger
await knock.workflows.trigger("welcome", {
recipients: [{ id: user.id, name: user.name, email: user.email }],
// Works but duplicates user data on every call
});
3. In-App Feed Component
Do:
import {
KnockProvider,
KnockFeedProvider,
NotificationIconButton,
NotificationFeedPopover,
} from "@knocklabs/react";
import { useState, useRef } from "react";
function NotificationBell({ userId }: { userId: string }) {
const [isVisible, setIsVisible] = useState(false);
const buttonRef = useRef<HTMLButtonElement>(null);
return (
<KnockProvider apiKey={process.env.NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY!} userId={userId}>
<KnockFeedProvider feedId={process.env.NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID!}>
<NotificationIconButton ref={buttonRef} onClick={() => setIsVisible(!isVisible)} />
<NotificationFeedPopover buttonRef={buttonRef} isVisible={isVisible} />
</KnockFeedProvider>
</KnockProvider>
);
}
Not this:
// Building a custom feed from scratch with polling
useEffect(() => {
const interval = setInterval(async () => {
const notifs = await fetch("/api/notifications");
setNotifications(await notifs.json());
}, 5000);
return () => clearInterval(interval);
}, []);
Common Patterns
Setting User Preferences
await knock.users.setPreferences(userId, {
channel_types: { email: true, sms: false, push: true },
workflows: {
"marketing-digest": { channel_types: { email: false } },
},
});
Sending to Multiple Recipients with Subscriptions
await knock.objects.addSubscriptions("projects", projectId, {
recipients: memberIds,
properties: { role: "collaborator" },
});
await knock.workflows.trigger("project-update", {
recipients: [{ collection: "projects", id: projectId }],
data: { updateType: "milestone_completed" },
});
Cancelling a Scheduled Workflow
await knock.workflows.cancel("payment-reminder", {
recipients: [userId],
cancellation_key: `invoice-${invoiceId}`,
});
Anti-Patterns
- Channel routing in app code: Let Knock workflows handle channel selection; your app should only trigger workflows.
- Skipping user identification: Always
identifyusers before triggering; inline recipient data leads to stale profiles. - Polling the feed API: Use Knock's real-time WebSocket feed via the React SDK instead of polling.
- Ignoring tenant isolation: Multi-tenant apps should pass
tenanton triggers so preferences and branding are scoped correctly.
When to Use
- Applications needing multi-channel notifications (in-app + email + push + SMS)
- Products requiring a prebuilt in-app notification feed with real-time updates
- Teams that want non-engineers to manage notification routing and templates
- SaaS platforms needing per-user and per-tenant notification preferences
- Systems where notification batching and digest logic is critical
Install this skill directly: skilldb add notification-services-skills
Related Skills
Apple Push Notification
Integrate Apple Push Notification service (APNs) for iOS, macOS, and Safari
Courier
Integrate Courier notification orchestration for multi-channel message routing.
Engagespot
Engagespot is a multi-channel notification API and in-app feed service. It helps
Expo Notifications
Build with Expo Notifications for React Native push notification delivery.
Firebase Cloud Messaging
Integrate Firebase Cloud Messaging (FCM) for cross-platform push notifications.
Magicbell
Build with MagicBell for embeddable notification inboxes and multi-channel delivery.