Skip to content

Configuration: channels ​

The channels section of agentx.json turns on the apps your agents talk through: Telegram, WhatsApp, GitLab, GitHub, phone app notifications (push), ntfy and browser calls (WebRTC). This page lists every field. For the other sections, see the Configuration reference; for what each channel does, see Channels.

Every channel is off until you set its enabled to true. Put secrets in .env and point to them with ${VARIABLE} references.

Telegram ​

Set up with agentx connect telegram (connect Telegram). One bot per account; each account is bound to one agent.

KeyTypeDefaultWhat it does
channels.telegram.enabledbooleanfalseTurns the Telegram channel on.
channels.telegram.accountsmap of accounts{}One entry per bot, keyed by an account name you choose.
tokenstringrequiredThe bot token from BotFather. Use a ${…} reference.
agentBindingstringrequiredThe agent that answers this bot's messages.
allowFromlist of strings—Who may message this bot: user ids, chat ids (negative numbers) or @username. Replaces channels.telegram.policy.allowFrom for this account.
pollInboundbooleantrueWhen false, the bot only sends (notifications, replies) and does not read new messages. Use it when the bound agent lives on another machine, so two daemons don't read the same bot.
channels.telegram.policy.dm"pair" | "block""pair"Direct-message policy. Written by setup; who may write is decided by allowFrom.
channels.telegram.policy.group"mention-required" | "all""mention-required"In groups, mention-required answers only messages that @-mention an agent.
channels.telegram.policy.allowFromlist of strings—Allowlist used by every account without its own allowFrom. If neither is set, all messages are ignored.
json
"telegram": {
  "enabled": true,
  "accounts": {
    "helper": { "token": "${TELEGRAM_BOT_TOKEN}", "agentBinding": "helper", "allowFrom": ["@your_username"] }
  }
}

WhatsApp ​

Set up with agentx connect whatsapp, which pairs a phone by QR code.

KeyTypeDefaultWhat it does
channels.whatsapp.enabledbooleanfalseTurns the WhatsApp channel on.
channels.whatsapp.sessionDirstring".agentx/whatsapp-sessions"Folder that keeps the paired session. Delete it to pair again.
channels.whatsapp.defaultAgentstring—Agent that answers when no route matches.
channels.whatsapp.allowFromlist of strings—Phone numbers allowed to message the agent (partial match). Add your own number to talk to the agent from your own chat.
channels.whatsapp.routeslist[]Send a contact or group to a specific agent. The first match wins.
contactstring—Phone number to match (with or without +).
groupstring—Group name or id to match (case-insensitive, partial).
agentstringrequiredAgent that answers matching messages.

Ingest ​

Ingest reads contact and group details (and, if you choose, recent messages) into the wiki. Nothing is read unless it is enabled and on an allow list.

KeyTypeDefaultWhat it does
channels.whatsapp.ingest.enabledbooleanfalseTurns ingest on.
channels.whatsapp.ingest.mode"metadata-only" | "messages""metadata-only"messages also reads the latest messages of each allowed chat.
channels.whatsapp.ingest.allowContactslist of strings[]Phone numbers or ids to read (partial match).
channels.whatsapp.ingest.allowGroupslist of strings[]Groups to read.
channels.whatsapp.ingest.denyContactslist of strings[]Contacts never read, even if allowed.
channels.whatsapp.ingest.denyGroupslist of strings[]Groups never read, even if allowed.
channels.whatsapp.ingest.messageCapnumber (1–500)50Most messages read per chat in messages mode.
channels.whatsapp.ingest.historyDaysnumber (1–365)30Oldest message age, in days, that is read.
channels.whatsapp.ingest.contactRefreshDaysnumber (1–90)7Days before an unchanged contact is written again.
channels.whatsapp.ingest.throttle.minMsBetweenCallsnumber (≥100)1500Pause between reads, in milliseconds, so the account isn't flagged.
channels.whatsapp.ingest.throttle.maxCallsPerMinutenumber (≥1)20Most reads per minute.
channels.whatsapp.ingest.throttle.maxChatsPerSweepnumber (≥1)25Most chats read in one pass.
channels.whatsapp.ingest.retentionDaysnumber (≥0)0Delete stored raw entries older than this many days. 0 keeps them.

