# How to Add a Custom Backend for an Unsupported Platform in Agent Reach

> Learn how to add a custom backend for an unsupported platform in Agent Reach. Extend the Channel class and register your new backend to support new platforms.

- Repository: [Pnant/Agent-Reach](https://github.com/Panniantong/Agent-Reach)
- Tags: how-to-guide
- Published: 2026-06-24

---

**To add a custom backend for an unsupported platform in Agent Reach, create a new channel class inheriting from `Channel` in `agent_reach/channels/`, implement the `can_handle` and `check` methods, and register it in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py).**

Agent Reach is an extensible automation framework that treats every internet platform as a **channel**. When you need to integrate an unsupported platform, you extend the channel architecture in the Panniantong/Agent-Reach repository. This guide walks through the exact steps to add a custom backend for an unsupported platform in Agent Reach, referencing the actual source files and implementation patterns used by existing channels like Twitter and YouTube.

## Understand the Channel Architecture

The foundation of every platform integration is the abstract `Channel` base class defined in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py). Each channel represents a specific platform (e.g., Twitter, YouTube) and manages one or more **backends**—upstream runtimes like `yt-dlp`, `twitter-cli`, or OpenCLI that perform the actual work.

The `Channel` contract requires these specific attributes and methods:

- **`name`** (str): Short identifier used in configuration keys (`<channel>_backend`).
- **`description`** (str): Human-readable description shown by the `doctor` command.
- **`backends`** (List[str]): Ordered list of candidate backends; the first usable one becomes `active_backend`.
- **`tier`** (int): `0` for zero-config, `1` for free API key, `2` for complex setup.
- **`can_handle(url)`**: Returns `True` if the URL belongs to this platform.
- **`check(config)`**: Probes the backends, sets `self.active_backend`, and returns a `(status, message)` tuple.

The base class provides the generic `ordered_backends` implementation, so you normally do not need to override backend selection logic.

## Create a New Channel Module

Create a file named `<platform>.py` under `agent_reach/channels/`. Use existing channels such as [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py) or [`agent_reach/channels/youtube.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/youtube.py) as templates. Below is a minimal example for a fictional **"ExampleSocial"** platform that reuses the shared OpenCLI backend.

```python

# agent_reach/channels/example_social.py

# -*- coding: utf-8 -*-

"""ExampleSocial – a new platform that reuses the OpenCLI backend."""

from .base import Channel
from agent_reach.backends import opencli_status

class ExampleSocialChannel(Channel):
    name = "examplesocial"
    description = "ExampleSocial posts and comments"
    # OpenCLI can drive this platform; list dedicated CLIs here if needed

    backends = ["OpenCLI"]
    tier = 0  # No API key required

    def can_handle(self, url: str) -> bool:
        from urllib.parse import urlparse
        host = urlparse(url).netloc.lower()
        return "examplesocial.com" in host

    def check(self, config=None):
        """Probe OpenCLI; delegate to the shared status helper."""
        st = opencli_status()
        if not st.installed:
            return "warn", (
                "OpenCLI 未安装。安装方式：\n"
                "  npm install -g @jackwener/opencli"
            )
        if st.broken:
            return "error", st.hint
        if st.ready:
            self.active_backend = "OpenCLI"
            return "ok", "OpenCLI 可用（复用浏览器登录态）"
        return "warn", st.hint

```

**Key implementation details:**

- The channel `name` becomes the configuration key `examplesocial_backend`.
- The `backends` list tells the framework which runtimes to probe; the probing logic for OpenCLI resides in [`agent_reach/backends/opencli.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/backends/opencli.py).
- The `check` method mirrors the pattern in [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py)—it calls `opencli_status()`, assigns `self.active_backend`, and returns a status tuple.

### Implementing a Dedicated Backend

If your platform requires a specific CLI tool (e.g., `example-cli`) instead of OpenCLI, add it to the `backends` list and implement a private probing method similar to `_check_twitter_cli` in [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py). This method should verify the binary exists and return availability status.

