OrbitDesk
Multi-tenant omnichannel CRM platform with AI-powered automation, visual flow builder, and real-time messaging across WhatsApp, Telegram, Messenger, Instagram, Email, SMS & Live Chat.
Project Overview
OrbitDesk is a full-featured, multi-tenant SaaS platform that enables businesses to manage customer communications across 7 messaging channels, automate interactions with visual flow builders, and leverage AI chatbots for customer support. Channels include WhatsApp (Cloud API + Baileys QR), Telegram, Facebook Messenger, Instagram DM, Email (SMTP/IMAP), Twilio SMS, and an embeddable Live Chat widget.
Key Capabilities
Omnichannel Inbox
Unified real-time inbox for 7 channels: WhatsApp, Telegram, Messenger, Instagram DM, Email, SMS & Live Chat. Agent assignment, conversation status, read receipts, and channel filter.
Visual Flow Builder
Drag-and-drop automation builder with 13+ node types: messaging, conditions, delays, HTTP calls, AI, and more.
AI Chatbots (RAG)
Orbit-powered AI agents with knowledge base (documents, URLs, text). Auto-handoff to human agents.
Multi-Tenant
Complete tenant isolation with role-based access control (Admin, Manager, Agent, Superadmin).
Plugin Ecosystem
Modular plugins for Google Sheets, Twilio (SMS/Voice), Email (SMTP), and WhatsApp QR pairing.
Dashboard Analytics
Real-time metrics: message volume, active conversations, contact growth, and 7-day trend charts.
Subscriptions & Billing
Built-in subscription system with Stripe and Razorpay integration. Plan-based resource limits, usage tracking, and automated billing lifecycle.
REST API Access
Scoped API keys for external integrations. Connect Zapier, CRMs, e-commerce platforms, and custom apps with fine-grained permission control and rate limiting.
AI Voice Calling
Outbound voice campaigns with AI agents powered by OpenAI Realtime API & Twilio. Real-time transcription, tool calling, sentiment analysis, and lead qualification.
Architecture
OrbitDesk follows a monorepo structure managed by Turborepo + pnpm workspaces. The platform consists of three application services backed by three data stores.
| Service | Technology | Port | Purpose |
|---|---|---|---|
| Web | React 19 + Vite 7 + Tailwind v4 | 3000 | Frontend SPA (Nginx in production) |
| API | NestJS 11 + Prisma + Mongoose | 3002 | REST API + WebSocket gateway + Voice bridge |
| AI | FastAPI + Uvicorn | 8000 | LLM completions & RAG queries |
| PostgreSQL | PostgreSQL 16 | 5432 | Primary relational data (Prisma) |
| MongoDB | MongoDB 7 | 27017 | Conversations, messages, flow sessions |
| Redis | Redis 7 | 6379 | Cache, pub/sub, rate limiting |
Tech Stack
Backend
- NestJS 11 — Framework
- Prisma 6 — PostgreSQL ORM
- Mongoose 8 — MongoDB ODM
- ioredis — Redis client
- Socket.IO 4 — WebSockets
- Passport + JWT + API Key — Authentication
- @nestjs/throttler — Rate limiting
- Swagger — API docs (
/api/docs) - Baileys — WhatsApp Web API
- Twilio SDK — Voice calling & SMS
- ws — Raw WebSocket (Twilio media stream)
- node-mulaw — Audio transcoding (μ-law ↔ PCM)
- AWS SDK v3 — S3 storage (optional)
Frontend
- React 19 — UI framework
- Vite 7 — Build tool
- Tailwind CSS v4 — Styling
- TanStack Query — Data fetching
- TanStack Table — Data tables
- React Router 7 — Routing
- React Hook Form + Yup — Forms
- FlowGram.ai — Visual flow editor
- ApexCharts — Dashboard charts
- wavesurfer.js — Audio waveform player
AI Service
- FastAPI — Python web framework
- Uvicorn — ASGI server
- OpenAI SDK — LLM provider
- Multi-provider: OpenAI, Anthropic, Gemini, Mistral, Deepseek
Infrastructure
- Turborepo — Monorepo orchestration
- pnpm 9 — Package manager
- Docker — Containerization
- Nginx — Reverse proxy + SSL
- PM2 — Process manager (bare metal)
- GitHub Actions — CI/CD
Messaging & Inbox
The real-time inbox is the core of OrbitDesk. Agents can manage customer conversations across all connected channels, send messages, assign chats, and track delivery status — all in real time via WebSockets.
WhatsApp Connection Types
Cloud API (Official)
Meta Business Platform integration. Requires Phone Number ID, Business Account ID, and permanent access token. Supports all message types, webhooks, and read receipts.
Baileys QR (Personal)
Connect any WhatsApp number by scanning a QR code. Uses the Baileys library. Requires the whatsapp-qr plugin installed. QR delivered via WebSocket.
Message Types
Conversation Lifecycle
Channel Pipeline (all channels)
Channel Adapters
OrbitDesk ships with a unified ChannelsModule that abstracts every messaging channel behind a common IMessageChannel interface. All adapters share the same pipeline: inbound normalisation → contact upsert → conversation upsert → MongoDB store → WebSocket emit → flow trigger.
Supported Channels
| Channel | Inbound Method | Outbound | Notes |
|---|---|---|---|
| WhatsApp Cloud API | Meta webhook (POST /whatsapp/webhook) |
Meta Graph API | HMAC-SHA256 signature verification, template support |
| WhatsApp QR (Baileys) | Baileys socket events | Baileys socket | Requires whatsapp-qr plugin; QR via WebSocket |
| Telegram | Bot API webhook (POST /channels/telegram/:token/webhook) |
Telegram Bot API | Text, photo, document, voice, sticker messages |
| Facebook Messenger | Meta Graph webhook (POST /channels/meta/webhook) |
Meta Graph API | HMAC-SHA256 verified; text & attachments |
| Instagram DM | Meta Graph webhook (shared with Messenger) | Meta Graph API | Messaging product: instagram |
| Email (SMTP / IMAP) | IMAP polling (ImapPollerService) |
Nodemailer SMTP | Configurable poll interval; supports HTML & plain text |
| Twilio SMS | Twilio webhook (POST /channels/twilio-sms/webhook) |
Twilio REST API | Two-way SMS; E.164 phone numbers |
| Live Chat Widget | REST endpoint (POST /channels/live-chat/message) |
WebSocket push to widget | Embeddable JS widget; JWT-authenticated visitor sessions |
Architecture
Each adapter implements IMessageChannel and self-registers via ChannelRouterService on module init. Inbound messages are normalised to a common NormalizedMessage shape by a per-channel Normalizer before entering the shared pipeline.
| Service | Responsibility |
|---|---|
ChannelRouterService | Registry of active adapters; routes outbound sends to the correct adapter by channel type |
ChannelPipelineService | Shared inbound processing: contact upsert → conversation upsert → store → emit → flow trigger |
ChannelConfigService | Reads per-tenant channel credentials from encrypted integration records |
*Normalizer | Per-channel class that converts raw provider payloads to NormalizedMessage |
Adding a Custom Channel
Implement the IMessageChannel interface, create a Normalizer, register the adapter in ChannelsModule, and add a controller for the inbound webhook. No changes needed to the pipeline, flow engine, or inbox.
Contact Management
Contacts are the CRM backbone. Each contact is scoped to a tenant and identified by phone number. Contacts support custom fields and tags for segmentation.
| Field | Type | Description |
|---|---|---|
phone | String (unique/tenant) | WhatsApp phone number (E.164) |
name | String | Contact display name |
email | String (optional) | Email address |
tags | String[] | Tags for segmentation (e.g., "VIP", "Lead") |
customFields | JSON | Flexible key-value store (e.g., company, city) |
optedIn | Boolean | Opted into communications |
avatarUrl | String (optional) | Profile picture URL |
Flow Automation
The visual flow builder (powered by FlowGram.ai) lets users design automation workflows with a drag-and-drop canvas. Flows are triggered by events and execute a graph of nodes.
Flow Lifecycle
Available Node Types (13)
Trigger
Entry point. Fires on: message received, keyword match, contact created, tag added, manual trigger, or external webhook.
Send Message
Send WhatsApp text, template, or media messages to the contact.
Condition
If/else branching based on variables, message content, or contact fields.
Delay
Wait for a specified duration (seconds, minutes, hours) before continuing.
AI Response
Call an LLM (OpenAI, Anthropic, etc.) with system prompt and dynamic context.
HTTP Request
Call external APIs (GET/POST/PUT/DELETE) with custom headers and body.
Set Variable
Create or update flow variables for use in subsequent nodes.
Send Email
Send emails via SMTP. Requires the email plugin.
Send SMS
Send SMS via Twilio. Requires the twilio plugin.
Voice Call
Initiate voice calls via Twilio. Requires the twilio plugin.
Google Sheets
Append data to a Google Sheets spreadsheet. Requires google-sheets plugin + OAuth.
Loop
Iterate over array variables and execute child nodes for each item.
End
Terminal node. Ends flow execution and marks session as completed.
Trigger Types
| Trigger | Description |
|---|---|
message_received | Any incoming WhatsApp message |
keyword | Message contains a specific keyword |
contact_created | New contact is created |
tag_added | A tag is added to a contact |
manual | Manually triggered by a user |
webhook | External HTTP request to a unique webhook URL (details below) |
Flow Execution Engine
The backend flow engine (FlowExecutorService) traverses the graph from the trigger node, executing each node via the NodeExecutorFactory. Flow sessions (state, variables, execution log) are stored in MongoDB. Webhook-triggered flows run asynchronously — the HTTP response is returned immediately while the flow executes in the background.
Webhook Integration
External services can trigger flow automations by sending HTTP requests to unique webhook URLs. This enables push-based integrations with e-commerce platforms, form builders, payment gateways, and any system that supports outgoing webhooks.
How It Works
When a flow’s trigger type is set to webhook, the system generates a unique UUID token. External services send a POST request to /api/v1/webhooks/{token}. The system validates the request, triggers the flow asynchronously, and returns an immediate 200 response.
Webhook URL
POST https://<your-domain>/api/v1/webhooks/<webhook-token>
The webhook token is a UUID generated automatically when a flow’s trigger type is set to webhook. It is globally unique and serves as both the identifier and access key.
Authentication & Signature Verification
Webhook endpoints are public (no Bearer token required) — access is controlled by the unique token in the URL. For additional security, you can configure an optional HMAC-SHA256 secret.
| Header | Description |
|---|---|
x-hub-signature-256 | GitHub-style HMAC signature (preferred) |
x-webhook-signature | Alternative signature header |
x-signature | Generic signature header |
If a webhook secret is configured, incoming requests must include a valid signature in one of the headers above. The expected format is:
sha256=<hex-encoded HMAC-SHA256 of raw request body>
The system uses timing-safe comparison to prevent timing attacks. Requests with invalid or missing signatures receive a 403 Forbidden response.
Request Format
POST /api/v1/webhooks/a1b2c3d4-e5f6-7890-abcd-ef1234567890?source=shopify HTTP/1.1
Host: app.example.com
Content-Type: application/json
X-Hub-Signature-256: sha256=abcdef1234567890...
{
"event": "order.created",
"data": {
"orderId": "12345",
"amount": 99.99,
"customer": {
"email": "user@example.com",
"name": "John Doe"
}
}
}
The endpoint accepts any valid JSON body. Query parameters, headers, and the full body are all captured and made available as flow variables.
Response
HTTP/1.1 200 OK
Content-Type: application/json
{ "success": true, "message": "Webhook received" }
Available Variables
Webhook data is accessible in all subsequent flow nodes using template syntax:
| Variable | Description |
|---|---|
{{webhook.body}} | Full JSON request body |
{{webhook.body.event}} | Nested field access (dot notation) |
{{webhook.headers}} | Request headers (sanitized — auth/cookie headers removed) |
{{webhook.query}} | URL query parameters |
{{webhook.method}} | HTTP method (always POST) |
{{webhook.receivedAt}} | ISO 8601 timestamp when the webhook was received |
{{webhook.token}} | The webhook token used |
Example — Using webhook data in a Send Message node:
Dear {{webhook.body.data.customer.name}},
Your order #{{webhook.body.data.orderId}} totaling ${{webhook.body.data.amount}}
has been received. We'll process it shortly.
Token Management
| Action | Endpoint | Description |
|---|---|---|
| Auto-generated | — | Token created automatically when trigger type is set to webhook |
| Regenerate token | POST /flows/:id/regenerate-webhook-token |
Generates a new UUID. The old URL becomes invalid immediately. Use if a token is compromised. |
| Set secret | PATCH /flows/:id/webhook-secret |
Sets HMAC-SHA256 secret for signature verification. Pass null to disable. |
Verification Endpoint
GET /api/v1/webhooks/:token
Returns the flow name and status. Use this to verify connectivity from your external service before sending events.
Security
- HMAC-SHA256 signatures — Optional but recommended for production. Timing-safe comparison prevents timing attacks.
- Header sanitization —
authorization,cookie, andset-cookieheaders are stripped from trigger data to prevent credential leakage. - Tenant isolation — Each webhook token maps to exactly one flow and tenant. No cross-tenant access is possible.
- Token rotation — Tokens can be regenerated instantly via API, invalidating the old URL.
- Flow state check — Only
ACTIVEflows are triggered. Draft, paused, or archived flows return403 Forbidden.
Error Responses
| Status | Reason |
|---|---|
404 Not Found | Invalid webhook token (no matching flow) |
403 Forbidden | Flow is not active, or signature verification failed |
200 OK | Webhook received and flow triggered successfully |
cURL Example
# Without signature verification
curl -X POST https://app.example.com/api/v1/webhooks/YOUR_TOKEN \
-H "Content-Type: application/json" \
-d '{"event": "order.created", "orderId": "12345"}'
# With HMAC-SHA256 signature
BODY='{"event": "order.created", "orderId": "12345"}'
SECRET="your-webhook-secret"
SIGNATURE="sha256=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)"
curl -X POST https://app.example.com/api/v1/webhooks/YOUR_TOKEN \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: $SIGNATURE" \
-d "$BODY"
Common Use Cases
E-commerce Orders
Shopify, WooCommerce, or custom storefronts send order events to trigger order confirmation and shipping notifications.
Form Submissions
Typeform, Google Forms, or landing pages trigger lead nurturing flows when a form is submitted.
Payment Events
Stripe, Razorpay, or PayPal send payment success/failure events to trigger customer notifications.
Custom Integrations
Any system with outgoing webhook support (CRMs, ERPs, monitoring tools) can trigger automated workflows.
REST API Access
OrbitDesk provides scoped API key authentication for external integrations. API keys allow third-party services, automation platforms, and custom applications to access the REST API without requiring user login credentials.
How It Works
API keys are managed from Settings > API Keys (admin-only). Each key is generated with a wcrm_ prefix, and only the SHA-256 hash is stored in the database. The raw key is displayed only once at creation time.
Authentication
API key authentication works alongside existing JWT authentication. Any protected endpoint accepts either a Bearer token or an X-Api-Key header. The system tries JWT first, then falls back to API key validation.
# Authenticate with API key
curl -H "X-Api-Key: wcrm_abc123..." \
https://your-domain.com/api/v1/contacts
# JWT authentication still works as before
curl -H "Authorization: Bearer eyJhb..." \
https://your-domain.com/api/v1/contacts
API key requests are automatically scoped to the tenant that created the key via the TenantScopeInterceptor — no manual tenant ID required.
Available Scopes
Each API key is granted specific scopes that control which endpoints it can access. JWT-authenticated users bypass scope checks entirely (full access as before).
| Scope | Grants Access To |
|---|---|
contacts:read | List and view contacts |
contacts:write | Create, update, and delete contacts |
conversations:read | List and view conversations |
messages:read | List and view messages |
messages:send | Send messages |
flows:read | List and view automation flows |
flows:trigger | Trigger flows programmatically |
templates:read | List and view message templates |
Key Management Endpoints
These endpoints require JWT authentication with admin or superadmin role:
Create Key Request
POST /api/v1/api-keys
Authorization: Bearer <admin-jwt-token>
Content-Type: application/json
{
"name": "Zapier Integration",
"scopes": ["contacts:read", "contacts:write", "messages:send"],
"expiresAt": "2027-01-01T00:00:00Z" // optional, null = never expires
}
Create Key Response
HTTP/1.1 201 Created
{
"id": "a1b2c3d4-...",
"name": "Zapier Integration",
"key": "wcrm_7f3a9b2c4d5e6f...", // ⚠ shown ONLY once
"scopes": ["contacts:read", "contacts:write", "messages:send"],
"expiresAt": "2027-01-01T00:00:00.000Z",
"createdAt": "2026-02-19T10:30:00.000Z",
"isActive": true
}
wcrm_...) is only returned in the creation response. It is not stored — only its SHA-256 hash is saved. Copy and securely store the key immediately after creation.
Rate Limiting
All API requests are rate-limited to 100 requests per minute. API key requests are tracked per key ID, while JWT requests are tracked per IP address. Rate limit information is included in response headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Remaining requests in current window |
Retry-After | Seconds to wait when rate limited (429 response) |
Use Cases
CRM Sync
Bidirectional contact sync with HubSpot, Salesforce, or Zoho. Use contacts:read + contacts:write scopes.
No-Code Automations
Connect Zapier, Make, or n8n workflows. Example: Shopify order triggers WhatsApp message via messages:send.
E-Commerce Notifications
Shopify/WooCommerce sends order confirmations and shipping updates via messages:send + contacts:read.
Custom Dashboards & BI
Pull data into Metabase or Google Data Studio using conversations:read + messages:read.
Lead Capture Forms
Website forms create contacts directly via contacts:write and trigger welcome flows via flows:trigger.
Bulk Contact Import
Scripts or microservices batch-import contacts from CSV or external databases using contacts:write.
Security
- SHA-256 hashed storage — Raw keys are never stored. Only the cryptographic hash is persisted.
- One-time key reveal — The raw key is shown only at creation. It cannot be retrieved afterward.
- Scope enforcement — Each key is limited to specific scopes. Missing scopes return
403 Forbidden. - Key expiration — Optional expiry date. Expired keys are automatically rejected.
- Tenant isolation — API keys are scoped to a single tenant. No cross-tenant access possible.
- Rate limiting — Per-key rate limiting prevents abuse (100 req/min default).
- Revocation — Keys can be instantly revoked or deleted from the Settings UI or API.
- Max 20 active keys per tenant to prevent key sprawl.
Error Responses
| Status | Reason |
|---|---|
401 Unauthorized | Invalid, revoked, or expired API key |
403 Forbidden | API key does not have required scope |
429 Too Many Requests | Rate limit exceeded |
cURL Examples
# List contacts with API key
curl -H "X-Api-Key: wcrm_7f3a9b2c4d5e6f..." \
https://your-domain.com/api/v1/contacts
# Create a contact
curl -X POST -H "X-Api-Key: wcrm_7f3a9b2c4d5e6f..." \
-H "Content-Type: application/json" \
-d '{"phone": "+1234567890", "name": "John Doe", "email": "john@example.com"}' \
https://your-domain.com/api/v1/contacts
# Send a message
curl -X POST -H "X-Api-Key: wcrm_7f3a9b2c4d5e6f..." \
-H "Content-Type: application/json" \
-d '{"conversationId": "conv-uuid", "type": "text", "content": "Hello!"}' \
https://your-domain.com/api/v1/messages
Templates & Campaigns
WhatsApp Message Templates
Templates are pre-approved message formats required by Meta for outbound messaging outside the 24-hour window. They go through a review process before they can be used.
Template components: Header (text/image/video), Body (text with variables), Footer (text), Buttons (quick reply / URL). Sync with Meta Business Account via API.
Campaigns (Bulk Messaging)
Campaigns enable sending templates to a list of contacts at scale. Campaign statuses: Draft Scheduled Running Completed Failed
Each campaign tracks totalRecipients, sentCount, and failedCount for delivery reporting.
AI Chatbots
OrbitDesk integrates with agents.fastlab.ai to provide AI-powered chatbots with Retrieval-Augmented Generation (RAG) and optional AI Function Calling. Each tenant can create multiple chat agents with their own knowledge bases and tool capabilities.
How It Works
- Create a Chat Agent — Define name, description, and behavior in Chat Agents
- Upload Datasources — Files (PDF, text), URLs, or raw text for RAG indexing
- Enable AI Function Calling — Optionally give the agent tools to take actions (CRM lookups, bookings, flow triggers)
- Assign to Conversations — Bot automatically responds to incoming messages
- Human Handoff — Configurable keywords trigger transfer to a live agent
Two Agent Modes
RAG Mode (default)
The agent answers questions using a knowledge base (PDFs, URLs, text). Queries are embedded and matched via vector search against the tenant's indexed datasources. Best for Q&A and support bots.
Tool Calling Mode
The agent runs a multi-step function-calling loop — it can look up contacts, update CRM fields, book appointments, trigger automation flows, and update conversation status before sending a final reply.
AI Function Calling
When AI Function Calling is enabled on a chat agent, incoming messages are routed to the Orbit FastAPI AI service instead of the standard RAG path. The service runs an autonomous tool-calling loop:
Available Tools (5)
| Tool | What it does | Host app endpoint |
|---|---|---|
lookup_crm |
Search contacts by phone, name, or email | GET /contacts?search=... |
update_contact |
Update contact name, email, notes, tags, or custom fields | PATCH /contacts/:id |
book_appointment |
Save a structured appointment note on a contact record | GET + PATCH /contacts/:id |
trigger_flow |
Trigger an automation flow via its webhook token | POST /webhooks/:token |
update_conversation |
Change conversation status or tags | PATCH /conversations/:id |
Configuring Tool Calling via the UI
Tool calling is configured in Chat Agents — no API calls or seeders required.
- Open Chat Agents and click Add Agent or the edit icon on an existing agent.
- Scroll to the AI Function Calling section and toggle it on.
- Configure the fields:
| Field | Description |
|---|---|
| Provider | openai or anthropic |
| Model | e.g. gpt-4o-mini, gpt-4o, claude-3-5-haiku |
| System Prompt | Optional custom instructions for the tool-calling agent |
| Enabled Tools | Checkboxes for each of the 5 built-in tools |
| Max Iterations | Tool-calling rounds before fallback (1–20, default 10) |
The configuration is stored on the ChatAgent record in PostgreSQL as toolsEnabled (boolean) and toolsConfig (JSON).
max_iterations is reached or the LLM provider throws a fatal error, the agent replies with a polite fallback message and sets fallback_used: true in the response log. The conversation is never silently dropped.
AI Service (FastAPI)
The standalone Python microservice at services/ai/ handles both RAG and tool-calling paths:
POST /api/v1/agent/chat— Agentic tool-calling loop (OpenAI or Anthropic)POST /chat/completion— Multi-provider LLM chatPOST /rag/query— RAG queries against indexed datasourcesGET /health— Health check endpoint
The service is stateless — all context (contact info, conversation ID, tool config) is passed per-request by NestJS. Authentication uses short-lived HS256 JWTs (INTERNAL_JWT_SECRET) that expire after 2 minutes. See services/ai/REQUIREMENTS.md for the full API spec.
AI Voice Calling
OrbitDesk includes a full AI-powered voice calling system built on Twilio for telephony and OpenAI Realtime API for conversational AI. AI voice agents can make outbound calls, converse naturally with contacts, execute tool calls, and qualify leads — all autonomously.
Voice Call Flow
Core Components
Voice Agents
Configure AI agents with custom system prompts, voice selection (OpenAI or ElevenLabs), tool capabilities, knowledge base integration, and conversation flow design.
Voice Campaigns
Schedule bulk outbound call campaigns. Assign an agent, contact list, phone number, concurrency limits, and retry configuration. DNC contacts are automatically skipped.
Knowledge Bases
Upload documents (PDF, text), URLs, or raw text. Content is chunked and embedded with pgvector for real-time RAG retrieval during live calls.
Call Logs & Transcripts
Full call history with audio playback (wavesurfer.js), live transcription viewer, sentiment analysis, lead qualification, and tool call logs.
Phone Numbers
Browse and purchase Twilio phone numbers, assign them to voice agents, and manage your number inventory per tenant.
Voice Analytics
Dashboard with call volume trends, outcome distribution, sentiment breakdown, agent performance, campaign comparison, cost analysis, lead funnel, and hourly heatmap.
Voice Agent Configuration
| Field | Type | Description |
|---|---|---|
name | String | Agent display name |
systemPrompt | Text | Instructions and persona for the AI (supports dynamic variables) |
voiceProvider | OPENAI / ELEVENLABS | TTS voice provider |
voiceId | String | Voice selection (e.g., "alloy", "echo", "shimmer" for OpenAI) |
model | String | OpenAI model (default: gpt-4o-realtime-preview) |
temperature | Float | AI creativity (0.0 – 1.0, default 0.7) |
maxCallDuration | Int | Auto-hangup after N seconds (default 600) |
greetingMessage | String | First message the agent speaks when the call is answered |
toolsConfig | JSON | Array of callable tools (book_appointment, transfer_call, lookup_crm, etc.) |
flowConfig | JSON | Visual call flow (optional alternative to free-form system prompt) |
knowledgeBaseId | UUID | Linked knowledge base for RAG retrieval during calls |
Available Agent Tools
| Tool | Description |
|---|---|
book_appointment | Schedule an appointment (date, time, notes) |
transfer_call | Transfer to a live agent or external number |
send_sms | Send an SMS to the contact during the call |
lookup_crm | Look up contact details from the CRM |
search_knowledge_base | Search the knowledge base for relevant information |
save_lead_info | Save captured lead data (name, email, interests) |
end_call | Gracefully end the conversation |
custom_webhook | POST to an external URL with call context |
Voice Campaigns
Voice campaigns enable automated bulk outbound calling with AI agents. Campaigns support scheduling, concurrency control, retry logic, and DNC compliance.
| Setting | Description |
|---|---|
| Agent | Which voice agent handles the calls |
| Contact List | Target contacts (DNC contacts automatically excluded) |
| Phone Number | Twilio number to call from |
| Concurrency | Max simultaneous calls (1–10) |
| Schedule | Start time, end time, timezone, active days |
| Retry Config | Max retries, delay between retries, retry on which statuses |
isOnDncList = true are automatically skipped by the campaign scheduler. The DNC list is managed from Settings > DNC List where you can add numbers individually, upload a CSV, or search and remove entries.
Call Lifecycle
Other terminal states: FAILED NO_ANSWER BUSY CANCELED
Post-Call Processing
After each call ends, a Bull queue pipeline performs:
- Transcription — Full conversation transcript stored as JSON
- Summarization — AI-generated call summary via Orbit FastAPI
- Sentiment Analysis — POSITIVE / NEUTRAL / NEGATIVE / MIXED / UNKNOWN
- Lead Qualification — HOT / WARM / COLD with score, interests, and next steps
- Cost Calculation — Per-minute, TTS, STT, and LLM token costs tracked
- Webhook Dispatch —
call.completed,lead.qualifiedevents sent to tenant webhooks
Knowledge Bases
Voice knowledge bases provide real-time information retrieval during live calls. When the AI agent needs specific information, it queries the knowledge base via pgvector semantic search.
| Source Type | Description |
|---|---|
FILE | Upload PDF or text documents (chunked and embedded) |
URL | Crawl a web page, extract text, chunk and embed |
TEXT | Paste raw text content directly |
Embeddings are generated via OpenAI text-embedding-3-small (1536 dimensions) and stored with pgvector for fast similarity search.
Voice Analytics Dashboard
The analytics dashboard provides 8 visualization panels:
KPI Cards
Total calls, total duration, total cost, and success rate.
Call Volume
30-day area chart with daily calls broken down by status.
Outcomes & Sentiment
Donut charts showing call outcome distribution and sentiment breakdown.
Agent Performance
Table comparing agents by total calls, success rate, avg duration, and cost.
Campaign Comparison
Bar chart comparing campaigns by total, completed, and failed calls.
Lead Funnel
Total → Connected → Completed → Qualified conversion funnel.
Cost Breakdown
Daily cost trend with per-type breakdown (call minutes, TTS, STT, LLM tokens).
Hourly Heatmap
7×24 heatmap showing call volume by day of week and hour.
Per-Tenant API Key Isolation
Each tenant configures their own API keys for voice services in Settings > Integrations:
- OpenAI API Key — Used for Realtime API and embeddings. Encrypted at rest (AES), synced to Orbit FastAPI.
- ElevenLabs API Key — Optional, for premium voice synthesis. Encrypted at rest.
- Twilio Credentials — Account SID + Auth Token for telephony. Encrypted at rest.
Keys are resolved per-request: tenant-specific key first, with global environment variable as fallback.
Plugin System
OrbitDesk has a modular plugin architecture. Plugins extend functionality and are installed per-tenant from the marketplace.
| Plugin | Key | Category | Capabilities |
|---|---|---|---|
| WhatsApp QR | whatsapp-qr | Messaging | Enable Baileys QR code pairing |
| Google Sheets | google-sheets | Write flow data to spreadsheets | |
| Twilio | twilio | Communication | SMS sending, voice calls |
| Email (SMTP) | email | Communication | Send emails from flows |
Plugins can be FREE or PAID, and have visibility: GLOBAL (all tenants) or TENANT (specific tenant). Custom plugins can be uploaded as .tgz packages.
Multi-Tenancy & Authentication
Tenant Isolation
Every resource (contacts, conversations, flows, templates, etc.) is scoped to a tenantId. The API extracts tenant context from the JWT token, ensuring complete data isolation between organizations.
Authentication Methods
OrbitDesk supports two authentication methods. Both extract tenant context automatically:
JWT (Dashboard Users)
Full access based on user role (admin/manager/agent).
API Key (External Integrations)
Limited to assigned scopes. No admin access. Details →
Role-Based Access Control
| Role | Scope | Capabilities |
|---|---|---|
| SUPERADMIN | Platform-wide | Manage all tenants, approve registrations, platform settings |
| ADMIN | Tenant | Full tenant management: users, settings, plugins, integrations, flows |
| MANAGER | Tenant | Manage team, view analytics, manage flows and templates |
| AGENT | Tenant | Handle conversations, send messages, view contacts |
Tenant Onboarding
Subscription Plans
Each plan defines limits: maxAgents, maxContacts, maxMessagesPerMonth, plus a flexible features JSON for feature flags. Plans can be priced and sorted in the marketplace.
Dashboard & Analytics
The home dashboard provides a real-time overview of key metrics, cached in Redis for 5 minutes.
Total Contacts
Count of all contacts in the tenant's CRM.
Messages Today
Total inbound + outbound messages sent today.
Active Conversations
Conversations with status "open" or "assigned".
Active Flows
Number of flows with status "ACTIVE".
7-Day Message Volume Chart — Line/bar chart showing inbound vs outbound messages over the past week, powered by ApexCharts.
Subscriptions & Billing
OrbitDesk includes a complete subscription and billing system with support for multiple payment gateways. Tenants can subscribe to plans with resource limits, and superadmins can manage plans, configure payment providers, and manually assign plans.
Payment Gateways
Stripe
Full integration with Stripe Checkout for subscription payments. Supports automatic webhook handling for activation, renewal, payment failure, and cancellation events.
Razorpay
Native Razorpay subscription integration with dynamic plan creation. Handles subscription activation, charging, halting, and cancellation via webhooks.
Plan Management
Superadmins can create and manage subscription plans with configurable resource limits:
Resource Limits
Each plan defines limits for: agents, contacts, messages/month, flows, chat agents, templates, and campaigns.
Limit Enforcement
Resource creation is automatically blocked when a tenant exceeds their plan limits. Usage tracked in real time with Redis caching (60–120s TTL).
Gateway Integration
Plans can be linked to Stripe price IDs and Razorpay plan IDs for automatic billing. Dynamic pricing creation is also supported.
Manual Assignment
Superadmins can manually assign plans to tenants without payment, useful for enterprise deals, trials, or internal accounts.
Subscription Lifecycle
ACTIVE, TRIALING, PAST_DUE (payment failed), or CANCELED. Cancellations take effect at the end of the current billing period.
Webhook Events
| Provider | Event | Action |
|---|---|---|
| Stripe | checkout.session.completed | Activate subscription |
| Stripe | invoice.payment_succeeded | Renew subscription period |
| Stripe | invoice.payment_failed | Mark as PAST_DUE |
| Stripe | customer.subscription.deleted | Mark as CANCELED |
| Razorpay | subscription.activated | Activate subscription |
| Razorpay | subscription.charged | Renew subscription period |
| Razorpay | subscription.pending / halted | Mark as PAST_DUE |
| Razorpay | subscription.cancelled | Mark as CANCELED |
Usage Tracking
The PlanLimitsService tracks resource usage across seven dimensions and enforces limits automatically:
| Resource | Plan Limit Field | Source |
|---|---|---|
| Agents | maxAgents | PostgreSQL (User count) |
| Contacts | maxContacts | PostgreSQL (Contact count) |
| Messages/month | maxMessagesPerMonth | MongoDB (current month) |
| Flows | maxFlows | PostgreSQL (Flow count) |
| Chat Agents | maxChatAgents | PostgreSQL (ChatAgent count) |
| Templates | maxTemplates | PostgreSQL (Template count) |
| Campaigns | maxCampaigns | PostgreSQL (Campaign count) |
| Voice Agents | maxVoiceAgents | PostgreSQL (VoiceAgent count) |
| Calls/month | maxCallsPerMonth | PostgreSQL (Call count, current month) |
| Knowledge Bases | maxKnowledgeBases | PostgreSQL (VoiceKnowledgeBase count) |
Payment Provider Configuration
Payment providers are configured via the Superadmin panel. Credentials are stored encrypted using AES encryption and masked in API responses (only last 4 characters shown). Only one provider can be active at a time.
- Publishable Key (
pk_live_...) - Secret Key (
sk_live_...) - Webhook Secret (
whsec_...)
- Key ID (
rzp_live_...) - Key Secret
- Webhook Secret
API Reference
All API endpoints are prefixed with /api/v1. Interactive Swagger docs are available at /api/docs in development.
Bearer token in the Authorization header or an API key in the X-Api-Key header. Tenant context is extracted automatically from both JWT and API key. See REST API Access for API key details.
Auth
Conversations
Messages
Contacts
Flows
Webhooks (Flow Triggers)
Templates
Chat Agents
Channel Adapters (public inbound)
Plans & Subscriptions
Webhooks (Payment)
Superadmin — Plans
Superadmin — Payment Providers
API Keys
Voice Agents
Voice Knowledge Bases
Voice Campaigns
Calls
Phone Numbers
Voice Analytics
Twilio Webhooks (Voice)
DNC (Do Not Call)
Other
Data Models
PostgreSQL (Prisma ORM)
Relational data for entities that need ACID guarantees, foreign keys, and complex queries.
| Model | Key Fields | Purpose |
|---|---|---|
| Tenant | name, slug, connectionType, status, settings | Organization / workspace |
| User | email, name, role, tenantId, isActive | Team members with RBAC |
| Contact | phone, name, email, tags, customFields | CRM contacts |
| Flow | name, graphJson, status, triggerType | Automation workflows |
| Template | name, language, category, status, components | WhatsApp message templates |
| Campaign | name, templateId, status, sentCount | Bulk messaging campaigns |
| Plan | name, price, maxAgents, maxContacts, maxMessagesPerMonth, maxFlows, maxChatAgents, maxTemplates, maxCampaigns, stripePriceId, razorpayPlanId, features | Subscription tiers with resource limits |
| Subscription | tenantId, planId, status, paymentProvider, currentPeriodStart/End, cancelAtPeriodEnd, stripeSubscriptionId, razorpaySubscriptionId | Active subscriptions with payment tracking |
| PaymentProviderConfig | provider (unique), displayName, isActive, credentials (encrypted JSON) | Payment gateway configurations (Stripe/Razorpay) |
| Plugin | key, name, billing, visibility, type | Installable extensions |
| TenantPlugin | tenantId, pluginId, status, config | Plugin installations |
| ChatAgent | name, orbitChatbotId, handoffKeywords | AI chatbot configs |
| ApiKey | name, keyHash, scopes[], expiresAt, lastUsedAt, isActive | Scoped API keys for external integrations |
| VoiceAgent | name, systemPrompt, voiceProvider, voiceId, model, temperature, maxCallDuration, toolsConfig, flowConfig, knowledgeBaseId | AI voice agent configurations |
| VoiceKnowledgeBase | name, description, embeddingStatus, totalChunks | Knowledge bases for voice RAG |
| VoiceKnowledgeBaseSource | type (FILE/URL/TEXT), fileName, url, textContent, status, chunksCount | KB data sources (documents, URLs, text) |
| VoiceKnowledgeBaseChunk | content, embedding (vector 1536), metadata, tokenCount | Embedded text chunks for pgvector search |
| TwilioPhoneNumber | twilioSid, number, friendlyName, country, capabilities, assignedAgentId | Purchased Twilio phone numbers |
| VoiceCampaign | name, agentId, type, status, contactListId, phoneNumberId, scheduleConfig, concurrencyLimit, retryConfig, totalContacts, completedContacts | Bulk outbound call campaigns |
| VoiceCampaignContact | campaignId, contactId, status, attempts, nextRetryAt, lastCallId | Per-contact campaign progress tracking |
| Call | agentId, contactId, twilioCallSid, direction, status, fromNumber, toNumber, durationSeconds, recordingUrl, transcript, summary, sentiment, leadQualification, toolCallsLog, costCents | Individual call records with AI analysis |
| CallEvent | callId, eventType, payload, timestamp | Call event timeline (status changes, tool calls, errors) |
| VoiceUsageLog | type (CALL_MINUTE/TTS_CHARACTER/STT_MINUTE/LLM_TOKEN/PHONE_NUMBER), quantity, costCents, callId | Granular voice usage tracking for billing |
| ContactList | name, contactsCount | Reusable contact lists for campaigns |
MongoDB (Mongoose ODM)
Document store for high-volume, time-series data that benefits from flexible schemas.
| Collection | Key Fields | Purpose |
|---|---|---|
| Conversation | tenantId, contactPhone, agentId, status, lastMessage, tags | Chat conversations with metadata |
| Message | conversationId, direction, type, content, status | Individual chat messages |
| FlowSession | flowId, contactId, currentNodeId, variables, state | Active flow execution state |
| BaileysAuth | tenantId, encrypted auth data | QR-based WhatsApp session storage |
Real-Time (WebSocket)
OrbitDesk uses Socket.IO on the /chat namespace for real-time communication. JWT authentication is required for connection.
| Event | Direction | Description |
|---|---|---|
join_conversation | Client → Server | Join a conversation room |
leave_conversation | Client → Server | Leave a conversation room |
send_message | Client → Server | Broadcast message |
typing | Client → Server | Typing indicator |
new_message | Server → Client | New message received (all channels) |
message_status | Server → Client | Delivery/read status update (all channels) |
notification | Server → Client | Tenant-wide notification |
baileys:qr | Server → Client | QR code for scanning |
baileys:status | Server → Client | Connection status change |
flow:session | Server → Client | Flow execution updates |
call:started | Server → Client | Voice call initiated (includes callId, agentId, contactId) |
call:status | Server → Client | Call status change (RINGING, IN_PROGRESS, COMPLETED, FAILED) |
call:transcript | Server → Client | Real-time transcript delta (role, text, timestamp) |
call:completed | Server → Client | Call ended with summary, sentiment, duration |
voice-campaign:progress | Server → Client | Campaign progress update (completed/total, success/fail counts) |
voice-campaign:completed | Server → Client | Campaign finished with final stats |
Rooms: tenant:{tenantId} (tenant-wide events), conversation:{conversationId} (per-conversation events)
/api/v1/realtime/media-stream. This is separate from the Socket.IO chat gateway and handles real-time audio bridging between Twilio and OpenAI. Nginx is configured with 1-hour timeouts for this path.
Security
JWT Authentication
Short-lived access tokens (15m) with refresh token rotation (7d). Tokens contain userId, tenantId, and role.
AES-256 Encryption
Sensitive fields (API keys, OAuth tokens, WhatsApp credentials) encrypted at rest with a 32-byte key.
Webhook Verification
All channel webhooks verified by signature: Meta (HMAC-SHA256 X-Hub-Signature-256), Twilio (request URL signature), Telegram (token in path). Live Chat uses JWT visitor tokens.
Tenant Isolation
Every query filtered by tenantId extracted from JWT. No cross-tenant data leakage.
Helmet + CORS
Security headers via Helmet. CORS restricted to configured frontend origin with credentials.
Input Validation
NestJS ValidationPipe with whitelist mode. No unrecognized properties allowed.
API Key Auth
SHA-256 hashed keys with fine-grained scopes. Raw keys shown once, never stored. Supports expiry and instant revocation.
Rate Limiting
Per-key and per-IP rate limiting via @nestjs/throttler. 100 requests/minute default. Prevents API abuse.
Project Structure
whatsapp-crm/
├── apps/
│ ├── api/ # NestJS Backend (REST + WebSocket)
│ │ ├── prisma/ # Prisma schema, migrations, seed
│ │ └── src/
│ │ ├── common/ # Shared guards, decorators, schemas
│ │ ├── modules/ # Feature modules (auth, contacts, flows,
│ │ │ # voice-agents, twilio-voice, voice-campaigns,
│ │ │ # voice-knowledge-base, voice-analytics, calls...)
│ │ └── main.ts # App bootstrap
│ ├── web/ # React Frontend (SPA)
│ │ └── src/
│ │ ├── pages/ # Page components (inbox, flows, settings,
│ │ │ # voice-agents, voice-campaigns, calls,
│ │ │ # phone-numbers, voice-analytics...)
│ │ ├── router/ # Route definitions
│ │ ├── services/ # API client (Axios + TanStack Query)
│ │ └── layouts/ # App shell, sidebar, topbar
│ └── plugins/ # Installable plugin packages
│ ├── orbitdesk-plugin-email/
│ ├── orbitdesk-plugin-google-sheets/
│ ├── orbitdesk-plugin-twilio/
│ └── orbitdesk-plugin-whatsapp-qr/
├── packages/
│ ├── shared/ # Shared types, validators, constants
│ └── ui/ # UI component library (Tailwind-based)
├── services/
│ └── ai/ # FastAPI Python AI service
├── docker/ # Dockerfiles + Nginx configs
├── scripts/ # Deployment & backup scripts
├── .github/workflows/ # CI/CD pipelines
├── turbo.json # Turborepo configuration
├── pnpm-workspace.yaml # Workspace definition
├── docker-compose.prod.yml # Production Docker Compose
└── ecosystem.config.cjs # PM2 configuration
Environment Variables
All environment variables are defined in .env at the project root. Copy from .env.example and configure for your environment.
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | PostgreSQL connection string |
MONGODB_URI | Yes | MongoDB connection string |
REDIS_URL | Yes | Redis connection string |
JWT_SECRET | Yes | Secret for signing access tokens |
JWT_REFRESH_SECRET | Yes | Secret for signing refresh tokens |
ENCRYPTION_KEY | Yes | 32-byte key for AES encryption |
CORS_ORIGIN | Yes | Allowed frontend origin (e.g., https://your-domain.com) |
NODE_ENV | Yes | Environment: development / production |
OPENAI_API_KEY | For AI | OpenAI API key for chat completions |
AI_SERVICE_URL | For AI | URL of the FastAPI AI service |
GOOGLE_OAUTH_CLIENT_ID | For Sheets | Google OAuth client ID |
GOOGLE_OAUTH_CLIENT_SECRET | For Sheets | Google OAuth client secret |
SMTP_HOST | For Email channel | SMTP server hostname (outbound email) |
SMTP_PORT | For Email channel | SMTP port (default: 587) |
SMTP_USER | For Email channel | SMTP username / address |
SMTP_PASS | For Email channel | SMTP password / app password |
TWILIO_ACCOUNT_SID | For SMS/Voice | Twilio account SID (global fallback) |
TWILIO_AUTH_TOKEN | For SMS/Voice | Twilio auth token (global fallback) |
TWILIO_SMS_FROM | For SMS channel | Twilio phone number for outbound SMS (E.164) |
ELEVENLABS_API_KEY | For Voice | ElevenLabs API key for premium TTS voices (global fallback) |
ORBIT_API_URL | For Voice | Orbit FastAPI service URL for AI inference |
STRIPE_SECRET_KEY | For Billing | Stripe secret key (fallback if not configured in DB) |
STRIPE_WEBHOOK_SECRET | For Billing | Stripe webhook signing secret |
RAZORPAY_KEY_ID | For Billing | Razorpay key ID (fallback if not configured in DB) |
RAZORPAY_KEY_SECRET | For Billing | Razorpay key secret |
RAZORPAY_WEBHOOK_SECRET | For Billing | Razorpay webhook signing secret |
FRONTEND_URL | For Billing | Frontend URL for checkout success/cancel redirects |
STORAGE_TYPE | No | local (default) or s3 |
IS_DEMO | No | Enable demo mode (restricts destructive actions) |
Deployment
OrbitDesk supports two deployment strategies on Ubuntu servers. See DEPLOYMENT.md for the complete step-by-step guide.
Docker Deployment
All services containerized with docker-compose.prod.yml. Includes Nginx, Certbot SSL auto-renewal, and health checks.
docker compose -f docker-compose.prod.yml up -d
Bare Metal Deployment
Direct install with PM2 process manager, system Nginx, and manual database setup. Automated setup script provided.
sudo bash scripts/deploy-bare-metal.sh
Server Requirements
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 vCPUs | 4 vCPUs |
| RAM | 4 GB | 8 GB |
| Storage | 40 GB SSD | 100 GB SSD |
| OS | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
Required Ports (Firewall)
Internal ports (3000, 3002, 5432, 27017, 6379, 8000) should NOT be exposed publicly.
CI/CD Pipeline
GitHub Actions workflows for automated deployment on push to main.
Pipeline Flow
Required GitHub Secrets
| Secret | Description | Example |
|---|---|---|
DEPLOY_HOST | Server IP or hostname | 203.0.113.50 |
DEPLOY_USER | SSH username | orbitdesk |
DEPLOY_SSH_KEY | SSH private key | Ed25519 key contents |
DEPLOY_SSH_PORT | SSH port (optional) | 22 |
Workflow Files
.github/workflows/deploy-docker.yml— Docker-based deployment.github/workflows/deploy-bare-metal.yml— Artifact-based deployment via SCP + PM2
Developer Setup
Prerequisites
- Node.js 20 (with corepack enabled)
- pnpm 9.15.4 (via corepack)
- Docker (for databases) or local PostgreSQL 16, MongoDB 7, Redis 7
- Python 3.11 (for AI service)
Quick Start
# 1. Clone the repository
git clone https://github.com/your-org/whatsapp-crm.git
cd whatsapp-crm
# 2. Install dependencies
corepack enable
pnpm install
# 3. Start databases (Docker)
docker compose -f docker/docker-compose.yml up -d
# 4. Configure environment
cp .env.example .env
# Edit .env with your settings
# 5. Setup database
pnpm db:generate
pnpm db:migrate
pnpm db:seed
# 6. Start development servers
pnpm dev
# Web: http://localhost:3000
# API: http://localhost:3002
# Docs: http://localhost:3002/api/docs
# 7. (Optional) Start AI service
cd services/ai
python3.11 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8000
Useful Commands
| Command | Description |
|---|---|
pnpm dev | Start all dev servers (Turborepo) |
pnpm build | Build all packages |
pnpm lint | Lint all packages |
pnpm db:migrate | Run Prisma migrations |
pnpm db:seed | Seed database with sample data |
pnpm db:studio | Open Prisma Studio (DB browser) |
pnpm db:generate | Generate Prisma client |
Frequently Asked Questions
What is OrbitDesk?
OrbitDesk is a multi-tenant SaaS platform for managing WhatsApp Business communications. It provides a real-time inbox, visual flow automation builder, AI-powered chatbots, template management, and a plugin ecosystem — all designed for teams to manage customer interactions at scale.
What are the two WhatsApp connection methods?
Cloud API (Official): Uses Meta's official Business Platform. Requires a verified business account, phone number ID, and access token. Best for production use with high reliability.
Baileys QR: Connects any WhatsApp number by scanning a QR code (similar to WhatsApp Web). Requires the whatsapp-qr plugin. Suitable for testing or personal numbers, but less stable than the official API.
How does multi-tenancy work?
Each organization (tenant) is completely isolated. Every database query is filtered by tenantId, extracted from the authenticated user's JWT token. Tenants cannot see or access each other's data. A Superadmin can manage all tenants from the platform admin panel.
What databases are used and why?
PostgreSQL: Primary relational database (via Prisma ORM) for structured data with referential integrity — tenants, users, contacts, flows, templates, plans, subscriptions, and plugins.
MongoDB: Document database (via Mongoose) for high-volume, time-series data — conversations, messages, and flow execution sessions. Flexible schema suits the varied message content types.
Redis: In-memory cache for dashboard analytics (5-minute TTL), session data, rate limiting, and real-time pub/sub.
How do flow automations work?
Flows are visual workflows created in the FlowGram.ai-powered editor. A flow starts with a Trigger node (e.g., message received) and chains through nodes like Send Message, Condition, Delay, AI Response, HTTP Request, etc.
When triggered, the FlowExecutorService traverses the graph, executing each node via the NodeExecutorFactory. Flow state (current node, variables, execution log) is persisted in MongoDB as a FlowSession.
How do AI chatbots (Chat Agents) work?
Chat Agents operate in two modes:
- RAG mode (default) — Create an agent, upload datasources (PDFs, text, URLs) for RAG indexing, then assign it to conversations. The bot answers questions using the knowledge base. Configurable handoff keywords trigger transfer to a human agent.
- Tool calling mode — Enable AI Function Calling on the agent (via Chat Agents › edit › AI Function Calling toggle). The agent runs an autonomous loop, calling tools like
lookup_crm,book_appointment,trigger_flow,update_contact, andupdate_conversationbefore returning a final reply. Configure provider (OpenAI/Anthropic), model, system prompt, which tools to enable, and max iterations — all from the UI.
What LLM providers are supported?
The FastAPI AI service supports: OpenAI (GPT-4o, GPT-4, GPT-3.5), Anthropic (Claude), Google Gemini, Mistral, Deepseek, and X.ai. Provider can be configured globally or overridden per flow node.
How do I add a new plugin?
Plugins follow a standard structure in apps/plugins/. Each plugin has a orbitdesk-plugin.json manifest defining key, name, capabilities, and billing. Plugins can be uploaded as .tgz packages via the Plugin API or added to the codebase directly.
Custom plugins can be tenant-scoped (visible only to the uploading tenant) or global (visible to all tenants, requires superadmin).
What is the difference between Docker and Bare Metal deployment?
Docker: All services (app + databases + nginx) run in containers orchestrated by docker-compose.prod.yml. Easier to set up, reproducible, includes automatic SSL renewal via Certbot container. Recommended for most use cases.
Bare Metal: Services run directly on the OS with PM2 as the process manager. Databases installed as system services. Requires more manual configuration but gives full control over the stack. Good for servers that already have databases running.
How do I set up SSL/HTTPS?
Docker: The production compose includes a Certbot container that automatically renews certificates. Initial certificate obtained via certbot certonly --standalone -d your-domain.com.
Bare Metal: Run sudo certbot --nginx -d your-domain.com after setting up the Nginx config. Certbot auto-configures SSL and sets up automatic renewal via systemd timer.
How do backups work?
The scripts/backup.sh script creates compressed backups of PostgreSQL (pg_dump), MongoDB (mongodump), and the uploads directory. It runs daily via cron at 2 AM and retains backups for 7 days by default. Backups are stored in /var/www/orbitdesk/backups/.
What is Demo Mode?
Setting IS_DEMO=true restricts destructive actions: users cannot delete flows, disconnect WhatsApp, delete templates, or modify critical settings. This is useful for public demos or showcases where you want to prevent data loss.
How do I troubleshoot WebSocket connection issues?
Common causes: (1) Nginx missing WebSocket upgrade headers — ensure proxy_set_header Upgrade and Connection "upgrade" are set for /socket.io/. (2) CORS_ORIGIN not matching the frontend domain. (3) JWT token expired or missing. Check browser console for Socket.IO errors and API logs for connection attempts.
What is the Superadmin panel?
The Superadmin panel (/platform/* routes) is only accessible to users with the SUPERADMIN role. It provides: tenant management (list, approve, reject new registrations), cross-tenant user management, public site page editing (landing page, privacy policy, terms of service), subscription plan management (create/edit/deactivate plans with resource limits), payment provider configuration (Stripe & Razorpay with encrypted credential storage), and manual plan assignment to tenants.
How does the campaign system work?
Campaigns allow sending a WhatsApp template message to a batch of contacts. You select a template, target contacts (by tags or manual selection), and schedule the send. The system tracks total recipients, sent count, and failed count. Campaign statuses: Draft, Scheduled, Running, Completed, Failed, Canceled.
How do subscriptions and billing work?
OrbitDesk supports Stripe and Razorpay as payment gateways for subscription billing. Superadmins configure payment provider credentials (encrypted at rest) via the platform admin panel. Only one provider can be active at a time.
Tenants subscribe by selecting a plan and completing checkout via the active payment provider. The system handles the full subscription lifecycle (activation, renewal, payment failures, cancellation) through webhook events. Plans define resource limits (agents, contacts, messages/month, flows, chat agents, templates, campaigns) that are enforced automatically when tenants create resources.
Superadmins can also manually assign plans to tenants without requiring payment, useful for enterprise deals or trial accounts.
How do API keys work for external integrations?
API keys allow external services (Zapier, CRMs, custom apps) to access OrbitDesk's REST API without user login. Admins create keys from Settings > API Keys, selecting specific scopes (e.g., contacts:read, messages:send). The raw key is shown only once at creation.
To authenticate, send the key in the X-Api-Key header. Keys are SHA-256 hashed before storage, support optional expiry dates, and can be instantly revoked. Each key is rate-limited to 100 requests/minute. See the REST API Access section for full details.
How does AI Voice Calling work?
AI Voice Calling uses Twilio for telephony and OpenAI Realtime API for real-time conversational AI. When a call is initiated, Twilio establishes a media stream WebSocket to the NestJS server. The server bridges this to OpenAI's Realtime API, transcoding audio between Twilio's μ-law 8kHz format and OpenAI's PCM16 format.
The AI agent converses naturally, can execute tools (book appointments, look up CRM data, search knowledge bases), and qualifies leads in real time. After the call ends, the system runs post-call processing: transcription, summarization, sentiment analysis, and lead qualification via the Orbit FastAPI microservice.
What do I need to set up voice calling?
You need three things:
1. Twilio Account: Account SID + Auth Token for telephony. Purchase at least one phone number via the Phone Numbers page.
2. OpenAI API Key: Required for the Realtime API (conversational AI) and embeddings (knowledge base). Configure in Settings > Integrations.
3. (Optional) ElevenLabs API Key: For premium voice synthesis. If not configured, OpenAI's built-in voices are used.
Each tenant configures their own API keys. Keys are encrypted at rest and resolved per-request with a global fallback.
What is the DNC (Do Not Call) list?
The DNC list prevents specific contacts from being called by voice campaigns. Contacts flagged with isOnDncList = true are automatically skipped during campaign execution. Manage the DNC list from Settings > DNC List where you can add individual numbers, bulk upload via CSV, or search and remove entries. This ensures compliance with Do Not Call regulations.
How do voice knowledge bases work?
Voice knowledge bases provide real-time information retrieval during live calls. You upload documents (PDF, text), URLs, or raw text. The system chunks the content, generates embeddings using OpenAI's text-embedding-3-small model (1536 dimensions), and stores them with pgvector in PostgreSQL.
During a live call, when the AI agent needs specific information, it uses the search_knowledge_base tool to perform semantic similarity search and retrieve relevant context. This enables agents to accurately answer questions about products, policies, or services.
Can I use S3/MinIO instead of local storage for media?
Yes. Set STORAGE_TYPE=s3 in your .env and configure S3_BUCKET, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, and optionally S3_ENDPOINT (for MinIO). The media module will automatically use S3 for uploads and generate pre-signed URLs for serving.
What are the user roles and their permissions?
SUPERADMIN: Platform-wide access — manage all tenants, approve registrations, manage platform settings and site pages.
ADMIN: Full tenant access — manage users, settings, plugins, integrations, flows, templates, and all conversations.
MANAGER: Limited admin access — manage team, view analytics, manage flows and templates, handle conversations.
AGENT: Operational access — handle assigned conversations, send messages, and view contacts.
OrbitDesk Documentation — Built with care
Node 20 • pnpm 9 • NestJS 11 • React 19 • FastAPI • PostgreSQL 16 • MongoDB 7 • Redis 7