Integrations

Connect Discord to your team

Discord is where most Le Mans Ultimate teams already live, so a lot of what Stintsmith offers — lap PBs, race signups, results — wants to land there rather than make people open another tab. The bot is built for that single job: bring portal events into the channels you already have, and let members run a few read-only slash commands without leaving Discord. It does not run polls, it does not moderate, it does not crawl messages, and it does not need Manage Server permanently.

Install the bot

Open Settings → Integrations → Discord in the portal. The page hosts the signed invite URL for the bot, scoped to the two things it actually needs: bot and applications.commands. Copy the URL, paste it in your browser, and pick the Discord server you want the bot in. You will need Manage Server on the Discord side for the invite to take; once it has joined, you can drop that role down to whatever your normal admin layer is.

The bot asks for four per-channel permissions: View Channel, Send Messages, Embed Links, and Read Message History. The default invite grants them everywhere; if your server uses per-channel overrides, add the bot’s role to the channels you want it to read or post in and leave it out of the rest. It will not ask for DM, Manage Roles, or Manage Channels — if a future feature ever needs more, the bot will surface a re-authorise prompt rather than silently asking for it.

Bind the server to a team

Once the bot is in your server, come back to Settings → Integrations → Discord and click Add server. Pick the Discord server from the dropdown (only servers where the bot is already a member appear) and confirm. From this point on, every slash command run from that Discord server resolves against your Stintsmith team, and only your team. The binding is one-to-one — a Discord server can be tied to exactly one team, and a team can be tied to as many servers as you need.

Members of the Discord server link their own portal accounts with /link. The bot DMs them a one-time URL that signs the click into their portal account; from then on, any personal data a slash command returns (their PB, their signups, their standings) is scoped to them and delivered ephemerally — only the caller sees it. Cross-team queries return the same not-found response as queries against resources that genuinely do not exist, so the bot never confirms what else lives outside the calling server’s team.

Post lap times automatically

Under the same Integrations page, Notifications routes portal events to specific Discord channels. The five most useful for a fresh setup:

  • lap.uploaded — a clean embed for every successful upload from the Companion. Useful in a #telemetry channel for engineers; turn it off for driver-only channels or it gets noisy fast.
  • lap.pb_set — only fires when a driver beats their personal best on a layout. This is the one almost every team enables and almost no one regrets — it is the social pulse of the team.
  • race.signups_opened — a single message in the race-ops channel with a button to RSVP. Replaces the manual who’s in for Sunday thread.
  • race.results_posted — finishing order with best lap and total time, in a race-ops or general channel. Goes out within a minute of the chequered flag.
  • championship.standings_updated — only relevant if you run a championship on Team Premium. Picks the right round automatically.

Notifications are silent by default — no @everyone on any event. If you want a specific event to ping a role (a captains-only ping on race signups, say), set the role mention in the same row. The bot will only use the role mentions you explicitly choose, and it cannot mention anyone who has not opted in to that role.

The slash-command reference, the invite URL with the correct permission integer, and the webhook event schemas live on the integrator page at /docs/discord. If you are setting this up for your team rather than building against the bot, you almost certainly do not need to open that page; the Integrations surface inside the portal does the same thing without the curl examples.