Skip to main content

Intercom Customer Support Integration

Status: Production-ready configuration
Last Updated: 2025-12-19
Aligned with: CLAUDE.md Project Values

Table of Contents


Overview

Purpose

Intercom provides customer support chat, help center, and user communication for the Sanctiv mobile app. This document outlines our production-ready configuration aligned with project values:
  • 100% Excellence - Graceful error handling, proper user identification
  • Multi-Tenant Architecture - org_id support for church isolation
  • Follow the Leader - Official Intercom React Native SDK
  • Reuse by Default - Follows Sentry integration pattern

Key Capabilities

Currently Implemented:
  • ✅ Customer support chat (Messenger)
  • ✅ User identification and context (best practices)
  • ✅ Help center integration
  • ✅ Strategic event tracking (key user actions)
  • ✅ Error integration with Sentry
  • ✅ Screen view tracking
  • ✅ Multi-tenant support (org_id)
Planned (Post-Pilot):
  • 🚧 Push notifications
  • 🚧 Help center UI component

Current Status

✅ What’s Working

Configuration: src/lib/intercom.ts Current Intercom configuration:
  • API Keys: Configured via environment variables (platform-specific or single key)
  • App ID: Configured via EXPO_PUBLIC_INTERCOM_APP_ID
  • Initialization: Manual initialization from JavaScript (Expo plugin configured)
  • User Identification: Automatic on login/logout via auth store
  • Error Handling: Graceful fallback if credentials missing
Integration Points:
  • App.tsx - Initializes Intercom on app startup
  • src/state/authStore.ts - Registers/logs out users automatically
  • src/lib/intercom.ts - Core integration module
Verified Working:
  • ✅ Initialization with/without credentials
  • ✅ User registration on login
  • ✅ User logout on sign-out
  • ✅ Messenger display
  • ✅ Help center access
  • ✅ Multi-tenant context (org_id)

Error Integration with Sentry

Status: ✅ Fully Implemented All errors captured by Sentry are automatically logged to Intercom for support context. This provides support agents with full visibility into user issues.

How It Works

  1. Automatic Integration: When captureException() is called in Sentry, it automatically logs to Intercom
  2. Error Events: Errors are logged as app_error_occurred events with error details
  3. User Context: Last error timestamp is stored as a user attribute for support agents
  4. Non-Blocking: Error logging never blocks error reporting or app functionality

Error Event Data

{
  error_name: "TypeError",
  error_message: "Cannot read property 'x' of undefined",
  // Plus any additional context from captureException()
}

User Attributes Updated

  • last_error_at - Unix timestamp of last error
  • last_error_component - Component where error occurred (if available)
Benefits:
  • Support agents see error history when users contact support
  • Better context for troubleshooting
  • Helps identify patterns in user issues

Event Tracking

Status: ✅ Fully Implemented Strategic event tracking follows Intercom best practices: track meaningful business events, not every user interaction.

Tracked Events

User Lifecycle:
  • signed_in - User logs in
  • signed_out - User logs out
Journal Actions:
  • journal_entry_created - New journal entry created
    • Metadata: word_count, has_content, has_tags
Companion Actions:
  • companion_request_sent - User sends companion request
    • Metadata: has_message
  • companion_request_accepted - User accepts companion request
    • Metadata: request_id
Flow Sessions:
  • flow_session_completed - Guided journal flow completed
    • Metadata: flow_template_id, flow_template_name, completed_steps, total_steps, duration_seconds
Screen Views:
  • screen_viewed - User navigates to a screen
    • Metadata: screen_name, timestamp
Errors:
  • app_error_occurred - Error captured by Sentry
    • Metadata: error_name, error_message, plus context

Best Practices

✅ Do Track:
  • Business-critical actions (journal entries, companion requests)
  • Feature usage (flow completions)
  • User lifecycle events (sign in/out)
  • Errors (automatic via Sentry integration)
❌ Don’t Track:
  • Every button tap or scroll
  • Navigation between screens (handled automatically)
  • Sensitive data (PII)
  • High-frequency events that don’t add value

Implementation

Events are tracked automatically in:
  • src/state/journalStore.ts - Journal entry creation
  • src/state/companionsStore.ts - Companion requests
  • src/flow-engine/FlowContext.tsx - Flow session completion
  • src/lib/sentryNavigation.ts - Screen views
  • src/lib/sentry.ts - Error events (via Sentry integration)

Future Enhancements

Note: The following features are not yet implemented and are planned for post-pilot:
  1. Push Notifications - Intercom push notification handling (see Push Notifications (Future))
  2. Help Center UI - Add UI component for help center access (API exists, UI component needed)
Implementation Priority:
  • 🟢 Low: Push notifications (post-pilot)
  • 🟢 Low: Help center UI component (post-pilot)

Configuration

Environment Variables

Add to your .env file:
# Required: App ID (same for all platforms)
EXPO_PUBLIC_INTERCOM_APP_ID=your-app-id-here

