# Channel Integrations

Facebook, Instagram, mail, Discord, call, and Call Pro runtimes and webhooks


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.

> [!WARNING]
> `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`](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/backend/plugins/frontline_api/src/modules/inbox/graphql/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`](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/backend/plugins/frontline_api/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`).

## 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

| Variable | Module | Purpose |
| --- | --- | --- |
| `FACEBOOK_*` / `INSTAGRAM_*` | facebook, instagram | Meta app credentials and OAuth settings |
| `FACEBOOK_GRAPH_URL` | facebook | Graph API base URL |
| `MAIL_RECEIVE_URL`, `MAIL_WORKER_URL`, `MAIL_TENANT`, `MAIL_WEBHOOK_SECRET` | mail | Inbound worker delivery |
| `MAIL_DOMAIN`, `MAIL_SENDING_ACCOUNT_ID`, `MAIL_SENDING_API_TOKEN`, `MAIL_SENDING_DAILY_LIMIT` | mail | 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 |

