Skip to content

Channel Adapters

Jina Connect uses a channel adapter pattern to abstract away provider-specific APIs. Each messaging channel (WhatsApp, Telegram, SMS, RCS) has one or more provider adapters that implement a common interface. This lets you swap providers without changing application code.

The ChannelRegistry (jina_connect/channel_registry.py) is a runtime registry where channel adapters register themselves during Django startup:

# Each app's AppConfig.ready() calls:
register_channel(platform, factory_function)
# At runtime, resolve the adapter:
adapter = get_channel_adapter(platform, tenant)

All channels map to canonical platform identifiers defined in jina_connect/platform_choices.py:

PlatformValueDescription
WHATSAPP"WHATSAPP"WhatsApp Business API
TELEGRAM"TELEGRAM"Telegram Bot API
SMS"SMS"SMS via provider APIs
RCS"RCS"RCS Business Messaging
EMAIL"EMAIL"Email (planned)
VOICE"VOICE"Voice calls (planned)

All channel adapters extend BaseChannelAdapter (wa/adapters/channel_base.py):

class BaseChannelAdapter(ABC):
@abstractmethod
def send_text(self, chat_id: str, text: str, **kwargs) -> dict:
"""Send a plain text message."""
@abstractmethod
def send_media(self, chat_id: str, media_type: str,
media_url: str, caption: str = None, **kwargs) -> dict:
"""Send a media message (image, video, document, audio)."""
@abstractmethod
def send_keyboard(self, chat_id: str, text: str,
keyboard: list, **kwargs) -> dict:
"""Send a message with interactive buttons/keyboard."""
@abstractmethod
def get_channel_name(self) -> str:
"""Return the canonical channel name."""

All adapter methods return an AdapterResult dataclass with success, message_id, raw_response, and error.

WhatsApp has a two-level adapter hierarchy:

BaseChannelAdapter
└── BaseBSPAdapter # Adds template management
├── MetaDirectAdapter # Meta Graph API
└── GupshupAdapter # Gupshup API

Each tenant’s TenantWAApp has a bsp field (BSPChoices) that determines which adapter to use. The factory function get_bsp_adapter(wa_app) returns the correct adapter.

FeatureDetails
APIMeta Graph API v18+
AuthPermanent access token (META_PERM_TOKEN)
Webhook verificationMETA_APP_SECRET for signature validation
Template syncDirect CRUD via Graph API
MediaUpload to Meta, get handle ID
FeatureDetails
APIGupshup Enterprise API
AuthEmail/password → session token
Template syncVia Gupshup template API
WebhookGupshup-format webhook events

Telegram uses the Telegram Bot API directly — no multi-provider abstraction needed.

ComponentLocation
Bot configurationtelegram/models.pyTelegramBotApp
Message sendingtelegram/services/message_sender.py
Webhook handlingtelegram/views.pyTelegramWebhookView

Each tenant registers their own Telegram bot with an encrypted bot_token. Webhooks are set up per-bot with a unique secret.

SMS uses the same adapter pattern as WhatsApp BSPs:

BaseSMSProvider (sms/providers/base.py)
├── TwilioProvider # Twilio API
├── MSG91Provider # MSG91 API
└── Fast2SMSProvider # Fast2SMS API

Each tenant’s SMSApp specifies a provider and stores encrypted provider_credentials (JSON).

ProviderDLT SupportSender IDWebhook Types
TwilioNofrom_numberInbound + DLR
MSG91Yessender_idDLR
Fast2SMSYessender_idDLR

RCS follows the same pattern with SMS fallback built in:

BaseRCSProvider (rcs/providers/base.py)
├── GoogleRBMProvider # Google RCS Business Messaging
└── MetaRCSProvider # Meta RCS API

Each tenant’s RCSApp can optionally link to an SMSApp via sms_fallback_app FK. When sms_fallback_enabled=True, failed RCS deliveries automatically retry via SMS.

ProviderAuthSuggestionsFallback
Google RBMService account JSONQuick replies, actionsSMS via linked SMSApp
Meta RCSAccess tokenQuick repliesSMS via linked SMSApp

To add a new provider for an existing channel:

  1. Create a new provider class extending the base (e.g., BaseSMSProvider)
  2. Implement all abstract methods (send_text, send_media, etc.)
  3. Add the provider to the choices enum (e.g., SMSProviderChoices)
  4. Register in the app’s AppConfig.ready() if using the channel registry

To add an entirely new channel (e.g., Email, Voice):

  1. Create a new Django app
  2. Implement BaseChannelAdapter
  3. Register via register_channel(platform, factory) in AppConfig.ready()
  4. Add URL patterns, models, serializers, and viewsets
  5. Add to the PlatformChoices enum