Skip to content

Bot Flows

DomeNinchen edited this page Oct 24, 2026 · 10 revisions

Bot Flows

A bot flow automates something on your server: a trigger fires, and a chain of actions runs, with conditions, variables and loops in between if you need them. You build them in a visual editor — drag nodes onto the canvas, connect an output dot to an input dot — and nothing runs until you enable the flow.

Three things trip everyone up at least once, so they're worth stating first: a new flow is disabled until you switch it on; event triggers need SSH configured on the connection (everything else works with WebQuery alone); and a stored WebQuery result is always a list, even for a single-row command — see Placeholders and Tips and Pitfalls.

The shape of a flow

Every flow starts with exactly one trigger and flows left to right through whatever you connect after it. Most nodes have a single output; conditions have two (true / false); actions can optionally expose an error output so the flow carries on down a different path when the action fails instead of stopping.

Triggers

Trigger Fires when Needs
Event A live TeamSpeak event happens — a client joins (notifycliententerview), leaves (notifyclientleftview), moves channel (notifyclientmoved), a chat message, and so on SSH on the connection (Getting Started)
Cron A schedule you give in cron syntax comes round —
Webhook An external system calls this flow's webhook URL with its secret (details) —
Chat command Someone types a !command you define SSH, same as events

Only event and command triggers need SSH, because those are the live server-side events WebQuery can't poll for. Cron and webhook flows work on a WebQuery-only connection.

Calling a webhook

A webhook trigger has a path, an HTTP method (or "any") and a secret. The URL is the manager's address followed by /api/bots/webhook/ and the path; the caller sends the secret in an X-Webhook-Secret header (a secret query parameter works too, but ends up in proxy logs). A trigger without a secret never fires. Several flows may share one path; a call then starts every one of them whose secret matches.

What the caller gets back:

Answer Meaning
200 {"triggered": n, "dropped": m} n flows started; m were already running their maximum number of executions and did not start this time
429 {"triggered": 0, "dropped": m} with Retry-After Every matching flow was busy; nothing ran. Try again after the given seconds
404 {"error": "Not found"} Unknown path, wrong method or wrong secret — deliberately the same answer for all three, so nobody can probe for valid paths

A caller that must not lose an event should retry on 429: nothing ran. A 200 with dropped above 0 means only some of the flows on that path ran; retrying the same call would start the others again, so give a flow that must never miss a call a path of its own.

How many runs at once

One flow runs at most 20 executions at the same time by default; this matters mostly for flows that wait (a Delay node) and fire often. A trigger beyond that is not queued: it is dropped, whatever started it — event, chat command, schedule or webhook. Dropped triggers are not silent: the flow's card on the Bot Flows page shows a warning badge with how many were dropped in the last 24 hours, the flow's execution list has an entry for them, the backend log names the flow and the trigger type, and a webhook says so in its answer (above). Executions still running when a flow is switched off or saved keep counting until they end (one that waits in a Delay ends as cancelled once its Delay is over, if the flow is still off), and executions that go on after a backend restart count again. The limit is an instance-wide admin setting on the Executions: retention and limit card at the bottom of the Bot Flows page (1 to 200); a change applies at once. Raise it only if the flow's actions can cope — every running execution may be talking to TeamSpeak at the same time.

Actions

