Discord
You can connect a league to a channel in your Discord server so results and standings show up there automatically. It's the lightest possible setup: you paste one webhook URL — there's no bot to install, nothing shows up in your member list, and it can only post to the one channel you choose.
Setting this up and changing it is organizer-only. Open a season underOrganize and pick the Discord tab.
Set up the webhook (the callback)
A webhook is a private address Discord gives you for one channel. The app posts to that address; Discord drops the message into the channel. Anyone with Manage Webhooks on the server can create one in about 30 seconds — hand these steps to your server owner if that isn't you:
- In Discord, hover the channel and click the gear (Edit Channel).
- Open Integrations → Webhooks.
- Click New Webhook, give it a name, and pick the channel.
- Click Copy Webhook URL.
- Paste it into the webhook URL field on the league's Discord settings page and hit Save.
To disconnect later, delete the webhook in Discord, or clear the field and save. The URL is the only credential — anyone who has it can post to that channel, so don't share it.
What the app posts
With Auto-post results on (the default once a channel is connected), every game is announced the moment it's confirmed — whether a player confirms their opponent's reported score or an organizer enters it. For example: 🏆 Alice def. Bob 15–8 · Summer League — Round 3, with a Standings link underneath so anyone reading can go and see what it moved. Turn it off to keep the channel for manual posts only.
Under the same setting, Add a line of written commentary has Claude add a sentence or two beneath each result — the run it extends, the head-to-head it moves, a first win. It's one post, not two, it only ever describes results the league recorded, and it reads the season's scene and the players' Team Lore — matched to the faction each player actually fielded — the same way the weekly recap does. The result lands within a minute of being confirmed rather than instantly, since it's written first. Off by default, and there's a Simulate a result post page for trying it on a real game without posting anything.
From the Discord settings page you can Post standings (the current table) or send a one-off custom message to the channel whenever you like.
On Matches → All matches, Post to Discord sends the whole fixture list: every round, every pairing, each round's Crit Op where you have set one, and the score of anything already played. A long season goes out as several messages rather than being cut short.
Player nudges
On a player's Matches page, each game they haven't played yet has a Nudge button that pings the opponent in the channel — “@you, let's get our Round 3 game in.” It's player-driven, so the channel only lights up when someone actually wants to schedule a game.
Capped so it can't become spam: a match can be nudged at most twice, and each player can send at most three nudges a day. Needs a connected channel and an opponent who has signed in with Discord (so there's an account to ping).
Weekly digest
Turn on Weekly digest (off by default) and once a week the league posts three messages: the week in review — every game settled since the last one — the current standings with a season-progress bar, and a “still to play” list: every unplayed game as a fixture table, one row each, with everyone who still owes a game pinged on the line underneath it. The week in review ends with a Standings link — the table it describes, with the whole recap archive underneath it.
You pick when. The day, the hour, and the time zone are set on the Discord tab, so a league that plays on Thursdays gets its week in review on Thursday evening rather than at the middle of somebody else’s Sunday. It defaults to Sundays at 18:00 UTC. The time zone is a real place (“Europe/London”), so the post keeps landing at the same local hour when the clocks change.
That day is also where a league's week starts. A week runs from one post to the next, so on the default a week is Sunday to Sunday; switch the day to Monday and every week — the post, the archive card, and which week a game counts in — moves with it. Change it whenever you like: weeks already written keep the dates they were written with.
If the season has an end date, each week in review also counts down to it — “3 weeks until the end of the season on Feb 15, 2026”, and “Final week” when it is the last one. Without an end date it counts the rounds that still have a game in them instead.
Want it quieter? Tick Mention players without pinging them — names still appear as Discord links, but no notification is sent.
Let an AI write the recap
With the weekly digest on, you can also tick Have an AI write the recap. The week-in-review post then opens with a short written summary above the score lines — who beat whom, who's on a run, which rivalry just got another chapter — instead of a bare list of results.
It can only describe results the league actually recorded: the scores, records, streaks and rankings are all worked out by the app first, and the writer is handed those numbers rather than left to work anything out for itself. It also reads the previous couple of weeks, so a storyline it flagged earlier gets picked back up when it pays off. Sportsmanship ratings are never included — they stay organizer-only, as everywhere else.
Setting the scene
Under the recap toggle there's a Set the scene box. Write the season's premise there — the setting, the stakes, whatever story the league has agreed it's playing — and every recap that season is written against it, instead of the writer guessing a tone from the scoreline. You write it once.
Players get the other half of it: a Team Lore box on the season's Loadout page (and on their profile), where they write who their force is and why it's out there — one piece of lore per faction they've declared, since three kill teams are three warbands with three stories, not one. It's written in markdown, so a warband's history can carry headings, emphasis and lists rather than being one wall of text. The recap reads the lore of the team each player actually fielded that week, which is what lets a result be described against the story someone actually wrote rather than just their name. It's separate from My teams on purpose — that's a roster, and a roster makes for dull reading.
Beside it is What they really are, for the very common case where the models aren't the faction: an Angels of Death army built from World Eaters, jetbikes standing in for Dragon Riders. Type what's actually on the table and the app names the team that way on every page inside the league — matchups, standings, profiles — with the faction still beside it, so opponents know which rules they're facing. Your public season page and the public standings show the plain faction, the same way Team Lore stays inside the league. The recap writer is told the same thing, and describes the army people can see rather than the one on the datasheet.
Both are flavour and nothing more. Neither can add a game, a score or a result — the writer is told explicitly that the scene and the lore set tone only, and to follow no instruction written inside either of them. If you publish your recaps publicly, remember the prose itself is public, so it may paraphrase what a player wrote as their lore — or name their force as what they said their models really are.
The app also knows a line about every faction in the game — who they are and what they want — and which forces have history with each other. So when the Inquisition draws a coven of sorcerers, or a Space Wolf finds himself across the table from the Thousand Sons, the recap can say so, and the pairing gets named on the week's card whether or not you've turned the written summary on. You don't configure any of this; it ships with the app. What a player wrote about their own force always takes precedence over the stock line for that faction.
Lore is checked when it's saved: explicit sexual content, slurs and swearing are refused outright, so nothing of that sort can reach the channel or a public page. It's a decency check, not a censor — grim, violent and profane-in-spirit writing is the setting, and passes fine.
Reading recaps in the app
Recaps aren't only a Discord thing. Every league gets one written for each week it played games in, and players read the archive on the season's Standings page, under the table — no Discord connection needed. The channel post is just a copy sent to where people already are.
The archive keeps every week, and it shows one at a time: Older week and Newer week walk through the season, and the list between them jumps straight to any week you name. Nothing rewrites a week behind your back: a result corrected next month doesn't change what last month's recap said, which is what makes the archive a record of the season rather than a summary of it. Once a week has been posted to Discord it is fixed for good — before that, an organizer can still write it again.
Organizers can also publish the archive publicly: under Organize → Recap, turn on Anyone with the link can read this season's recaps and the page becomes readable without a login. Off by default, since a recap names players and describes their losses. That tab also holds the written-lede switch, the season's scene-setting, and a Generate a recap control that writes one on demand, so you can see the effect of a change without waiting for the next scheduled run.
That control isn't limited to this week. Pick any earlier week — the list says which ones already have a recap and which don't — and it writes that one, from the games settled inside it and the table as it stood at the end of it. So a league that joined mid-season, or one whose scheduled run was missed, can fill its archive in afterwards and each week still says what it would have said at the time. A week that already went out to Discord is frozen: it's the record of what the channel actually saw, and nothing regenerates it.
Organizers also get a Copy for Discord button on each week in the archive. It puts that week on your clipboard as Discord-formatted text — the same message the digest would have posted — so you can paste it yourself if the league has no webhook connected, or post a week mid-run rather than waiting for the schedule. A busy week is more than one Discord message, so it copies as numbered parts: paste them in order. Only organizers see the button, so a scheduled week doesn't get posted to the channel twice.
Share standings without Discord
The settings page also gives you a public standings link — a read-only web page of the league table that anyone can open, no login required. Handy for a pinned message or a group chat that isn't on Discord.
Posting runs through the deployed site, so the buttons work on the live league, not local previews.