Channel Integrations
Connect external channels to the inbox: Facebook, Instagram, mail, Discord, Call, and Call Pro, plus the integration lifecycle and webhook routes.
Backend: backend/plugins/frontline_api/src/modules/integrations/<kind>/ holds one runtime per kind; integrations/trpc is the in-plugin tRPC router. Frontend: frontend/plugins/frontline_ui/src/modules/{integrations,integrations-config}.
Concepts
Integration kinds
Every Integration record has a kind. inbox.getIntegrationKinds (modules/inbox/utils.ts) returns the registered map: messenger (Messenger), lead (Popups & forms), webhook (Webhook, a generic inbound webhook with an auto-generated token), booking, callpro (Callpro), mail (Email), facebook-messenger, facebook-post, instagram-messenger, instagram-post, discord-messenger (Discord), calls (Phone call), client (Client Portal), and vendor (Vendor Portal). Creation code also accepts cloudflarecalls as a service kind.
An integration is always attached to one channel. integrationsCreateExternalIntegration refuses channelId values that point at another user's personal channel; when no channelId is given, the integration lands in the connecting user's own personal channel.
integrationsCreateExternalIntegration fails with Channel … not found for an unknown channelId (create the channel first) and with Cannot create an integration on another user's personal channel when the target is someone else's scope: personal channel (omit channelId to use your own).
Lifecycle operations
Mutations live in resolvers/mutations/integrations.ts.
| Operation | Purpose |
|---|---|
integrationsCreateExternalIntegration(data, channelId, brandId, accountId, kind, name, …) | Create a channel integration; forwards to the per-kind service over tRPC |
integrationsCreateMessengerOnboarding / integrationsEditMessengerOnboarding / integrationsCreateMessengerIntegration / integrationsEditMessengerIntegration / integrationsSaveMessengerConfigs / integrationsSaveMessengerAppearanceData / integrationsSaveMessengerColorTheme / integrationsSaveMessengerTicketData | erxes messenger integration + widget config |
integrationsCreateLeadIntegration / integrationsEditLeadIntegration / integrationsCopyLeadIntegration | Lead (form) integrations |
integrationsEditCommonFields(_id, name, details, channelId, brandId) | Rename, re-channel, or update details; pushes sendUpdateIntegration to the kind's service |
integrationsRepair(_id, kind) | Recreate the service-side resources for an integration |
integrationsArchive(_id, status) | Archive/unarchive a conversation stream |
integrationsRemove(_id) | Remove the integration and its conversations/messages |
integrationsRemoveAccount(_id, kind) | Remove an OAuth account and all its integrations |
integrationsSendSms(integrationId, content, to) | Send an SMS through a supported integration |
Queries: integrations, integrationDetail, allLeadIntegrations, integrationsGetUsedTypes, integrationsGetUsedTypesByChannel, integrationGetLineWebhookUrl, integrationsTotalCount, and integrationsGetMessengerColorThemes, plus per-kind lists.
Webhook routes
src/routes.ts mounts routers on the plugin's Express app:
| Mount | Purpose |
|---|---|
/facebook | Meta webhook: GET verification against FACEBOOK_VERIFY_TOKEN config, POST Messenger/feed/comment events |
/instagram | Meta webhook for Instagram events |
/mail | POST /mail/receive: HMAC-signed email payloads from the Cloudflare Email Routing worker |
/callpro | POST /callpro/receive: Call Pro PBX events; mounted only when CALLPRO_ENABLED=true |
Inbound payloads flow into receiveInboxMessage, which creates/reuses the customer, conversation, and message and publishes conversationClientMessageInserted.
Facebook and Instagram (Meta)
App credentials come from getConfig: per-workspace config records first, environment variables as fallback; environment-only when VERSION=saas.
| Env var | Purpose |
|---|---|
FACEBOOK_APP_ID, FACEBOOK_APP_SECRET | Main Meta app for messenger/post integrations |
FACEBOOK_POST_APP_ID, FACEBOOK_POST_APP_SECRET | Separate app for comment/feed captures |
FACEBOOK_VERIFY_TOKEN | Webhook GET verification token |
FACEBOOK_PERMISSIONS | OAuth permission list |
FACEBOOK_LOGIN_REDIRECT_URL | OAuth redirect URL |
FACEBOOK_GRAPH_URL | Graph API base URL |
INSTAGRAM_APP_ID, INSTAGRAM_APP_SECRET, INSTAGRAM_PERMISSIONS, INSTAGRAM_LOGIN_REDIRECT_URL, INSTAGRAM_VERIFY_TOKEN | Instagram equivalents |
The facebook/instagram modules store per-integration tokens and page/account state, mirror comments/messages into conversations, and expose GraphQL login/reply/account helpers. Facebook and Instagram automation triggers/actions (e.g., comment events) are registered from meta/automations, and the Facebook comment outbox worker starts at plugin init (main.ts).
Inbound and outbound mail run through Cloudflare.
- A Cloudflare Email Routing worker posts inbound email to
POST /mail/receiveon this API, signed withMAIL_WEBHOOK_SECRET(HMAC).MAIL_RECEIVE_URL,MAIL_WORKER_URL, andMAIL_TENANTconfigure the worker side. - Mail messages thread onto conversations by
mailData/References/In-Reply-Toheaders; each mail integration maps to one inbox channel. - Outbound mail uses Cloudflare Email Sending. A workspace connects its own Cloudflare account (
mailCloudflareConnect,mailCloudflareProvision,mailCloudflareDisconnect); without one, the deployment's fallback account (MAIL_DOMAIN,MAIL_SENDING_ACCOUNT_ID,MAIL_SENDING_API_TOKEN,MAIL_SENDING_DAILY_LIMIT) is used. mailPipelineIntegration/mailPipelineConnect/mailPipelineUpdate/mailPipelineForwardVerified/mailPipelineDisconnectroute mail into pipelines;mailSendMail,mailMessageRetry,mailConversationDetail,mailSendingReadiness,mailCloudflareConnection,mailCloudflareSendingQuota,mailCloudflareZones,mailCheckConnectioncover sending, retry, and diagnostics.
Discord
initDiscord() runs at plugin startup (main.ts) and opens a Discord gateway client per configured integration; guild messages become conversations and agent replies post back through the Discord API. Discord automation entries are registered in meta/automations.
Call (SIP/CDR)
The call module stores CallCdrs and integrates a SIP provider via CALL_ENDPOINT_URL. CALL_WEBHOOK_REQUIRE_SIGNATURE=true enforces signed webhooks; CALL_SUBSCRIPTION_REQUIRE_AUTH=true requires auth on the call GraphQL subscription. CDR records join inbox search by phone number and feed the call report services.
Call Pro (webhook PBX)
Call Pro is gated per tenant (the subdomain a request belongs to):
CALLPRO_ENABLED=trueturns the module on;CALLPRO_SUBDOMAINSis a comma-separated tenant allowlist on shared deployments.- Tenants outside the allowlist get a disabled config, a
404from/callpro/receive, and rejected create/update operations. POST /callpro/receiveingests PBX call events, which appear as conversations/CDR entries and feed call reports.
Bot automations
Conversation automation is coordinated through automatedReplyControl on each conversation (status, reason, pausedUntil) and the conversationSetAutomatedReplyControl mutation. A bot sets handoff_requested; an agent accepts with human_active. The automationStatus inbox filter exposes these states (standby, handoff, responded).
Environment variables
| Variable | Module | Purpose |
|---|---|---|
FACEBOOK_* / INSTAGRAM_* | facebook, instagram | Meta app credentials and OAuth settings |
FACEBOOK_GRAPH_URL | Graph API base URL | |
MAIL_RECEIVE_URL, MAIL_WORKER_URL, MAIL_TENANT, MAIL_WEBHOOK_SECRET | Inbound worker delivery | |
MAIL_DOMAIN, MAIL_SENDING_ACCOUNT_ID, MAIL_SENDING_API_TOKEN, MAIL_SENDING_DAILY_LIMIT | Deployment-level Cloudflare Email Sending fallback | |
CALL_ENDPOINT_URL, CALL_WEBHOOK_REQUIRE_SIGNATURE, CALL_SUBSCRIPTION_REQUIRE_AUTH | call | SIP/CDR endpoint and auth toggles |
CALLPRO_ENABLED, CALLPRO_SUBDOMAINS | callpro | Enable + tenant allowlist |
DOMAIN, ENDPOINT_URL, JWT_TOKEN_SECRET, VERSION | shared | Platform URLs, token signing, saas mode |