WhatsApp triage ​

Watched WhatsApp chats, read by an agent that sorts each message and drafts replies for your approval. Messages come from wacli sync --webhook to POST /webhook/wacli, not from the WhatsApp channel above; the two are independent. Set up with Watch a WhatsApp chat, the WhatsApp triage block in Settings › Webhooks, or agentx whatsapp triage.

KeyTypeDefaultWhat it does
whatsappTriage.enabledbooleanfalseTurns triage on. Off, the webhook answers 404.
whatsappTriage.secretEnvstring"WACLI_WEBHOOK_SECRET"Environment variable holding the secret given to wacli sync --webhook-secret. Unset, every message is refused (503).
whatsappTriage.batchSecondsnumber (0–600)20Messages from one chat within this many seconds go to the agent as one task.
whatsappTriage.timezonestringthis computer'sTime zone for quiet hours, for example Europe/Paris.
whatsappTriage.allowAutoAckbooleanfalseSecond switch for rules with autoAck. Both must be on.
whatsappTriage.describeMediabooleantrueDownload pictures for the agent to look at, and transcribe voice notes when speech to text is set up (voice.stt).
whatsappTriage.wacli.binstring"wacli"The wacli program, or its full path.
whatsappTriage.wacli.accountstring—wacli --account name, when several accounts are paired.
whatsappTriage.wacli.storestring—wacli --store folder.
whatsappTriage.ruleslist[]Watch rules. The first enabled match wins. A message no rule matches is dropped and not stored.
idstringrequiredShort name: lowercase letters, digits, - and _.
enabledbooleantrueTurns the rule on or off.
chatslist of strings[]Chats to watch: a contact's JID (15550001111@s.whatsapp.net), a group's JID (…@g.us), or a phone number.
senderslist of strings[]Only messages from these people, in any chat the rule covers. A rule needs at least one chat or sender.
agentstringrequiredAgent on this computer that triages the messages.
promptstring (≤4000)—Extra instructions: who the contact is, where issues go, how to reply.
quietHours.start, quietHours.end"HH:MM"—No notifications in this window. It may cross midnight. Triage and drafts still happen.
autoAckbooleanfalseSend the agent's reply to messages it marks ack without asking. Needs allowAutoAck too.

Every other reply waits in Approvals as a whatsapp: item and is sent only when you say yes.

json
"whatsappTriage": {
  "enabled": true,
  "rules": [
    {
      "id": "test-contact",
      "agent": "helper",
      "chats": ["+1 555 000 1111"],
      "prompt": "Open bug reports in the tracker project example/app.",
      "quietHours": { "start": "22:00", "end": "07:00" }
    }
  ]
}

GitLab ​

AgentX receives GitLab webhooks and answers @-mentions in issues and merge requests (watch GitLab).

KeyTypeDefaultWhat it does
channels.gitlab.enabledbooleanfalseTurns the GitLab channel on.
channels.gitlab.webhookPortnumber18810Separate port that receives GitLab webhooks.
channels.gitlab.webhookSecretstring—Secret GitLab sends in its X-Gitlab-Token header; requests without it are refused.
channels.gitlab.hoststring"https://gitlab.com"Address of your GitLab server.
channels.gitlab.tokenstring—Access token used to post comments.
channels.gitlab.autoReplyLegacybooleantrueWhen true, the agent's answer is posted as a comment automatically. When false, the agent must post its reply itself. An agent's gitlabAutoReply overrides this.
channels.gitlab.routeslist[]Which agent handles which project.
projectstringrequiredProject path, such as group/project, or * for all other projects.
agentstringrequiredAgent for that project.
channels.gitlab.agentMappingslist[]Links GitLab users to agents, so a mention of that user reaches the agent.
agentIdstringrequiredThe agent.
gitlabUsernameslist of strings[]GitLab usernames that stand for this agent.
keywordslist of strings[]Accepted for older configs. Routing uses @-mentions only.
tokenstring—This agent's own GitLab token, so it comments as itself.
nodestring—Mesh node the agent lives on, when it runs on another machine.
json
"gitlab": {
  "enabled": true,
  "host": "https://gitlab.example.com",
  "token": "${GITLAB_TOKEN}",
  "webhookSecret": "${GITLAB_WEBHOOK_SECRET}",
  "routes": [{ "project": "*", "agent": "helper" }]
}

