Knock logo

knock

Cross-channel notifications and messaging infrastructure

$ npx docs2skills add knock-notifications
SKILL.md

Knock

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.