knock
Cross-channel notifications and messaging infrastructure
$ npx docs2skills add knock-notificationsKnock
Cross-channel notifications and messaging infrastructure
What this skill does
Knock is a notification infrastructure platform that handles multi-channel messaging across email, SMS, push notifications, in-app feeds, Slack, and other channels. Instead of building notification systems from scratch or managing multiple providers, developers use Knock to design visual workflows, manage templates, handle user preferences, and deliver notifications at scale.
The platform provides workflow builders for complex notification logic (delays, batching, branching), template editors with Liquid templating, preference management systems, and pre-built UI components for in-app notifications. It's designed for product teams who need reliable, scalable notification delivery with rich customization and user control.
Prerequisites
- Knock account and API keys (public/secret key pair)
- Node.js 16+ for JavaScript SDKs
- React 16.8+ for UI components
- Channel provider accounts (SendGrid, Twilio, etc.) for external delivery
Quick start
npm install @knocklabs/node @knocklabs/react
Backend setup:
import { Knock } from "@knocklabs/node";
const knock = new Knock(process.env.KNOCK_SECRET_KEY);
// Identify a user
await knock.users.identify("user_123", {
email: "user@example.com",
name: "John Doe"
});
// Trigger a workflow
await knock.workflows.trigger("welcome-series", {
recipients: ["user_123"],
data: {
company_name: "Acme Corp"
}
});
Frontend in-app notifications:
import { KnockProvider, KnockFeedProvider, NotificationFeedPopover } from "@knocklabs/react";
function App() {
return (
<KnockProvider
apiKey={process.env.REACT_APP_KNOCK_PUBLIC_KEY}
userId="user_123"
>
<KnockFeedProvider feedId="in-app-feed">
<NotificationFeedPopover buttonRef={buttonRef} />
</KnockFeedProvider>
</KnockProvider>
);
}
Core concepts
Workflows: Visual notification sequences with steps like delays, branches, and channel deliveries. Triggered by events or API calls.
Recipients: Users or objects that receive notifications. Users have preferences; objects enable notifications about resources.
Channels: Delivery mechanisms (email, SMS, push, in-app, Slack). Each requires provider configuration and templates.
Templates: Liquid-based content templates with variables, partials, and translations. Support HTML, text, and rich content.
Preferences: User-controlled opt-in/opt-out settings at workflow, category, or channel level.
Environments: Development/staging/production isolation with branch-based version control.
Key API surface
// User management
knock.users.identify(userId, properties)
knock.users.setChannelData(userId, channelId, data)
knock.users.setPreferences(userId, preferenceSet)
knock.users.deleteUser(userId)
// Workflow triggering
knock.workflows.trigger(workflowKey, { recipients, data, tenant })
knock.workflows.cancel(workflowRunId)
// Bulk operations
knock.workflows.triggerBulk(workflowKey, { recipients })
// Objects (for notifications about resources)
knock.objects.set(collection, id, properties)
knock.objects.addSubscriptions(collection, id, subscriptions)
// Messages and feeds
knock.messages.list({ userId, source })
knock.messages.setStatus(messageId, status)
React hooks and components:
// Feed management
const { items, metadata, loading } = useFeed();
const { markAsRead, markAllAsRead } = useFeedActions();
// Preference management
const { preferences, loading } = usePreferences();
const { updatePreferences } = usePreferenceActions();
// UI Components
<NotificationFeed />
<NotificationFeedPopover />
<NotificationToaster />
Common patterns
Welcome sequence with delay:
// Trigger workflow with user data
await knock.workflows.trigger("user-onboarding", {
recipients: [userId],
data: {
user_name: user.name,
trial_end_date: user.trialEndDate
}
});
// Workflow steps: Email → Delay 3 days → Follow-up email
Team notifications with objects:
// Set up project object
await knock.objects.set("projects", "proj_123", {
name: "Mobile App",
team_id: "team_456"
});
// Subscribe team members
await knock.objects.addSubscriptions("projects", "proj_123", {
recipients: ["user_1", "user_2", "user_3"]
});
// Notify about project updates
await knock.workflows.trigger("project-updated", {
recipients: [{ collection: "projects", id: "proj_123" }],
data: { update_type: "status_change" }
});
In-app feed with real-time updates:
function NotificationCenter() {
const feedClient = useFeedClient();
const { items, unreadCount } = useFeed();
useEffect(() => {
feedClient.listenForUpdates(); // WebSocket connection
return () => feedClient.teardown();
}, []);
return (
<div>
<Badge count={unreadCount} />
{items.map(item => (
<NotificationItem key={item.id} item={item} />
))}
</div>
);
}
Conditional branching workflow:
// Workflow with branch function based on user tier
await knock.workflows.trigger("feature-announcement", {
recipients: userIds,
data: {
feature_name: "Advanced Analytics",
user_tier: "premium" // Used in branch condition
}
});
// Branch step checks: user_tier == "premium" → Email + Slack
// Else → Email only
Preference management:
function PreferenceCenter() {
const { preferences, loading } = usePreferences();
const { updatePreferences } = usePreferenceActions();
const handleToggle = (workflowKey, channelType, enabled) => {
updatePreferences({
workflows: {
[workflowKey]: {
channel_types: { [channelType]: enabled }
}
}
});
};
return (
<div>
{preferences.workflows.map(workflow => (
<PreferenceRow key={workflow.key} workflow={workflow} onToggle={handleToggle} />
))}
</div>
);
}
Configuration
Environment variables:
KNOCK_SECRET_KEY=sk_test_... # Server-side operations
KNOCK_PUBLIC_KEY=pk_test_... # Client-side operations
KNOCK_SIGNING_KEY=... # Webhook verification
Channel configuration (in Knock dashboard):
- Email: SendGrid, Mailgun, SES, Postmark
- SMS: Twilio, AWS SNS
- Push: FCM, APNs, Expo
- Chat: Slack, Discord, Microsoft Teams
React provider setup:
<KnockProvider
apiKey={publicKey}
userId={userId}
userToken={userToken} // For enhanced security
host="https://api.knock.app" // Custom host if needed
>
<KnockFeedProvider
feedId="default"
colorMode="dark"
defaultFeedOptions={{
archived: "exclude",
read: "include"
}}
>
<App />
</KnockFeedProvider>
</KnockProvider>
Best practices
Use objects for resource notifications: Model business entities (projects, documents, orders) as objects to enable subscription-based notifications.
Implement preference granularity: Provide workflow-level and channel-level preferences, not just global on/off switches.
Handle user tokens for security: Use signed user tokens in production to prevent user impersonation in client-side requests.
Batch similar notifications: Use batch functions in workflows to group related notifications and reduce noise.
Set up proper environments: Use separate Knock environments for development, staging, and production with proper API key management.
Leverage workflow conditions: Use recipient preferences, user properties, and custom conditions to control delivery logic.
Monitor delivery status: Track message statuses (sent, delivered, bounced) and implement retry logic for failed deliveries.
Version control workflows: Use Knock's git-like branching system for workflow changes and proper deployment processes.
Optimize feed performance: Use feed pagination, mark items as read programmatically, and implement proper loading states.
Template with care: Use Liquid safely with proper escaping, test with edge case data, and implement fallbacks for missing variables.
Gotchas and common mistakes
User identification timing: Users must be identified before triggering workflows. Unidentified recipients cause workflow failures.
Channel data requirements: Each channel needs specific data (email addresses, phone numbers, push tokens) set on users before delivery.
Environment isolation: API keys are environment-specific. Using wrong keys causes "resource not found" errors across environments.
Workflow key immutability: Workflow keys cannot be changed after creation. Plan naming conventions carefully.
Preference inheritance: Object preferences inherit from associated user preferences. Understand the hierarchy: user → tenant → object.
Rate limiting: Knock has API rate limits. Use bulk operations for high-volume scenarios rather than individual calls.
Webhook verification: Always verify webhook signatures using signing keys to prevent unauthorized requests.
React strict mode: Feed components may render twice in development. Use proper cleanup in useEffect hooks.
Template variable scope: Variables in workflow steps have specific scope. data.* comes from triggers, recipient.* from user properties.
Async workflow timing: Delays and schedules use UTC time. Account for timezone differences in scheduling logic.
Feed state management: Feed items don't automatically refresh on workflow triggers. Call refetch() or use real-time listeners.
Liquid template errors: Invalid Liquid syntax silently fails rendering. Test templates thoroughly with various data shapes.
Subscription cascading: Deleting objects doesn't automatically clean up subscriptions. Handle cleanup in your application logic.