## Register the Channel in the Global Registry

After creating the channel module, you must expose it to the framework by updating the global registry in [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py).

Import your new class and append an instance to `ALL_CHANNELS`:

```python

# agent_reach/channels/__init__.py

...
from .example_social import ExampleSocialChannel
...
ALL_CHANNELS: List[Channel] = [
    GitHubChannel(),
    TwitterChannel(),
    YouTubeChannel(),
    RedditChannel(),
    BilibiliChannel(),
    XiaoHongShuChannel(),
    LinkedInChannel(),
    XiaoyuzhouChannel(),
    V2EXChannel(),
    XueqiuChannel(),
    RSSChannel(),
    ExaSearchChannel(),
    WebChannel(),
    ExampleSocialChannel(),   # <-- newly added

]

```

This registration makes the channel visible to the CLI `doctor` command and the core routing logic.

## Implement Platform-Specific Operations

If the platform supports reading posts, searching, or transcribing media, implement the corresponding optional methods. For example, a `read` method might invoke an upstream API wrapper. Follow the lazy import pattern used in `YouTubeChannel.transcribe` to avoid loading heavy dependencies when the feature is unused:

```python
def transcribe(self, url: str, config=None):
    from agent_reach.utils.media import download_audio
    # Implementation here

```

## Test Your Custom Backend

Validate your implementation by running the project's test suite:

```bash
pytest tests/ -v

```

Create unit tests in `tests/test_<platform>_channel.py` following the structure of [`tests/test_twitter_channel.py`](https://github.com/Panniantong/Agent-Reach/blob/main/tests/test_twitter_channel.py). Verify that:

- `can_handle()` correctly identifies platform URLs.
- `check()` sets `active_backend` to the expected backend under different probe outcomes.
- The channel appears in the `get_all_channels()` registry.

## Summary

- Create a new channel class inheriting from `Channel` in `agent_reach/channels/` to define the platform contract.
- Implement `can_handle()` for URL identification and `check()` for backend probing according to the interface in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py).
- Reuse existing backends like OpenCLI via [`agent_reach/backends/opencli.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/backends/opencli.py) or implement dedicated probing logic similar to [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py).
- Register the channel instance in `ALL_CHANNELS` inside [`agent_reach/channels/__init__.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/__init__.py) to activate it.
- Write unit tests following the pattern in [`tests/test_twitter_channel.py`](https://github.com/Panniantong/Agent-Reach/blob/main/tests/test_twitter_channel.py) to ensure stability.

## Frequently Asked Questions

### What is the difference between a channel and a backend in Agent Reach?

A **channel** is the abstract representation of a platform (e.g., Twitter, YouTube) that inherits from the `Channel` base class in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py). A **backend** is the actual runtime tool or CLI (e.g., `twitter-cli`, `yt-dlp`, OpenCLI) that executes commands for that platform. The channel's `check()` method probes the backends list to determine which runtime is available and sets `active_backend` accordingly.

### Can I use multiple backends for a single platform?

Yes. The `backends` attribute accepts an ordered list of strings. Agent Reach probes them sequentially, and the first usable backend becomes the `active_backend`. For example, the Twitter channel in [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py) can fall back from a dedicated CLI to OpenCLI if the primary tool is unavailable.

### How do I handle authentication for my custom backend?

Authentication is typically handled by the backend itself (e.g., OpenCLI inherits browser login states). If your backend requires API keys, set `tier = 1` or `tier = 2` in your channel class and read the configuration in the `check()` method. Store sensitive credentials in Agent Reach's config system rather than hardcoding them in `agent_reach/channels/<platform>.py`.

### Where should I place unit tests for my new channel?

Create a test file in the `tests/` directory following the naming convention `test_<platform>_channel.py`. Mirror the test patterns found in [`tests/test_twitter_channel.py`](https://github.com/Panniantong/Agent-Reach/blob/main/tests/test_twitter_channel.py), which verify that `can_handle()` correctly identifies URLs and that `check()` sets the `active_backend` attribute under different probe outcomes.