GitHub ​

KeyTypeDefaultWhat it does
channels.github.enabledbooleanfalseTurns the GitHub channel on.
channels.github.autoReplyLegacybooleantrueSame as the GitLab setting: post the answer automatically, or let the agent post it.
channels.github.tokenstring—Personal access token used to post comments.
channels.github.tokenFilestring—File holding the token (first line is read at start).
channels.github.appIdnumber—GitHub App id, when you use a GitHub App instead of a token.
channels.github.clientIdstring—GitHub App client id (preferred over appId).
channels.github.privateKeyFilestring—Path to the GitHub App private key file.
channels.github.webhookSecretstring—Secret used to check the X-Hub-Signature-256 header of each webhook.
channels.github.routeslist[]Which agent handles which repository.
repostringrequiredowner/repo, or * for all other repositories.
agentstringrequiredAgent for that repository.
channels.github.agentMappingslist[]Links GitHub users to agents.
agentIdstringrequiredThe agent.
githubUsernameslist of strings[]GitHub usernames that stand for this agent.
tokenstring—This agent's own token.
tokenFilestring—File holding this agent's token.
nodestring—Mesh node the agent lives on.

push ​

Sends notifications to the AgentX phone app. It only sends; it does not receive messages. One computer hosts the phone app and sends; every other computer sets relayTo and passes its notifications to that one over the mesh.

FieldTypeDefaultWhat it does
channels.push.enabledbooleanfalseTurns phone app notifications on.
channels.push.subjectstring—Contact the push services can reach you at: mailto:you@example.com or an https:// address. Required on the computer that hosts the phone app.
channels.push.keysFilestring".agentx/push-keys.json"Where agentx app push-keys saves the key pair, relative to the folder AgentX runs in (the one that holds agentx.json).
channels.push.relayTostring—Mesh peer that hosts the phone app. Set it on every other computer; leave it out on the host.
channels.push.ttlSecondsnumber86400How long the push service keeps trying a phone that is offline.
channels.push.keepRecentnumber50How many recent notifications the app's Alerts tab keeps.
channels.push.allowedHostslist of strings["fcm.googleapis.com", "push.services.mozilla.com", "push.apple.com", "notify.windows.com"]Push services a phone may turn notifications on with. Each entry also covers its subdomains. The defaults cover Chrome and Android, Firefox, Safari and iPhone, and Edge. AgentX refuses any other address, because it sends to whatever address the phone gives.

A message's chat ID picks the phones: default sends to every phone that turned notifications on, and a device id from agentx app devices (it starts with tok_) sends to that phone only.

ntfy ​

ntfy sends push notifications to your phone. It only sends; it does not receive messages.

KeyTypeDefaultWhat it does
channels.ntfy.enabledbooleanfalseTurns ntfy on.
channels.ntfy.serverstring"https://ntfy.sh"ntfy server; change it if you host your own.
channels.ntfy.topicstring—Default topic. Anyone who knows it can read it, so treat it as a secret.
channels.ntfy.tokenstring—Access token for protected topics.
channels.ntfy.defaultPrioritynumber (1–5)3Priority when a message sets none.
channels.ntfy.defaultTitlestring—Title when a message sets none (otherwise AgentX).

Browser calls (WebRTC) ​

WebRTC lets people call an agent from a browser.