The editor groups them; the full set:

  • Moderation — Kick, Ban, Move, Message, Poke, Poke Group, AFK Mover, Idle Kicker.
  • Channels — Create, Edit, Delete, Temp-Channel Cleanup, and Animated Channel (a name that cycles on a timer, run as a background job; each Animated Channel node animates its own channel at its own speed, with a time zone for {{time.*}} in the text, and renames through the Bot Identity when one is set). TeamSpeak puts the connection that creates a channel into it; the flow's connection leaves again once a Move action puts someone in, or when the run ends, so a temporary channel is deleted as soon as its last user leaves. Until then, other runs on the same virtual server wait before creating a channel of their own. If the backend stops while the connection still sits in such a channel, the next start moves it out.
  • Groups & ranks — Add to / Remove from a server group, Rank Check (automatic ranking by connection time), Count Online in Groups, List Membership (maintain a named list, e.g. an ignore list).
  • Integration — WebQuery (run a raw ServerQuery command and store its result — only commands from a fixed list: ones that would stop, delete or reconfigure the server itself, or create or read privilege keys, are refused, and the flow's execution log says "not allowed in bot flows"), HTTP Request (call an external API), Webhook (send an outgoing webhook), Generate Code.
  • Voice — Play, Stop, Pause/Resume, Skip, Seek, Volume, Join Channel, Leave Channel, TTS — for driving a music bot from a flow.

Flow control

  • Condition — an expression; the flow takes the true or false output. See expressions below.
  • Variable — set, increment, append, or get a persisted variable. Variables survive between executions and are stored per flow.
  • Delay — wait a fixed time (at most 5 minutes) before the next node. A run waiting in a Delay with nothing else pending behind it goes on after a backend restart, see When the backend restarts.
  • Loop — iterate over an array variable (for example the rows of a stored WebQuery result), running the nodes after it once per item.
  • Log — write a line to the execution log at a chosen level; the fastest way to see what a flow actually did.

Placeholders and expressions

Any text field in a node can contain {{...}} placeholders, resolved when the flow runs:

  • {{event.<field>}} — data from the trigger event, e.g. {{event.clid}}, {{event.client_database_id}}.
  • {{temp.<name>.<row>.<field>}} — a result you stored earlier with a WebQuery action's Store As. It's always a list, so address the first row explicitly: {{temp.myResult.0.client_unique_identifier}}.
  • {{var.<name>}} — a persisted flow variable.
  • {{time.time}}, {{time.timestamp}} and similar — the current time.
  • {{flowId}}, {{executionId}}, {{configId}}, {{sid}}, {{triggerType}} — context about this run.

Placeholders take a pipe filter: {{...|uptime}} formats a seconds count as 2d 3h 40m, {{...|round}} and {{...|floor}} for numbers.

Condition nodes use expression syntax (not placeholders) with these helpers: contains, startsWith, endsWith, lower, upper, length, split, plus two specific to this app — hasGroup(clientGroups, groupId) to test group membership, and count(value) to count the rows of a stored WebQuery result (a plain string length or .length would give the wrong answer on an array of row objects).

Bot Identity

A flow's actions can run under a separate WebQuery login — the connection's Bot Identity — so they appear under their own name in TeamSpeak's own logs instead of blurring into your admin connection. Set it on the server connection; leave it unset and the flow uses the admin login.

The template gallery

New flows can start from a ready-made template you then adjust — a much faster start than a blank canvas. The gallery ships:

Channel displays: Clock Channel, Online Counter, Server Stats, Animated Channel Name. Onboarding & channels: Welcome Message, Support System, Private Channel Creator. Moderation & ranks: Auto-Rank, Last-Seen Tracker, AFK Mover, Idle Kicker, Bad Name Checker, Group Protector, Anti-VPN. Webhook-driven: Webhook → Server Message, Webhook → Assign Group, Webhook → Update Channel.

Templates with settings (channel IDs, groups, messages) ask for them in a setup dialog. A flow built that way remembers its template and the values: Configure template in the editor reopens the dialog with them and rebuilds the flow from the current version of the template. Nodes you changed by hand are replaced; nothing is stored until you press Save. Flows created before this button existed (frontend 3.85.0) don't have it.

The templates are laid out as a reference for building your own: the main path runs along one row, and branches that end early sit a row above it (from a true output) or below it (from false or error), so no two lines cross or run on top of each other.

Watching a flow run

Each execution is recorded with its per-node log — the place to look when a flow "did nothing." In the flow editor, Executions in the toolbar opens a side panel with the flow's last 50 runs: running, waiting in a Delay (with the time it ends), completed (with how long it took), failed (with the error), interrupted by a backend restart (with the reason and the node it had started last), cancelled because the flow was switched off while it waited, or dropped at the concurrency limit, with how many triggers that entry stands for (the count is final once that minute is over). Click a run to see its log line by line, with level and node name. The panel refreshes itself every few seconds while it is open, so you can leave it open, trigger the flow, and watch the run appear. Admins and bot operators see it, like the editor itself. Combined with Settings → Debug → ServerQuery Debug (which logs the exact command sent and TeamSpeak's exact answer, see Troubleshooting), it's usually quick to tell a trigger problem from an action problem.

When the backend restarts

A run lives in the backend process. What happens to it when that process ends — an update, a restart, a crash — depends on where the run is at that moment:

  • Waiting in a Delay with nothing else pending — no other branch of the flow still to run behind it, not inside a Loop, and not sitting in a channel the run created: the run keeps its place. The execution list shows it as Waiting, with the time its Delay ends. After the restart it waits out the rest, then goes on with its variables, and its next action runs once. If the backend only comes back more than 5 minutes after the Delay ended, the run is Interrupted instead: its moment has passed.
  • Anywhere else — busy with a node, or in a Delay inside a Loop or with another branch still pending: the run is cut off and shown as Interrupted, with the reason and the node it had started last. Open its log to see how far it got.

How much time there is depends on how the backend stops:

  • A regular stop (stopping or updating the containers, or a scheduled restart) gives runs that are busy with a node a few seconds to finish before the process ends; only what is still running after that is interrupted. Waiting runs do not hold the stop up.
  • A hard stop (a crash, a killed container, a power cut) gives no time at all; the next start sorts the runs out as above.

Nothing is repeated by itself. An action that had already started when the backend stopped is never run again, so a restart does not create a channel or send a message twice. Whether the last action of an interrupted run took effect (say, whether its channel was created) is best checked on the server itself.

Changes while a run waits are checked when its Delay ends, with or without a restart in between: if the flow (or its server connection) was switched off, the run is Cancelled; if the flow was edited and the Delay node is gone, it is interrupted; otherwise it goes on with the flow as it is now. Deleting a flow removes its runs with it.

A Delay waits at most 5 minutes. Something that has to happen much later is better started by a schedule (a cron trigger).

How long the history is kept

Every run adds rows to the execution history, and a flow on a busy event trigger can add a lot of them. An admin-only card on the Bot Flows page (Executions: retention and limit) sets how long that history is kept — a retention in days and a row cap per table (the default is 30 days and 50 000 rows). Dropped triggers are kept the same way, as one entry per flow and minute rather than one per trigger. The setting is instance-wide across all flows; anything past the window or the cap is removed automatically about once an hour, and lowering either value prunes right away. There is no need to clear the history by hand.


v1.7, last edited 24.10.2026

Getting started

Home
Getting Started
Install on Debian 13
Setup: same machine
Setup: elsewhere / rented
Users and Roles

Using the app

Dashboard
Virtual Servers and Instance
Channels
Clients
Client Database
Permissions
Server and Channel Groups
Moderation
Tokens and Passwords
Files
Icons
Bot Flows
Music Bots
Server Widgets
Server Maintenance
Server Logs
Query Console
Connection Journal
Themes and Appearance

Running it

Administration
Configuration
Deployment
Privacy and Hosting

When something is odd

Tips and Pitfalls
Troubleshooting

Contributing

Development

Repository · Changelog

Clone this wiki locally