---
summary: "Routing rules per channel (WhatsApp, Telegram, Discord, Slack) and shared context"
read_when:
  - Changing channel routing or inbox behavior
title: "Channel Routing"
---

# Channels & routing

SKYKOI routes replies **back to the channel where a message came from**. The
model does not choose a channel; routing is deterministic and controlled by the
host configuration.

## Key terms

- **Channel**: `whatsapp`, `telegram`, `discord`, `slack`, `signal`, `imessage`, `webchat`.
- **AccountId**: per‑channel account instance (when supported).
- **KoiId**: an isolated workspace + session store (“brain”).
- **SessionKey**: the bucket key used to store context and control concurrency.

## Session key shapes (examples)

Direct messages collapse to the koi’s **main** session:

- `koi:<koiId>:<mainKey>` (default: `koi:main:main`)

Groups and channels remain isolated per channel:

- Groups: `koi:<koiId>:<channel>:group:<id>`
- Channels/rooms: `koi:<koiId>:<channel>:channel:<id>`

Threads:

- Slack/Discord threads append `:thread:<threadId>` to the base key.
- Telegram forum topics embed `:topic:<topicId>` in the group key.

Examples:

- `koi:main:telegram:group:-1001234567890:topic:42`
- `koi:main:discord:channel:123456:thread:987654`

## Routing rules (how an koi is chosen)

Routing picks **one koi** for each inbound message:

1. **Exact peer match** (`bindings` with `peer.kind` + `peer.id`).
2. **Guild match** (Discord) via `guildId`.
3. **Team match** (Slack) via `teamId`.
4. **Account match** (`accountId` on the channel).
5. **Channel match** (any account on that channel).
6. **Default koi** (`kois.list[].default`, else first list entry, fallback to `main`).

The matched koi determines which workspace and session store are used.

## Broadcast groups (run multiple kois)

Broadcast groups let you run **multiple kois** for the same peer **when SKYKOI would normally reply** (for example: in WhatsApp groups, after mention/activation gating).

Config:

```json5
{
  broadcast: {
    strategy: "parallel",
    "120363403215116621@g.us": ["alfred", "baerbel"],
    "+15555550123": ["support", "logger"],
  },
}
```

See: [Broadcast Groups](/channels/broadcast-groups).

## Config overview

- `kois.list`: named koi definitions (workspace, model, etc.).
- `bindings`: map inbound channels/accounts/peers to kois.

Example:

```json5
{
  kois: {
    list: [{ id: "support", name: "Support", workspace: "~/.SKYKOI/workspace-support" }],
  },
  bindings: [
    { match: { channel: "slack", teamId: "T123" }, koiId: "support" },
    { match: { channel: "telegram", peer: { kind: "group", id: "-100123" } }, koiId: "support" },
  ],
}
```

## Session storage

Session stores live under the state directory (default `~/.SKYKOI`):

- `~/.SKYKOI/koi/<koiId>/sessions/sessions.json`
- JSONL transcripts live alongside the store

You can override the store path via `session.store` and `{koiId}` templating.

## WebChat behavior

WebChat attaches to the **selected koi** and defaults to the koi’s main
session. Because of this, WebChat lets you see cross‑channel context for that
koi in one place.

## Reply context

Inbound replies include:

- `ReplyToId`, `ReplyToBody`, and `ReplyToSender` when available.
- Quoted context is appended to `Body` as a `[Replying to ...]` block.

This is consistent across channels.
