---
title: 'Channels'
description: 'Let people talk to agents from Slack, Telegram and Discord, and have agents answer GitHub and any webhook'
---
[View as Markdown](https://camelai.com/docs/camelrun/channels.md) · [Docs index for coding agents](https://run.camelai.com/llms.txt)

A channel lets people talk to agents from a messaging service (Telegram, Slack
or Discord), or has agents answer activity on GitHub. A `webhook` channel
lets any service that sends webhooks (Sentry, Linear, Stripe, your own) start
agents. For Discord you can also skip making a bot: [add the Camel Discord
bot](#add-the-camel-discord-bot) to your server from the console. Manage
channels with `/v1/channels` or the console's Channels page:

```bash
curl https://run.camelai.com/v1/channels \
  -H "Authorization: Bearer $CAMELAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"type": "telegram", "credentials": {"botToken": "<from @BotFather>"},
       "definition": "def_...", "access": {"allow": ["@ada", "123456789"]}}'
```

Each conversation's agent is made from the channel's `definition`. A channel
created without one gets an empty definition of its own.

| Service           | Credentials                                      | Messages arrive by                          | One conversation (one agent) is                               |
| ----------------- | ------------------------------------------------ | ------------------------------------------- | ------------------------------------------------------------- |
| `telegram`        | `botToken`                                       | Webhook, registered for you                 | A chat                                                        |
| `slack`           | `botToken` (`xoxb-...`), `signingSecret`         | Webhook, pasted into the app                | A thread started by an @mention, or a DM                      |
| `discord`         | `botToken`                                       | The Gateway, which camelRun keeps connected | A DM, or a channel or thread where the bot is @mentioned      |
| Camel Discord bot | None: one Discord authorization from the console | Camel's shared Gateway                      | An allowed server channel or thread where Camel is @mentioned |
| `github`          | `appId`, `privateKey`, `webhookSecret`           | Webhook, pasted into the GitHub App         | A pull request or issue                                       |
| `webhook`         | `secret`, optional `replyUrl`                    | Webhook, pasted into the sending service    | Whatever the `key` template names                             |

Creating a channel checks its credentials with the service. Credentials are
stored encrypted, and the API returns only masked values.

## Add the Camel Discord bot

Camel is camelRun's own Discord bot. Adding it to a server takes one step and
no Discord application or bot token:

### Add Camel

On the console's Channels page, select **Add Camel to Discord**, or open
[run.camelai.com/console/?start=discord](https://run.camelai.com/console/?start=discord),
which signs you in first if needed. In Discord, choose a server you own or
have Manage Server in, and authorize Camel. That adds the bot and connects
the server to your camelRun account. One server belongs to one camelRun
account at a time.

### Configure the server

The server's settings open in the console. Choose a
[definition](/docs/camelrun/definitions), or create one with the prompt, model
and tools you want. Select the allowed channels, then who may talk to
Camel: a list of Discord user IDs, or every member. Saving turns Camel on
in that server; until then it doesn't answer there.

### Mention Camel

In an allowed channel, send a message that @mentions Camel. Pick the entry
with the App badge in Discord's autocomplete: a role with the same name
doesn't count.

Each allowed channel or thread has its own persistent agent, with history
shared by the people in that conversation. Every new turn needs a direct
mention of Camel, and direct messages are ignored. Camel asks for View
Channel, Send Messages, Send Messages in Threads, Attach Files and Read Message
History, never Administrator, and no privileged intents. It has no slash
commands.

> **Warning:**
>
> Members who can talk to Camel use the definition's tools with your camelRun
> account's credentials, and their usage is billed to your account. Choose the
> tools and the allowed members accordingly.

* **Limits.** Each member has a rate limit (5 turns a minute by default, up to
  100\), and the server has a daily turn limit (100 by default, up to 10,000).
  Turns you start through the API count against it. There is no dollar budget
  per server yet; your account's credit and [spend limits](/docs/camelrun/models-and-keys)
  apply. An account connects at most 10 servers, or 1 while on free credit,
  which also caps each server at 500 turns a day.
* **No schedules.** A definition with the `schedule` builtin can't serve a
  server, since any member could have the agent wake itself outside those
  limits.
* **Changing it.** **Configure** edits the definition, tools included, and can
  apply it to existing conversations; the console reports which were updated,
  queued or failed.
* **Pausing and removing.** Pause stops new replies and pending sends.
  Disconnect keeps the conversation history and stops replies, but leaves Camel
  in the server. Removing Camel from the server disconnects it, and adding it
  back needs reactivation in the console. A saved configuration isn't proof a
  reply was delivered: test with a new mention in an allowed channel.
* **Moving a server.** A server's administrators decide who runs Camel there.
  To move it to another camelRun account, remove Camel from the server, then
  add it again from that account. The previous account keeps its past
  conversations but sees no new ones.

The Camel bot is set up from the console only, not with `POST /v1/channels`.
For your own bot name and avatar, or to answer DMs, use the `discord` channel
with your own bot token below.

## Set up each service

### Telegram

Create a bot with @BotFather and pass its token. camelRun registers the
bot's webhook with a random secret that each delivery must echo, and removes
it when you delete the channel. `/start` gets the channel's `greeting`
without a model call.

### Slack

Create an app with the bot scopes `app_mentions:read`, `chat:write`,
`im:history`, `channels:history` (and `groups:history` for private
channels), `files:read` and `files:write`, and install it. Paste the
channel's `webhookUrl` into Event Subscriptions and subscribe to
`app_mention`, `message.im` and `message.channels`. A mention starts a
thread and replies go there; later messages in that thread reach its agent
without a mention.

### Discord

Create an application, add a bot, and invite it with View Channel, Send
Messages, Send Messages in Threads, Attach Files and Read Message History.
The bot needs no privileged intents: it answers DMs and messages that
mention the bot user. A mention of a role with the bot's name doesn't
count: in autocomplete, pick the entry with the App badge. Replies never
ping anyone. Once the token is saved, the console shows an invite link and
a test mention.

### GitHub

Create a GitHub App with the repository permissions Pull requests (read and
write), Issues (read and write), Contents (read) and Metadata (read).
Subscribe it to Pull request, Issue comment and Pull request review comment
(and Issues, for `issues.opened`), generate a private key, set a webhook
secret, and install it. Create the channel with the App ID, the private key
and the secret, then paste the channel's `webhookUrl` into the app.

Each pull request or issue gets its own agent. A pull request's diff is
attached as `pr-<n>.diff`, and the reply is a comment. `settings` choose
what starts a turn: `events`, `repos` (`owner/repo` or `owner/*`),
`ignoreDrafts` (default `true`), `reply` (`comment` or `none`), `authors`
(`allowlist` or `members`) and `debounceSeconds` (default 30).

> **Warning:**
>
> On a public repository, anyone can open pull requests and comments.
> Treat them as untrusted input, keep `access.public` off, and give the
> agent's tools no more than the conversation needs.

> The runtime also supports email channels, an address that agents answer
> mail at. They require a runtime configured to receive mail, and aren't
> enabled on run.camelai.com.

## What all channels share

* Each external conversation gets its own agent, made on first contact from the
  channel's definition, at its revision then.
* Senders must be on the allowlist (`access.allow`) unless the channel sets
  `access.public`. Each sender is rate limited (`limits.perSenderPerMinute`,
  default 10), and the channel has a daily turn cap (`limits.turnsPerDay`,
  default 1,000).
* Each message carries its sender as `from`, and tool calls carry a runtime-set
  `origin` (`{channel, conversationId, sender}`) that tools can authorize
  against.
* Attachments are streamed into the agent's workspace at
  `uploads/<requestId>/<name>`: up to 10 files a message, 25 MiB each (20 MB
  on Telegram) and 100 MiB in all. A file too large or that fails to download
  is left out, and the prompt says so.
* The turn's final answer is sent back when the turn ends, split to the
  service's message limit (4,096 characters on Telegram, 4,000 on Slack, 2,000
  on Discord), with a typing indicator meanwhile where the service has one.
  Channel agents also get a `send_message` tool for updates mid-turn, which
  can send files too.
* Files the agent presents follow the reply: up to 10 MB as photos and 50 MB
  as documents on Telegram, 100 MiB on Slack, and 10 MiB on Discord. A larger
  file is sent as a [signed link](/docs/camelrun/files) that works for 24 hours.
* Messages are recorded durably before they are acknowledged, duplicates are
  dropped, and replies go through a durable outbox, so each part is sent once.
* Questions and approvals appear as text, and the next fitting message from an
  allowed person answers them. See [Human input](/docs/camelrun/human-input).

## Generic webhooks

A `webhook` channel turns each delivery from any service into a prompt. Give it
the secret the service signs with, then point the service at the channel's
`webhookUrl`:

```bash
curl https://run.camelai.com/v1/channels \
  -H "Authorization: Bearer $CAMELAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"type": "webhook", "credentials": {"secret": "<the signing secret>"}, "definition": "def_...",
       "settings": {"signature": {"type": "hmac-sha256", "header": "Sentry-Hook-Signature"},
                    "key": "sentry-{{data.issue.id}}",
                    "filter": [{"path": "action", "in": ["created"]}],
                    "prompt": "Sentry issue {{data.issue.title}} ({{data.issue.web_url}}) was created. Triage it."}}'
```

| Setting              |                                                                                                                                                                                                                                                                                         |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `signature`          | How a delivery proves itself: `{"type": "standard"}` (Standard Webhooks, the default), `{"type": "hmac-sha256", "header", "prefix"?, "encoding"?}` (GitHub, Sentry, Linear, Shopify and most others), or `{"type": "token", "header"}`. Unsigned or wrongly signed deliveries get a 401 |
| `key`                | Picks the conversation, and so the agent. Deliveries that render to the same key go to the same agent. Without a key, the channel has one agent                                                                                                                                         |
| `prompt`             | What the agent is told. By default, the payload itself. Either way the whole payload is attached as `payload.json`                                                                                                                                                                      |
| `sender`             | Who a delivery is from, for `from` and rate limits, such as `{{actor.email}}`                                                                                                                                                                                                           |
| `filter`             | Conditions that must all hold (`path` with `in`, `equals` or `exists`), or the delivery is acknowledged and ignored                                                                                                                                                                     |
| `idPath`, `idHeader` | Where a delivery's id is, so retries are dropped                                                                                                                                                                                                                                        |

Templates are `{{path}}`: a dotted path into the JSON payload
(`data.items.0.id`), or `{{headers.<name>}}` for a request header. Bodies must
be JSON, up to 1 MiB.

There's no conversation to answer, so the agent acts through its tools and the
turn's end reaches you as a [`run.completed`](/docs/camelrun/webhooks) event. To get
the reply too, set `credentials.replyUrl`: each reply is POSTed there as
`{"type": "message", "conversationId", "text"}`, signed per Standard Webhooks
with the channel's secret.

A webhook channel is public (`access.public: true`) and allows 60 deliveries a
minute per sender: the signature is what keeps others out. It still has the
daily turn cap.
