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.

OperationPurpose
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 / integrationsSaveMessengerTicketDataerxes messenger integration + widget config
integrationsCreateLeadIntegration / integrationsEditLeadIntegration / integrationsCopyLeadIntegrationLead (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:

MountPurpose
/facebookMeta webhook: GET verification against FACEBOOK_VERIFY_TOKEN config, POST Messenger/feed/comment events
/instagramMeta webhook for Instagram events
/mailPOST /mail/receive: HMAC-signed email payloads from the Cloudflare Email Routing worker
/callproPOST /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 varPurpose
FACEBOOK_APP_ID, FACEBOOK_APP_SECRETMain Meta app for messenger/post integrations
FACEBOOK_POST_APP_ID, FACEBOOK_POST_APP_SECRETSeparate app for comment/feed captures
FACEBOOK_VERIFY_TOKENWebhook GET verification token
FACEBOOK_PERMISSIONSOAuth permission list
FACEBOOK_LOGIN_REDIRECT_URLOAuth redirect URL
FACEBOOK_GRAPH_URLGraph API base URL
INSTAGRAM_APP_ID, INSTAGRAM_APP_SECRET, INSTAGRAM_PERMISSIONS, INSTAGRAM_LOGIN_REDIRECT_URL, INSTAGRAM_VERIFY_TOKENInstagram 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).

Mail

Inbound and outbound mail run through Cloudflare.

  1. A Cloudflare Email Routing worker posts inbound email to POST /mail/receive on this API, signed with MAIL_WEBHOOK_SECRET (HMAC). MAIL_RECEIVE_URL, MAIL_WORKER_URL, and MAIL_TENANT configure the worker side.
  2. Mail messages thread onto conversations by mailData/References/In-Reply-To headers; each mail integration maps to one inbox channel.
  3. 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.
  4. mailPipelineIntegration/mailPipelineConnect/mailPipelineUpdate/mailPipelineForwardVerified/mailPipelineDisconnect route mail into pipelines; mailSendMail, mailMessageRetry, mailConversationDetail, mailSendingReadiness, mailCloudflareConnection, mailCloudflareSendingQuota, mailCloudflareZones, mailCheckConnection cover 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=true turns the module on; CALLPRO_SUBDOMAINS is a comma-separated tenant allowlist on shared deployments.
  • Tenants outside the allowlist get a disabled config, a 404 from /callpro/receive, and rejected create/update operations.
  • POST /callpro/receive ingests 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

VariableModulePurpose
FACEBOOK_* / INSTAGRAM_*facebook, instagramMeta app credentials and OAuth settings
FACEBOOK_GRAPH_URLfacebookGraph API base URL
MAIL_RECEIVE_URL, MAIL_WORKER_URL, MAIL_TENANT, MAIL_WEBHOOK_SECRETmailInbound worker delivery
MAIL_DOMAIN, MAIL_SENDING_ACCOUNT_ID, MAIL_SENDING_API_TOKEN, MAIL_SENDING_DAILY_LIMITmailDeployment-level Cloudflare Email Sending fallback
CALL_ENDPOINT_URL, CALL_WEBHOOK_REQUIRE_SIGNATURE, CALL_SUBSCRIPTION_REQUIRE_AUTHcallSIP/CDR endpoint and auth toggles
CALLPRO_ENABLED, CALLPRO_SUBDOMAINScallproEnable + tenant allowlist
DOMAIN, ENDPOINT_URL, JWT_TOKEN_SECRET, VERSIONsharedPlatform URLs, token signing, saas mode
Was this helpful?