Repository navigation
Widgets
A widget is an embeddable, public snapshot of a server — the kind of live status banner you put on a website or forum so visitors can see who's online without opening TeamSpeak. You manage them from the Embed widgets button on the Dashboard (admins only) — not to be confused with the dashboard's own tiles, which every user arranges for themselves.
In the widget manager, Create and set:
- Name — for your own reference.
- Server and Virtual Server ID — which server it reports on.
- Theme — a visual style for the rendered banner.
- Show Channel Tree / Show Clients — what the widget includes.
- Hide Empty Channels and Max Channel Depth — trim a large tree down to something that fits in a banner.
A widget's user count leaves out ServerQuery logins. An admin can narrow it further in a widget's Edit view, under Online count of the embed widgets: tick server groups whose clients should not be counted, and turn on Don't count this manager's music bots to leave out its own music bots. The setting belongs to the virtual server, so it applies to every widget of that server, and it is separate from the dashboard's own setting. Only the number changes; a widget's channel tree still lists every client.
Each widget gets its own token, and that token is the public key to it: anyone with the URL can view the widget without logging in, and only that one widget's data is exposed. Three forms are served, all under /api/widget/<token>/:
| URL | Use |
|---|---|
…/<token>/image.svg |
Sharp vector image — best for a web page |
…/<token>/image.png |
Raster image — for places that won't render SVG, like some forum software |
…/<token>/data |
Raw JSON — if you want to build your own display |
There's also a live embed (an <iframe> snippet the manager gives you) for a self-updating banner rather than a static image. The manager's SVG and embed buttons copy the right URL or snippet for you.
Because the token grants public read access, treat it like a shared link: regenerating or deleting the widget invalidates the old URL. Widget responses are briefly cached, so a change on your server shows up within a short delay rather than instantly.
The live widget page (/widget/<token>) is the one page of the app that other sites may put in an iframe. Every other page refuses to be framed from another site, on purpose.
If the frame on the other site stays empty, the browser's developer console (F12) says why, usually a message like "Framing … violates the following Content Security Policy directive: frame-ancestors" or "Refused to display … in a frame because it set X-Frame-Options". Two causes are worth checking:
- An older frontend. With the Docker images, frontend versions before 3.66.1 sent the strict "this site only" framing headers on the widget page as well, so the live embed was refused everywhere. Update the frontend. The SVG and PNG images are not framed, so they do not depend on this; they have a check of their own, see the next section.
-
A proxy that sets its own framing headers. A reverse proxy or CDN in front of the app, configured to add
X-Frame-Optionsor aframe-ancestorspolicy to the whole site, overrides what the app sends. Leave the path/widget/out of that rule, or don't set the header there.
To see what actually arrives, ask for the widget page and look at the headers (any text works as a token for this check):
curl -sI https://your-manager.example/widget/testThere should be no X-Frame-Options line, and the Content-Security-Policy line should contain frame-ancestors *.
The two images are meant to be loaded by other sites (an <img> tag, a forum's image code), so the backend lets exactly these two be embedded cross-site; its other responses keep the stricter default. If one of them shows as a broken image on the other site, two causes are worth checking:
-
An older backend. Backend versions before 2.52.1 sent
Cross-Origin-Resource-Policy: same-originon the images like on everything else, and browsers refuse to show such an image on another site (Chromium-based browsers lognet::ERR_BLOCKED_BY_RESPONSE.NotSameOriginin the developer console). Update the backend. The JSON at…/<token>/datais read with an ordinary cross-origin request and is not affected by this. -
A proxy that sets its own policy. A reverse proxy or CDN in front of the app, configured to add a
Cross-Origin-Resource-Policyheader to the whole site, overrides what the app sends. Leave the path/api/widget/out of that rule, or don't set the header there.
To see what actually arrives, ask for one of your widget's image URLs and look at the headers:
curl -sI https://your-manager.example/api/widget/<token>/image.svgThe Cross-Origin-Resource-Policy line should say cross-origin.
A widget shows only what you enabled — channel tree and/or client list for the one virtual server. It's read-only and carries no management access whatsoever; the token can't be used against the rest of the API. Still, it is public, so don't enable the client list on a widget if who's connected is something you'd rather not put on a public page.
The live page stores nothing in the visitor's browser and shows no storage notice, because it sits on somebody else's site; see Privacy and Hosting.
v0.6, last edited 16.10.2026
Getting started
Using the app
Running it
When something is odd
Contributing