# How to Add a Custom Backend Fallback for an Existing Channel in Agent-Reach

> Learn how to add a custom backend fallback for an existing channel in Agent-Reach by updating backends, implementing a probe helper, and wiring it into the check method for resilient service.

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

---

**Adding a custom backend fallback requires updating the channel's `backends` list, implementing a probe helper that returns a health status tuple, and wiring it into the channel's `check()` method so it executes when primary backends fail.**

Agent-Reach abstracts internet platforms as **Channel** classes that orchestrate upstream CLI tools through an ordered fallback system. When you need to integrate a proprietary or internal tool for times when standard options like `twitter-cli` or `OpenCLI` are unavailable, you can extend any existing channel to recognize your custom backend without modifying the core framework.

## Understand the Channel Backend Architecture

Each platform in Agent-Reach is modeled as a class extending the base `Channel` ABC. The channel declares an ordered list of `backends`—strings identifying the CLI tools capable of handling that platform—and probes them sequentially until one reports a healthy status.

### The Channel Base Class

The core abstraction lives in **[`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)**. It defines the `Channel` class and the critical `ordered_backends(config)` method, which returns the backend list respecting user overrides from configuration or environment variables.

```python

# agent_reach/channels/base.py (conceptual)

class Channel(ABC):
    backends = []
    
    def ordered_backends(self, config):
        # Returns backends list with optional user-priority override

        ...
    
    def check(self, config):
        # Iterates through ordered_backends and probes each

        ...

```

### Backend Selection Logic

When `Channel.check()` runs, it iterates over `self.ordered_backends(config)` and probes each candidate. The first backend returning `"ok"` or `"warn"` becomes the `active_backend`. If a backend is not installed, the probe returns `None`, and the loop continues to the next candidate. This mechanism allows seamless fallback to your custom tool when earlier options fail.

## Step-by-Step Implementation

To add a custom fallback backend for an existing channel (for example, extending `TwitterChannel`), follow these three steps.

### Step 1: Declare the Backend in the Channel Class

Append your custom backend identifier to the channel's `backends` list. Position matters: place it **after** existing preferred backends so it only activates when they are unavailable.

```python

# agent_reach/channels/twitter.py

class TwitterChannel(Channel):
    name = "twitter"
    # "mycli" is appended as a fallback candidate

    backends = ["twitter-cli", "OpenCLI", "bird CLI (legacy)", "mycli"]
    tier = 1

```

### Step 2: Implement a Health Check Probe

Create a private helper method that probes your custom CLI. According to the patterns in **[`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py)**, use `probe_command()` to safely execute a health check and categorize the result.

Your helper should return:
- `None` if the tool is not installed (skips this candidate)
- A tuple `(status, message)` where status is `"ok"`, `"warn"`, or `"error"`

```python

# agent_reach/channels/twitter.py

def _check_mycli(self):
    """Probe the custom CLI 'mycli'. Return None if not installed,
    otherwise a (status, message) tuple."""
    from agent_reach.probe import probe_command

    probe = probe_command(
        "mycli", 
        ["--health"], 
        timeout=10, 
        package="mycli"
    )
    
    if probe.status == "missing":
        return None  # Not installed → skip candidate

    
    if probe.status in ("broken", "timeout"):
        return "error", probe.hint  # Installed but unusable

    
    # Healthy command returns exit-code 0

    return "ok", "mycli is ready for Twitter operations"

```

### Step 3: Integrate the Probe into the Check Loop

Modify the channel's `check()` method to include a branch for your new backend. The method iterates through `self.ordered_backends(config)` and dispatches to the appropriate helper based on the backend string.

```python

# Inside TwitterChannel.check()

for backend in self.ordered_backends(config):
    if backend == "twitter-cli":
        result = self._check_twitter_cli()
    elif backend == "OpenCLI":
        result = self._check_opencli()
    elif backend == "bird CLI (legacy)":
        result = self._check_bird()
    elif backend == "mycli":
        result = self._check_mycli()  # ← new branch

    else:
        continue
    
    # Standard handling of result...

    if result:
        status, msg = result
        if status in ("ok", "warn"):
            self.active_backend = backend
            break

```

Now, when `twitter-cli` and `OpenCLI` are missing or broken, Agent-Reach automatically falls back to `mycli`.

## Override Backend Priority via Configuration

You can force your custom backend to the front of the list without code changes by using the configuration override system. The `ordered_backends()` method checks for a key named `<channel>_backend` in the config file or the environment variable `<CHANNEL>_BACKEND`.

Promote your fallback to primary via TOML:

```toml

# config.yaml

twitter_backend = "mycli"

```

Or via environment variable:

```bash
export TWITTER_BACKEND=mycli

```

When set, Agent-Reach moves the specified backend to index zero in the probe order, ensuring it is checked first.

## Summary

- **Channel architecture** in Agent-Reach uses an ordered `backends` list defined in concrete channel classes like `TwitterChannel` in [`agent_reach/channels/twitter.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/twitter.py).
- **The fallback system** probes backends in sequence via `ordered_backends()` (defined in [`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)) until one reports `"ok"` or `"warn"`.
- **Adding a custom backend** requires: (1) appending the name to `backends`, (2) implementing a probe helper using `probe_command()` from [`agent_reach/probe.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/probe.py), and (3) wiring the helper into the `check()` method's dispatch loop.
- **Configuration overrides** via `{channel}_backend` keys or `{CHANNEL}_BACKEND` environment variables let users force specific backends without modifying source code.

## Frequently Asked Questions

### What file contains the base Channel class in Agent-Reach?

The abstract base class is located in **[`agent_reach/channels/base.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/channels/base.py)**. This file defines the `ordered_backends()` method and the default `check()` implementation that concrete channels inherit.

### How does Agent-Reach determine if a backend is healthy?

The `check()` method relies on probe helpers that return a status tuple. Status values of `"ok"` or `"warn"` indicate a healthy backend, while `"error"`, `"broken"`, or `"timeout"` signal failure. If the CLI is not installed, the probe returns `None`, causing the loop to skip to the next candidate.

### Can I force a specific backend to be used regardless of the ordered list?

Yes. Set the configuration key `<channel>_backend` in your config file (for example, `twitter_backend = "mycli"`) or export the environment variable `<CHANNEL>_BACKEND` (for example, `TWITTER_BACKEND=mycli`). The `ordered_backends()` method automatically promotes the specified backend to the front of the probe order.

### What should my probe helper return if the custom CLI is not installed?

Return `None` when the tool is missing. This signals to the `check()` loop that the backend is unavailable, allowing Agent-Reach to fall through to the next candidate in the `backends` list rather than treating it as a fatal error.