KeyTypeDefaultWhat it does
channels.webrtc.enabledbooleanfalseTurns calls on.
channels.webrtc.stunServerslist of strings["stun:stun.l.google.com:19302"]Servers that help two devices find each other across networks.
channels.webrtc.turnServerslist[]Relay servers, needed when both sides are behind strict firewalls.
urlsstringrequiredRelay address.
usernamestring—Relay user name.
credentialstring—Relay password.
channels.webrtc.allowedCallerslist of strings[]Mesh peers allowed to start a call. Empty allows all peers.
channels.webrtc.ringNotifylist[]Where to send "someone is calling" messages. Empty sends none.
channelstringrequiredChannel to notify, such as telegram.
chatIdstringrequiredChat to notify.
accountIdstring—Which account of that channel sends it.
channels.webrtc.callUrlBasestring—Address used in the join link. Defaults to http:// plus node.bind; set it when people reach the daemon by another name.
channels.webrtc.camera.widthnumber (160–3840)1280Picture width the phone app asks for when it shares its camera. The phone uses the nearest size its camera supports.
channels.webrtc.camera.heightnumber (120–2160)720Picture height the phone asks for.
channels.webrtc.camera.frameRatenumber (1–60)15Frames per second the phone asks for.
channels.webrtc.camera.maxSecondsnumber (10–7200)600The phone stops sharing its camera after this many seconds.
channels.webrtc.camera.bot.frameIntervalSecondsnumber (0–3600)0When an agent watches the camera, how often it gets a picture by itself, in seconds. 0 means only when you tap Look now or the agent asks for one. Each picture the agent gets by itself is a turn of the agent, so keep this high or at 0.
channels.webrtc.camera.bot.maxSessionMinutesnumber (1–240)10An agent's watch ends after this many minutes, whatever the phone does.
channels.webrtc.camera.bot.maxFrameEdgenumber (160–3840)1024Pictures are shrunk so their longer side is at most this many pixels before the agent sees them.
channels.webrtc.camera.bot.keepFramesbooleanfalseKeep the picture files in the agent's workspace (.agentx/camera/) after the share ends. Off: every picture is deleted when the share ends.

Call bot ​

The call bot joins a call, writes down what is said and posts it to a chat. It does not speak.

KeyTypeDefaultWhat it does
channels.webrtc.bot.enabledbooleanfalseTurns the call bot on.
channels.webrtc.bot.defaultAgentIdstring—Agent the transcript is credited to.
channels.webrtc.bot.whisperBackend"auto" | "mlx" | "openai""auto"Speech-to-text engine. auto tries local MLX Whisper, then OpenAI.
channels.webrtc.bot.whisperModelstring—Speech-to-text model name.
channels.webrtc.bot.whisperLanguagestring"auto"Spoken language, or auto to detect it.
channels.webrtc.bot.mlxBinarystring—Full path to mlx_whisper if the daemon can't find it.
channels.webrtc.bot.transcriptChannel.channelstringrequiredChannel that receives the transcript.
channels.webrtc.bot.transcriptChannel.chatIdstringrequiredChat that receives the transcript.
channels.webrtc.bot.transcriptChannel.accountIdstring—Which account of that channel sends it.
channels.webrtc.bot.maxCallMinutesnumber (1–240)30The bot leaves after this many minutes.

Check it worked ​

  1. Terminal: in the folder with agentx.json, run agentx config check. It prints ✓ Config valid.
  2. Terminal: run agentx config get channels.telegram.enabled (or the field you changed). It prints the new value.
  3. Terminal: restart the daemon, then send the agent a test message on that channel. It answers.

If something is wrong ​

  • config check names a field: fix that field. A required field in the tables above is missing.
  • Telegram ignores every message: neither the account's allowFrom nor channels.telegram.policy.allowFrom is set, or your id isn't in it.
  • Telegram logs 409 Conflict: two daemons read the same bot. Set pollInbound to false on the machine that shouldn't read it.
  • GitLab says the webhook failed: check that channels.gitlab.webhookPort is reachable from GitLab and that the secret matches channels.gitlab.webhookSecret.
  • A value is empty at runtime: a ${…} reference points at a variable missing from .env. Add it and restart the daemon.

Released under the MIT License.