Discord
You can connect a league to a channel in your Discord server so results and standings show up there automatically — and so anyone in that server can type /standings or /schedule for the live table and remaining fixtures, or /mymatches for their own games, without opening the app. Posting is still the lightest possible setup: you paste one webhook URL. There's no bot in your member list, and the webhook can only post to the one channel you choose. Slash commands are a separate, optional install of the same Pairings Up app under Server Settings → Integrations — also not a member, just a command list.
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 counts — as soon as a player reports it, or, in a league that asks for confirms, once their opponent confirms it — or when 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. On by default for a new season, 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.
When you commit a schedule, Announce the pairings in Discord (ticked unless you untick it) posts the same fixture list with a “Pairings are up!” line on top. Replace a schedule mid-season and the post says the schedule has changed instead, so nobody keeps playing the old pairings. Both lines can be reworded (see below).
Rewording the posts
The nudge, the welcome, the join request, the result and quest lines, the heading of the weekly “still to play” reminder and the “Pairings are up” and “Schedule changed” lines can all be reworded. On the Discord settings page, open Edit message wording. Each message shows a preview of the post as Discord would draw it; Edit wording opens the text, and clicking a field such as {winner}, {loser_faction} or {winner_score} drops it in, to be filled with the real name, faction or score when the message goes out. The preview updates as you type.
Anything you don't reword uses the site's default wording. A template can't ping anyone — @everyone, @here and hard-coded mentions are refused — though the nudge's {opponent} still pings the player it names. The link back into the app and the card under a result are added whatever the wording says.
Slash commands
Once a channel is connected, anyone in that Discord server can type /standings or /schedule and get a reply built from this league's real data — the same table the in-app Standings page ranks, the same remaining fixtures the weekly digest would list. The reply is public in the channel by default (so the club can see it); tick private to keep it to yourself. Several leagues sharing a server pass season: with the league's name.
/mymatches is the personal one: your rank and record, every result you've played (including a score still waiting on a confirm), and the opponents you still have to play, finals included. It works out who you are from your Discord account, so it only knows you once you've signed in to the app with Discord and joined the league — otherwise it tells you how to do both. The reply is private by default; set private to False to share it in the channel. If you play in more than one league on the server it picks the one you're in, and only asks for season: when you're in several.
The commands only answer in a server whose organizer has connected a webhook. That is the permission model: pasting the webhook is how the league proves it owns a channel there, so a command typed in some other server cannot read this league. There is no Pairings Up login on the Discord side — if you can see the command, you can invoke it.
A server owner adds the app once (the Discord settings page has the invite when the site has an application configured). Commands can take up to an hour to appear the first time they are registered globally; after that they show up as soon as the app is in the server and a webhook is saved. Reporting a result still happens in the app: the score form and the opponent-confirm step are the two-step flow a slash command would have to fake, and we didn't.
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).
Player guides
New players know the game but not the app. So once a season starts, the channel gets a short player guide every other day until the set runs out. There are six. Finding your matches, reporting a score, how the standings work, quests, the gallery and Best Painted. Each one is a few lines on what the feature does and how to use it, with a picture of the page, a link to the full guide here in Help and a link to the page in your league.
They start the day you commit the schedule and post at your digest hour. They skip the digest's own day, so nobody gets the week in review and a how-to at once. The quests guide waits until your season has quests, and the Best Painted guide waits for a Best Painted vote. New seasons have them switched on.
To change any of that, open Choose the guides and preview them under Player guides on the Discord tab. That page turns the whole drip on or off, leaves out any guide you don't want, shows which have gone out and which is next, and lets you reword each one while you watch a preview of the post. The first line of a guide is the message itself. The rest goes in the card, and the app always adds the links and the picture. If Discord refuses three guides in a row, usually because the webhook was deleted, the app stops trying and that page tells you. Fix the connection, then press Try again. The same six guides head the Help page, so players can read them whenever they like.
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. A week with no games settled skips the week in review, but the standings and the “still to play” list still go out.
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
On Organize → Recap, Have Claude write the weekly lede is ticked for every new season. The week in review 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. The scheduled run writes that lede whenever the switch is on, including for a league with no channel connected, and the archive on Standings gets the same prose.
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
On the same Organize → Recap tab, Set the scene sits under that toggle. 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 Have Claude write the weekly lede, Set the scene, 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.