Open-source customer messaging platform - an alternative to Intercom.
| Document | Description |
|---|---|
| Contributing Guide | Development setup, code style, PR guidelines |
| OSS Documentation Hub | Canonical hub for all documentation |
| Architecture & Repo Map | System topology and code navigation |
| Setup & Deploy | Self-hosting and deployment paths |
| Document | Description |
|---|---|
| Data Model | Complete database schema reference (50+ tables) |
| Backend API | Convex queries, mutations, and actions |
| Widget SDK | Embeddable widget client-side API |
| Mobile SDKs | React Native, iOS (Swift), Android (Kotlin) SDK guides |
| Security | RBAC, HMAC identity verification, audit logs, webhook security |
| Testing | Unit, integration, E2E testing guide and CI pipeline |
| Scripts | Build, deploy, security, and utility script reference |
| Document | Description |
|---|---|
| Testing & Verification | Verification workflows for contributors |
| Security & Operations | Security boundaries and operational readiness |
| Source-of-Truth Contract | Documentation ownership and update rules |
| Feature Audit | Comprehensive feature inventory and test coverage |
- Chat - Real-time messaging with customers via embeddable widget and email channel
- Product Tours - Guide users through your product with WYSIWYG editor
- Knowledge Base - Self-service help center with collections and articles
- Mobile Apps - iOS and Android admin apps for on-the-go support with push notifications
- Campaigns - Targeted outbound messaging with audience rules and trigger conditions
- Series - Multi-step automated message sequences
- Surveys - In-app surveys (NPS, rating, text, multiple choice) with analytics
- Tickets - Issue tracking with priority, status, and agent assignment
- Segments - Dynamic visitor grouping by attributes and behavior
- Reports - Analytics dashboards for conversations, response times, and satisfaction
- AI Agent - Automated responses using knowledge base with confidence scoring and human handoff
- Email Channel - Send and receive emails as conversations via Resend integration
- Tooltips - Contextual UI hints attached to page elements
- Outbound Messages - Chat, post, and banner messages with trigger conditions
- Checklists - Onboarding task lists for visitors
- Carousels - Multi-screen promotional content for mobile SDKs
- CSAT - Customer satisfaction ratings on conversations
- Identity Verification - HMAC-based visitor identity verification
- Native SDKs - React Native, iOS (Swift), and Android (Kotlin) SDKs
- Frontend: React, Next.js, Tailwind CSS, Shadcn UI
- Mobile: React Native / Expo
- Backend: Convex (serverless)
- Package Manager: PNPM
opencom/
├── apps/
│ ├── web/ # Next.js dashboard for agents/admins
│ ├── mobile/ # Expo app for iOS/Android (Admin App)
│ ├── widget/ # Embeddable chat widget for websites (Vite)
│ └── landing/ # Next.js marketing/landing page
├── packages/
│ ├── convex/ # Convex schema and functions (backend)
│ ├── types/ # Shared TypeScript types
│ ├── ui/ # Shared React components
│ ├── sdk-core/ # Shared SDK business logic
│ ├── react-native-sdk/ # React Native SDK for customer apps
│ ├── ios-sdk/ # Native iOS SDK (Swift, SPM + CocoaPods)
│ └── android-sdk/ # Native Android SDK (Kotlin)
└── openspec/ # Reserved structure for future spec workflows
| App | Purpose | Location | Users |
|---|---|---|---|
| Admin App | Agent/teammate mobile app for responding to conversations | apps/mobile |
Support agents |
| Mobile SDK | Embeddable SDK for customer mobile apps | packages/react-native-sdk |
End users of customer apps |
| Web Widget | Embeddable widget for customer websites | apps/widget |
Website visitors |
| Web Dashboard | Admin dashboard for managing workspace | apps/web |
Admins and agents |
| Landing Page | Marketing website | apps/landing |
Public visitors |
For the canonical setup paths (quickstart, self-host, env vars, deployment profiles), use:
docs/open-source/setup-self-host-and-deploy.md
The fastest way to get Opencom running locally:
# Clone the repository
git clone https://github.com/opencom-org/opencom.git
cd opencom
# Run the setup script
./scripts/setup.shThe setup script will:
- Check prerequisites (Node.js 18+, PNPM 9+)
- Install dependencies
- Create a Convex project and deploy
- Prompt for your admin email and password
- Create your workspace and admin account
- Generate all
.env.localfiles - Start the web dashboard and widget
Prerequisites:
- Node.js 18+
- PNPM 9+ (
npm install -g pnpm) - Convex account (free at convex.dev)
Non-interactive mode (for CI/scripts):
./scripts/setup.sh --email admin@example.com --password yourpassword --non-interactive --skip-devUpdate environment files:
./scripts/update-env.sh --url https://your-project.convex.cloud --workspace your_workspace_idIf you prefer manual setup:
# Install dependencies
pnpm install
# Navigate to convex package
cd packages/convex
# Login to Convex
npx convex login
# Initialize and deploy
npx convex devThen create .env.local files manually (see Environment Variables Reference below).
# Start all apps
pnpm dev
# Start specific app
pnpm dev:web # Next.js dashboard
pnpm dev:mobile # Expo mobile app
pnpm dev:widget # Widget dev server
pnpm dev:convex # Convex backend
# Build all apps
pnpm build
# Lint
pnpm lint
# Format
pnpm formatOpencom supports multiple deployment configurations depending on your needs:
| Option | Backend | Web App | Mobile Apps | Best For |
|---|---|---|---|---|
| A | Hosted | Hosted | Hosted | Quick start, no infrastructure |
| B | Self-hosted | Hosted | Hosted | Data control with hosted apps |
| C | Self-hosted | Self-hosted | Hosted | Full web control |
| D | Self-hosted | Self-hosted | Self-hosted | Complete self-hosting |
The simplest way to use Opencom:
- Sign up at app.opencom.dev
- Create a workspace
- Copy the widget snippet from Settings → Widget Installation
- Add the snippet to your website
No infrastructure setup required. Your data is stored on the default Opencom Convex instance.
Control your data while using the hosted web and mobile apps.
1. Create a Convex project:
# Clone the repository
git clone https://github.com/opencom-org/opencom.git
cd opencom
# Install dependencies
pnpm install
# Navigate to convex package
cd packages/convex
# Login to Convex
npx convex login
# Create and deploy your project
npx convex dev --once2. Configure environment variables in Convex Dashboard:
Go to your Convex dashboard → Settings → Environment Variables and set:
| Variable | Required | Description |
|---|---|---|
AUTH_SECRET |
Yes | Random string for JWT signing (generate with openssl rand -base64 32) |
RESEND_API_KEY |
For email | API key from Resend for sending emails |
EMAIL_FROM |
For email | Email address to send from (e.g., YourCompany<noreply@yourdomain.com>) |
3. Connect hosted apps to your backend:
- Web: Go to app.opencom.dev, enter your Convex URL on the login page
- Mobile: Open the Opencom app, tap "Connect to Backend", enter your Convex URL
Your Convex URL looks like: https://your-project-123.convex.cloud
Full control over backend and web dashboard.
1. Complete Option B steps first (create and deploy Convex project)
2. Deploy the web app:
# From repository root
cd apps/web
# Create .env.local
cat > .env.local << EOF
NEXT_PUBLIC_OPENCOM_DEFAULT_BACKEND_URL=https://your-project-123.convex.cloud
EOF
# Build
pnpm build
# Deploy to Vercel
npx vercel --prod
# Or deploy to Netlify
npx netlify deploy --prod --dir=.next3. Configure your domain (optional):
Set up a custom domain in your hosting provider (Vercel/Netlify) dashboard.
Complete self-hosting including mobile apps.
1. Complete Options B and C steps first
2. Build mobile apps:
# From repository root
cd apps/mobile
# Create .env.local
cat > .env.local << EOF
EXPO_PUBLIC_OPENCOM_DEFAULT_BACKEND_URL=https://your-project-123.convex.cloud
EOF
# Build for iOS (requires macOS and Xcode)
npx expo build:ios
# Build for Android
npx expo build:android3. Distribute apps:
- Submit to App Store / Play Store for public distribution
- Use enterprise distribution for internal apps
- Use Expo's internal distribution for testing
Note: App store submission and signing certificates are outside the scope of this guide.
After setting up your workspace, install the chat widget on your website.
Add this snippet before the closing </body> tag:
<script src="https://cdn.opencom.dev/widget.js"></script>
<script>
OpencomWidget.init({
convexUrl: "YOUR_CONVEX_URL",
workspaceId: "YOUR_WORKSPACE_ID",
});
</script>Find your exact snippet with pre-filled values in Settings → Widget Installation after logging into your workspace.
If you prefer to serve the widget script from your own infrastructure instead of cdn.opencom.dev, keep using the local bundle path:
bash scripts/build-widget-for-tests.shThis builds apps/widget/dist/opencom-widget.iife.js and copies it into:
apps/web/public/opencom-widget.iife.jsapps/landing/public/opencom-widget.iife.js
For your own hosted frontend, publish that file (or use scripts/deploy-widget-cdn.sh with your Cloudflare R2 bucket) and point your embed snippet or NEXT_PUBLIC_WIDGET_URL at your hosted script URL.
Link conversations to logged-in users:
OpencomWidget.identify({
email: "user@example.com",
name: "John Doe",
userId: "user_123",
company: "Acme Inc",
customAttributes: {
plan: "pro",
signupDate: "2024-01-15",
},
});Track custom events for analytics:
OpencomWidget.trackEvent("feature_used", {
featureName: "export",
});| Option | Type | Default | Description |
|---|---|---|---|
convexUrl |
string | required | Your Convex deployment URL |
workspaceId |
string | required | Your workspace ID |
trackPageViews |
boolean | false | Automatically track page views |
user |
object | undefined | Pre-identify user on init |
Set these in your Convex Dashboard → Settings → Environment Variables:
| Variable | Required | Description |
|---|---|---|
AUTH_SECRET |
Yes | Secret for JWT signing |
AUTH_RESEND_KEY |
For email | Resend API key for OTP email sending |
RESEND_API_KEY |
For email | Resend API key for transactional/campaign emails |
EMAIL_FROM |
For email | Sender email address (e.g., Opencom <noreply@yourdomain.com>) |
RESEND_WEBHOOK_SECRET |
For email | Secret for verifying Resend webhook signatures |
EMAIL_WEBHOOK_INTERNAL_SECRET |
Recommended for email | Internal secret used to authorize webhook-only Convex email handlers |
ENFORCE_WEBHOOK_SIGNATURES |
Recommended (true) |
Enforce webhook signature/internal-secret checks; set to "false" only for local debugging |
WEBHOOK_MAX_AGE_SECONDS |
Optional | Max webhook signature age before rejection (replay window); defaults to 300 |
OPENCOM_DEMO_BLOCKED_EMAIL_CAMPAIGN_WORKSPACE_IDS |
Optional (demo) | Comma-separated workspace IDs where outbound email campaign sends are blocked by policy; transactional auth emails continue to work |
ALLOW_TEST_DATA |
For testing | Set to "true" to enable test data seeding mutations |
TEST_ADMIN_SECRET |
For testing | Shared secret for testAdmin.runTestMutation gateway in test deployments |
To prevent accidental marketing-email blasts in hosted/demo environments while keeping signup/sign-in verification emails operational, set:
OPENCOM_DEMO_BLOCKED_EMAIL_CAMPAIGN_WORKSPACE_IDS=workspace_id_1,workspace_id_2- The guard is enforced in
emailCampaigns.send. - Leave this variable unset to allow campaign sends normally.
- Transactional auth email flows are not blocked by this guard.
Set in .env.local or your hosting provider:
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_OPENCOM_DEFAULT_BACKEND_URL |
No | Pre-fill backend URL on login |
Set in .env.local or EAS secrets:
| Variable | Required | Description |
|---|---|---|
EXPO_PUBLIC_OPENCOM_DEFAULT_BACKEND_URL |
No | Pre-fill backend URL on login |
For local development only:
| Variable | Required | Description |
|---|---|---|
VITE_CONVEX_URL |
Dev only | Convex URL for dev server |
VITE_WORKSPACE_ID |
Dev only | Workspace ID for dev server |
Opencom apps support connecting to any self-hosted Convex backend at runtime:
Web App:
- On the login page, enter your backend URL
- The app validates the connection before proceeding
Mobile App:
- On first launch, tap "Connect to Backend"
- Enter your Convex deployment URL
- Recent backends are saved for quick switching
Backend URL Format:
- Use your Convex deployment URL:
https://your-project-123.convex.cloud - HTTPS is required for security
- The app validates the backend by fetching
/.well-known/opencom.json
Workspace admins can configure signup restrictions:
- Invite Only (default): Users must be invited to join
- Domain Allowlist: Users with emails from specified domains can self-signup
Configure which login methods are available:
- Password: Traditional email/password login
- Email Code (OTP): Passwordless magic link authentication
Opencom uses a dedicated Convex test deployment to avoid polluting development or production data.
Setting up the test deployment:
# Navigate to convex package
cd packages/convex
# Create and deploy to test project (first time only)
npx convex dev --project opencom-test --once
# Create a local test env file from template
cp .env.test.example .env.test
# Update CONVEX_URL in packages/convex/.env.testRunning tests:
# Run all tests
pnpm test
# Run unit tests only (Vitest)
pnpm test:unit
# Run E2E tests only (Playwright)
pnpm test:e2e
# Run tests with coverage
pnpm test:ciSee docs/testing.md for detailed testing guidelines.
For the canonical OSS verification flow (targeted checks + CI-equivalent path), use:
docs/open-source/testing-and-verification.md.
Security and release operations guidance is centralized in:
docs/open-source/security-and-operations.md.
After running the setup script, the React Native SDK example is ready to use:
cd packages/react-native-sdk/example
pnpm startThis will start Expo and allow you to run the example app on:
- iOS Simulator (press
i) - Android Emulator (press
a) - Physical device via Expo Go
The example app demonstrates all SDK features including chat, user identification, and push notifications.
When self-hosting Opencom, consider these security best practices:
- Never commit secrets - All
.env.localfiles are gitignored - Rotate secrets periodically - Especially after team member departures
- Use strong API keys - Generate secure random keys for production
| Variable | Purpose | Where to Set |
|---|---|---|
RESEND_WEBHOOK_SECRET |
Verify email webhook signatures | Convex Dashboard |
EMAIL_WEBHOOK_INTERNAL_SECRET |
Restrict webhook-only email handlers to trusted internal callers | Convex Dashboard |
ENFORCE_WEBHOOK_SIGNATURES |
Fail closed on webhook signature/internal-secret validation (true by default) |
Convex Dashboard |
WEBHOOK_MAX_AGE_SECONDS |
Replay-window bound for webhook signatures (default: 300s) | Convex Dashboard |
AUTH_SECRET |
Sign authentication/session tokens | Convex Dashboard |
CONVEX_SITE_URL |
Auth callback domain used by Convex Auth provider | Convex Dashboard |
ALLOW_TEST_DATA |
Enable/disable test data mutations | Convex Dashboard |
TEST_ADMIN_SECRET |
Secure test admin gateway for internal test mutations | Convex Dashboard (test deployments only) |
All mutations enforce authentication and permission checks:
- Workspace isolation - Users can only access their workspace's data
- Role-based permissions - Owner > Admin > Agent > Viewer
- Signed visitor sessions - All visitor-facing endpoints require a cryptographic session token (
wst_…) validated viaresolveVisitorFromSession(); raw visitor IDs are never trusted alone - Conversation authorization - Dual-path: authenticated agents with permission OR visitors with valid session token who own the conversation
- Bot message restriction - Bot/system messages restricted to internal callers only
- Test data protection - All test data mutations gated behind
ALLOW_TEST_DATAenvironment variable - CORS hardening - No wildcard
Access-Control-Allow-Origin; workspace-level origin allowlists
For production deployments, enable HMAC identity verification:
- Go to Settings → Security in your dashboard
- Enable identity verification and copy the secret
- Generate user hashes server-side when identifying users
- Pass the hash to the widget to prevent impersonation
- Configure allowed origins in Settings → Security for each workspace
- The system validates widget origins against the workspace's allowlist
- Requests without an
Originheader do not receive CORS headers (no wildcard fallback) - All CORS responses include
Vary: Originfor proper caching
See docs/security.md for detailed security documentation.
Opencom uses standardized error codes for consistent error handling across all API endpoints:
| Code | Message | Description |
|---|---|---|
NOT_AUTHENTICATED |
Authentication required | User is not logged in |
SESSION_EXPIRED |
Session has expired | JWT token has expired, re-login required |
INVALID_CREDENTIALS |
Invalid email or password | Login failed due to wrong credentials |
| Code | Message | Description |
|---|---|---|
NOT_AUTHORIZED |
Not authorized to perform this action | User lacks required permissions |
PERMISSION_DENIED |
Permission denied | Specific permission check failed |
NOT_WORKSPACE_MEMBER |
Not a member of this workspace | User tried to access a workspace they don't belong to |
| Code | Message | Description |
|---|---|---|
NOT_FOUND |
Resource not found | Requested entity doesn't exist |
ALREADY_EXISTS |
Resource already exists | Attempted to create a duplicate |
CONFLICT |
Operation conflicts with existing data | Concurrent modification conflict |
| Code | Message | Description |
|---|---|---|
INVALID_INPUT |
Invalid input provided | Request data failed validation |
MISSING_REQUIRED_FIELD |
Required field is missing | A required field was not provided |
INVALID_FORMAT |
Invalid format | Data format doesn't match expected pattern |
| Code | Message | Description |
|---|---|---|
RATE_LIMITED |
Too many requests | Request was throttled, retry later |
try {
await client.mutation(api.conversations.create, { ... });
} catch (error) {
if (error.name === 'NOT_AUTHENTICATED') {
// Redirect to login
} else if (error.name === 'PERMISSION_DENIED') {
// Show permission error UI
} else if (error.name === 'NOT_FOUND') {
// Show 404 page
} else {
// Generic error handling
}
}"Could not determine Convex deployment URL"
- Ensure you're logged into Convex:
npx convex whoami - Try running
npx convex dev --oncemanually inpackages/convex/
"npx convex login" hangs or fails
- Check your internet connection
- Try
npx convex logoutthennpx convex loginagain
Permission denied running setup.sh
- Run
chmod +x scripts/setup.shto make it executable
"CONVEX_URL is not set"
- Run
./scripts/update-env.shto regenerate environment files - Or manually create
.env.localfiles (see Environment Variables Reference)
Widget not connecting
- Verify
VITE_WORKSPACE_IDinapps/widget/.env.localmatches your workspace - Check browser console for CORS errors - add your origin in workspace settings
OTP emails not sending
- OTP requires
RESEND_API_KEYin Convex environment variables - Use password authentication for initial setup (works without Resend)
The setup script requires Bash. Options:
- Use WSL (Windows Subsystem for Linux)
- Use Git Bash
- Follow the Manual Setup instructions instead
GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE.
All contributions are subject to the Contributor License Agreement.