# Option 1: Single API key for all platforms
EXPO_PUBLIC_INTERCOM_API_KEY=your-api-key-here

# Option 2: Platform-specific API keys (recommended)
EXPO_PUBLIC_INTERCOM_IOS_API_KEY=ios_sdk-abc123
EXPO_PUBLIC_INTERCOM_ANDROID_API_KEY=android_sdk-xyz789
Where to Find Credentials:

Expo Configuration

The Intercom Expo plugin is configured in app.config.js:
plugins: [
  [
    "@intercom/intercom-react-native",
    {
      useManualInit: true  // Initialize from JavaScript
    }
  ]
]
iOS Permissions: Camera and microphone permissions are configured for voice messages and photo attachments.

Initialization

Intercom initializes automatically on app startup in App.tsx:
import { initializeIntercom } from "./src/lib/intercom";

useEffect(() => {
  const prepare = async () => {
    // Initialize Intercom BEFORE rendering app to prevent race condition
    // This ensures Intercom is ready when authStore.initialize() runs
    await initializeIntercom();
    // ... rest of app initialization
  };
  prepare();
}, []);
Behavior:
  • ✅ Initializes if credentials are configured
  • ✅ Gracefully skips if credentials are missing
  • ✅ Logs success/failure to console (dev mode only)
  • ✅ Non-blocking (app continues if initialization fails)
  • ✅ Awaited before app render to prevent race conditions with user registration

User Identification

Automatic Registration

Users are automatically registered with Intercom when they sign in. This happens in src/state/authStore.ts:
import { registerUser, logoutUser } from "../lib/intercom";

// On sign in
registerUser({
  userId: user.id,
  email: user.email,
  name: user.full_name,
  orgId: user.org_id,  // Multi-tenant context
});

// On sign out
logoutUser();
User Attributes Sent:
  • userId - Unique user identifier
  • email - User email address
  • name - User full name
  • org_id - Organization ID (custom attribute for multi-tenant)

Manual User Updates

Update user attributes manually:
import { setUserAttributes } from "./src/lib/intercom";

await setUserAttributes({
  subscription_tier: "premium",
  last_active: Date.now(),
});

Usage

Show Messenger

Display the Intercom support chat:
import { showMessenger } from "./src/lib/intercom";

// In a button handler
const handleSupportPress = async () => {
  await showMessenger();
};

Show Help Center

Display Intercom help articles:
import { showHelpCenter } from "./src/lib/intercom";

const handleHelpPress = async () => {
  await showHelpCenter();
};

Log Events

Track user actions for support context:
import { logEvent } from "./src/lib/intercom";

// Track a user action
await logEvent("journal_entry_created", {
  entry_type: "gratitude",
  word_count: 150,
});

Check Unread Count

Get the number of unread conversations:
import { getUnreadCount } from "./src/lib/intercom";

const unreadCount = await getUnreadCount();
if (unreadCount > 0) {
  // Show badge indicator
}

Hide Messenger

Programmatically hide the messenger:
import { hideMessenger } from "./src/lib/intercom";

await hideMessenger();

Multi-Tenant Context

Organization Isolation

Intercom includes org_id as a custom attribute for multi-tenant support:
registerUser({
  userId: user.id,
  email: user.email,
  orgId: user.org_id,  // Automatically sent as custom attribute
});
Benefits:
  • Support agents can filter conversations by organization
  • Better context for church-specific support
  • Analytics by organization
Implementation:
  • org_id is automatically included when users register
  • Stored as customAttributes.org_id in Intercom
  • Available for filtering and segmentation

Best Practices

1. Error Handling

All Intercom functions gracefully handle errors:
// Safe to call - won't crash if Intercom not initialized
await showMessenger();
Pattern: Functions return early if Intercom not initialized or configured.

2. User Identification

Best Practices:
  • ✅ Identify immediately after login (userId + email required)
  • ✅ Update attributes separately (name, org_id) after identification
  • ✅ Include org_id for multi-tenant context
  • ✅ Log sign-in event for analytics
  • ✅ Log sign-out event before logout
Implementation:
  • registerUser() handles identification in correct order:
    1. loginUserWithUserAttributes() - Identifies user (userId + email)
    2. updateUser() - Updates additional attributes (name, org_id)
    3. logEvent("signed_in") - Tracks sign-in for analytics
Never:
  • ❌ Register users before login
  • ❌ Skip logout on sign-out
  • ❌ Send sensitive data as custom attributes
  • ❌ Update attributes before identification

3. Event Tracking

Track meaningful business events:
  • ✅ User lifecycle (sign in/out)
  • ✅ Key actions (journal entries, companion requests, flow completions)
  • ✅ Errors (automatic via Sentry integration)
  • ✅ Screen views (automatic via navigation tracking)
Avoid:
  • ❌ Tracking every tap/scroll
  • ❌ Sending PII in event metadata
  • ❌ Over-tracking (privacy concern)
  • ❌ Redundant events (screen views are automatic)
Event Naming:
  • Use snake_case: journal_entry_created, companion_request_sent
  • Be descriptive: flow_session_completed not flow_done
  • Include context in metadata, not event name

4. Support Access

Make support easily accessible:
  • ✅ Add “Contact Support” button in Settings
  • ✅ Show unread count badge
  • ✅ Provide help center access

Troubleshooting

Intercom Not Initializing

Symptoms: Console shows “Intercom credentials not configured” Solutions:
  1. Check .env file has EXPO_PUBLIC_INTERCOM_APP_ID
  2. Verify API key is set (platform-specific or single key)
  3. Restart Expo dev server after adding env vars
  4. Check app.config.js has Intercom plugin configured

Users Not Appearing in Intercom

Symptoms: Users sign in but don’t appear in Intercom dashboard Solutions:
  1. Verify registerUser() is called after login
  2. Check user has userId or email (required)
  3. Check Intercom dashboard filters (may be filtered out)
  4. Verify API keys are correct for your Intercom workspace

Messenger Not Showing

Symptoms: showMessenger() called but nothing appears Solutions:
  1. Verify Intercom initialized (isIntercomReady())
  2. Check user is logged in to Intercom (registerUser() called)
  3. Verify app is in foreground (Intercom requires active app)
  4. Check Intercom workspace settings (messenger enabled?)

Platform-Specific Issues

iOS:
  • Verify camera/microphone permissions in Info.plist
  • Check Expo plugin applied correctly (npx expo prebuild)
  • Ensure iOS API key is correct
Android:
  • Verify Android API key is correct
  • Check google-services.json if using push notifications
  • Ensure minSdkVersion >= 23

Push Notifications

Implementation Status

Push notifications are now integrated! Push notifications for Intercom messages are automatically registered when users grant notification permissions.

How It Works

  1. Automatic Token Registration: When registerForPushNotificationsAsync() is called (on app start or when user enables notifications):
    • Gets Expo push token (for our own notifications)
    • Gets native device push token (for Intercom)
    • Registers device token with Intercom using Intercom.sendTokenToIntercom()
    // In src/services/notifications/push.service.ts
    if (isIntercomReady()) {
      const deviceTokenData = await Notifications.getDevicePushTokenAsync();
      if (deviceTokenData?.data) {
        await Intercom.sendTokenToIntercom(deviceTokenData.data);
      }
    }
    
  2. Notification Handling: When a user taps an Intercom push notification:
    • Intercom SDK automatically handles the notification
    • Opens Intercom messenger if app is running
    • Routes to Intercom conversation
    // In src/services/notifications/push.service.ts
    Notifications.addNotificationResponseReceivedListener((response) => {
      const data = response.notification.request.content.data as any;
      if (isIntercomReady() && (data?.intercom === true || data?.intercom_message_id)) {
        Intercom.handlePushMessage();
      }
    });
    

Setup Requirements

Intercom Dashboard Configuration:
  1. Get Apple Push Notification Credentials:
    • Go to Apple Developer
    • Navigate to Certificates, Identifiers & Profiles > Keys
    • Click + to create a new key
    • Enable Apple Push Notifications service (APNs)
    • Download the .p8 file (you can only download once!)
    • Note the Key ID and your Apple Team ID
  2. Configure in Intercom Dashboard:
    • Go to Settings > Channels > Messenger > Install tab
    • Scroll to “Setup push notifications” section
    • Fill in:
      • App name: Sanctiv
      • Bundle ID: com.sanctiv.app
      • Upload .p8 file: Upload the downloaded file
      • Key ID: Enter the Key ID from Apple
      • Apple Team ID: Enter your Team ID
    • Click Save
  3. Test Push Notifications:
    • Send a test message from Intercom dashboard to a user
    • Verify notification appears on device
    • Tap notification to verify it opens Intercom messenger

Troubleshooting

Push notifications not working:
  1. Check Intercom is initialized:
    • Verify isIntercomReady() returns true
    • Check Metro logs for “Device token registered with Intercom”
  2. Check device token registration:
    • Look for ”✅ Device token registered with Intercom” in logs
    • If you see warnings, check Intercom initialization
  3. Check Intercom dashboard:
    • Verify APNs credentials are correctly configured
    • Ensure Bundle ID matches com.sanctiv.app
    • Check that push notifications are enabled for your app
  4. Check device permissions:
    • Verify user granted push notification permissions
    • Check device Settings > Notifications > Sanctiv
Note: getDevicePushTokenAsync() may only work on physical devices, not simulators.

Code Reference

  • Token Registration: src/services/notifications/push.service.tsregisterForPushNotificationsAsync()
  • Notification Handling: src/services/notifications/push.service.tsinitializePushNotifications()
  • Intercom Integration: src/lib/intercom.tsisIntercomReady()

Future Enhancements

Potential improvements for post-pilot:
  1. Notification Badge Count:
    // Show unread count badge on Intercom icon
    const unreadCount = await Intercom.getUnreadConversationCount();
    // Update UI badge
    


Maintained by: All contributors
Review frequency: After any Intercom changes
Last audit: 2025-12-19