---
title: 'How to Make an ER:LC Discord Bot for SSU Votes and Sessions'
published date: '2026-10-22'
author: 'Illiana Reed'
title-image: 'https://camelai.com/blog/assets/images/covers/erlc-discord-bot.png'
---

An ER:LC Discord bot can run the routine around each session on your private server: the SSU vote, the startup post with the join code, the shutdown, and a session log. In this tutorial, I'll walk you through how to make one with camelBot, without writing code or handing over an ER:LC server key.

We built this exact bot, called ssu, tested it privately with pretend members, then added it to our own Discord server. The replies, screenshots, and lessons below come from that build.

camelAI isn't affiliated with Police Roleplay Community (PRC), which makes Emergency Response: Liberty County (ER:LC, often typed ERLC), or with Roblox.

## What police roleplay servers make with camelBot

Department applications with staff review, support tickets (often one per department), and moderation tools come up again and again. Session tools are close behind: SSU votes, startup and shutdown posts, and sign-ups for session pings. So are shift and duty logs, promotions and discipline, arrest reports, and text dispatch boards for calls and BOLOs. Most are workflow bots built on forms, buttons, roles, and saved records rather than AI.

## What a good ER:LC Discord bot does

An SSU (server startup) opens your private server for a session once enough people say they'll join, and an SSD (server shutdown) closes it. A session bot keeps that routine the same every time.

- **A vote with a target and a deadline.** **I'll join** and **Remove my vote** buttons, a live count like 3/5, and the closing time.
- **One vote per member.** A second press changes nothing.
- **A clear result.** The vote passes at the target and tells whoever opened it, or fails at the deadline.
- **A startup when the server is really up.** Server name, join code, rules link, host, and start time, plus a mention of everyone who voted.
- **Fresh voters only.** A passed vote can be used for at most one session, so nobody is pinged twice for the same vote.
- **A shutdown and a log.** How long the session ran, and a private list of past sessions.

Many servers ping an SSU role at startup. A camelBot bot can't ping a role, `@here`, or `@everyone`, so this one mentions the members who voted instead.

Ready-made ER:LC bots are quicker to install if a standard setup is all you need. Make your own for your own vote rules, wording, and log.

## Make an ER:LC session bot: the steps at a glance

1. **Describe your bot.** Sign in at [bots.camelai.com](https://bots.camelai.com/) with Discord and paste the prompt from Step 1.
2. **Choose Camel's app and a name.** Ours is ssu.
3. **Tighten the vote rules.** We sent one planned follow-up and one repair.
4. **Run a practice SSU.** Vote, start, and end a session in the test channel as three people.
5. **Check deadlines and pings.** A failed vote, a late press, and two passed votes in a row.
6. **Publish and add it to Discord.** Then check its private log in your server.

## What you need

- A Discord account, to sign in at bots.camelai.com.
- The **Manage Server** permission in your Discord server, and a channel for sessions, such as `#sessions`.
- Your private server's name, its join code, and a rules link.
- No ER:LC API pack, server key, Roblox account, or code. camelBot is free while in beta, with no credit card, and this bot uses no AI.

## Step 1: Describe your ER:LC session bot

Once you're signed in, camelBot asks **What should your bot do?** Copy the prompt below, paste it into the box, and select **Continue**. It's the prompt we used, word for word.

```text
Build an ER:LC session bot for our Roblox police roleplay server.

/setup (Manage Server only) saves our private server's name, its join code and our rules link, and answers privately.

/vote, /start, /end and /log need Manage Messages.

/vote takes votes needed (2 to 30) and minutes (1 to 120). It posts an SSU vote here with "I'll join" and "Remove my vote" buttons, the count like 3/5, and the deadline as a Discord timestamp. One vote per member; answer presses privately. When the count is reached, close the vote, turn its buttons off, and mention whoever started it to say it passed. At the deadline, close it as failed.

/start posts the startup: server name, join code, rules link, host and start time. If a vote passed since the last session, mention everyone who voted in it.

/end posts the shutdown and how long the session ran.

Refuse a second open vote, /start while a session is running, and /end when none is.

/log privately shows the last 10 sessions: host, votes, start and end times, and who ended it.

Save every vote, voter and session.
```

The startup is its own command because the host still has to open the server in ER:LC; posting it the moment a vote passes could share the join code before anyone can get in. The prompt also spells out every refusal, since camel-builder fills any gap with its own guess.

## Step 2: Choose Camel's app and name your bot

We picked **Camel's app**, marked **Recommended**, so there's nothing to set up on Discord's developer site. Commands then start with the bot's name: we named ours **ssu**, so staff type `/ssu vote` and `/ssu start`. Select **Create ssu**.

Camel's app offers **Use this name and picture in Discord** for a custom look in your servers. For top-level commands like `/vote` or a separate Discord app identity, choose **Your own app** and follow our [own app guide](https://camelai.com/docs/camelbot/own-app). Paste its bot token only into camelBot's token field, never into the chat. This bot doesn't use ordinary messages or member joins, so it works the same either way.

## Step 3: Tighten the vote rules with two follow-ups

camel-builder writes and tries the code while **camel-builder is working…** shows under the message box. Ours finished with **Built ssu · checks passed**. It named the options it chose, `/ssu setup server code rules` and `/ssu vote needed minutes`, and said what it hadn't tried: it had closed its own failed vote by running the deadline alarm by hand. Step 5 checks a real deadline.

**Follow-up 1: use each passed vote once.** The prompt says to mention everyone who voted in a vote that passed since the last session. We made that rule exact right after the first build:

```text
One change: /start uses only the most recent vote that passed since the last session ended, and uses it once. Save that vote's voters with the session at that moment, so later votes or vote removals don't change them, and never use an older passed vote again. /log shows which vote each session used.
```

camel-builder saved it: each session now keeps a copy of its vote's voters, made at the start, and `/ssu log` names the vote each session used.

**Follow-up 2: a repair.** Two problems showed up next. camel-builder's summary said our oldest session, from its own earlier tries, now read "vote #1 · 0 voters", although two people had voted. That session predated the saved voter copies, so the real number was unknown, not zero. And when we asked for a read-only review of the code, camel-builder found a gap: a press after the deadline, before the deadline alarm ran, could still pass a vote. We sent one repair, trimmed here:

```text
Repair two confirmed generated-code gaps in ssu, preserving all existing rows and fixtures. (1) An old session with a vote id but no saved snapshot must say voter snapshot not recorded, never 0 voters; do not reconstruct a historical snapshot from mutable vote rows or change original data. (2) Every join/remove and pass transition must enforce closes_at as well as status. A press at/after the deadline must fail the still-open vote once, disable buttons and privately refuse; it must never pass even if the scheduled alarm is late. An alarm fired before its deadline must not fail a vote early.
```

Now that session reads "voter snapshot not recorded", and each press checks the deadline as well as the vote's status. camel-builder checked edge cases like a press exactly at the deadline in its own model with a pretend clock; we didn't reproduce those in the test channel. Your generated code may differ, so ask camel-builder whether a press after the deadline can still pass a vote, and send the repair if it can.

## Step 4: Run a practice SSU in the test channel

Beside the chat is the bot's private [test channel](https://camelai.com/docs/camelbot/test-channel), `#try-ssu`. Nothing there reaches Discord, and your commands run the saved code. Under **Talk as**, you can switch between yourself, the server owner, and two pretend members with no permissions, **Alex** and **Sam**. Quote values with spaces, like `server:"Liberty Test RP"`.

1. **Try staff commands as Alex.** Run `/ssu setup` and `/ssu vote` as Alex. Both are refused before the bot runs: setup needs Manage Server, and vote needs Manage Messages.
2. **Save your server's details.** As yourself, run `/ssu setup server:"Liberty Test RP" code:TEST1 rules:"https://example.com/rules"`. The private reply starts "Saved for this server: Liberty Test RP · join code TEST1".
3. **Open a vote.** Run `/ssu vote needed:2 minutes:10`. A public panel shows 0/2, the deadline, and both buttons. Run it again: "There's already an open vote in #try (0/2). Close it before starting another. No vote was posted."
4. **Vote twice, then change your mind, as Alex.** Press **I'll join** twice, **Remove my vote**, then **I'll join**. The private replies go 1/2, "You're already counted in this vote (1/2).", "Your vote is removed: 0/2.", then 1/2.
5. **Pass it as Sam.** Press **I'll join** as Sam. The panel changes to **Passed** at 2/2, both buttons turn off, and a public message mentions you: "the SSU vote passed: 2/2. Start the session with /ssu start." No startup is posted yet.

[![Test channel: an SSU vote panel that passed at 2/2 with its buttons off, a second vote refused, and Alex's repeat vote refused privately](/blog/assets/images/inline/erlc-discord-bot/test-channel-ssu-vote-passed.jpg)](/blog/assets/images/inline/erlc-discord-bot/test-channel-ssu-vote-passed.jpg "Open full-size screenshot")

6. **Start and end the session.** Switch **Talk as** back to yourself, then run `/ssu start` twice and `/ssu end` twice. The startup, "SSU is starting", lists Liberty Test RP, join code TEST1, the rules link, you as host, the start time, and "Voted to start: @Alex, @Sam". The second start is refused because a session is running. The shutdown, "SSU is over", gives the session's length and the vote that started it. The second end gets "No session is running, so there's nothing to end."

[![Test channel: the ER:LC Discord bot's startup mentioning Alex and Sam, a refused second start, the shutdown, and a refused second end](/blog/assets/images/inline/erlc-discord-bot/test-channel-ssu-startup-shutdown.jpg)](/blog/assets/images/inline/erlc-discord-bot/test-channel-ssu-startup-shutdown.jpg "Open full-size screenshot")

Our session lasted a few seconds and showed "The session ran for 1m.", so read the length as rounded. The test channel also showed the panel's `##` and `-#` formatting codes as plain text. Discord formats them as a heading and small print, but we didn't post a vote in Discord, so check the look during your first real SSU.

It's working when Alex and Sam can vote but can't run staff commands, each member counts once, and the startup waits for `/ssu start`.

## Step 5: Check deadlines and who gets pinged

Now try the cases most likely to go wrong. Run staff commands as yourself, switching to Alex or Sam only for their button presses. We ran these on saved code, except the late press, which camel-builder ran for us.

- **A vote that runs out.** Run `/ssu vote needed:3 minutes:2`, press **I'll join** as Sam, and wait. Alarms due within 15 minutes go off by themselves here; later ones wait for **Run now** in the alarm clock at the top. Ours went off on time: the panel changed to failed at 1/3 with its buttons off, and nobody was mentioned.
- **A press after it closed.** Disabled buttons can't be pressed here, so ask camel-builder: "Without changing any code, press I'll join on the failed vote as Alex." Its press got "This vote is closed, so it takes no more votes." and saved nothing. camel-builder's own presses run its working copy of the code, which it reported was unchanged since its last save.
- **The passed vote's own deadline.** After the first vote's 10 minutes were up, it still read passed.
- **A session with no fresh vote.** Run `/ssu start` and `/ssu end` again. This startup has no "Voted to start" line, because the only vote since the last session failed. You're still mentioned as host.
- **Two passed votes waiting.** Open two votes with `needed:2`, one after the other. Pass the first with yourself and Alex, the second with yourself and Sam. `/ssu start` then mentions you and Sam, not Alex, because only the latest vote counts. End it, then start and end one more session: that startup mentions no voters, and the older vote is never brought back.

Then run `/ssu log`. Ours showed eight sessions, newest first, including four from camel-builder's own tries. Each entry has the host, the vote it used with its voter count (or "no vote"), the start and end times, the length, and who ended it.

[![Test channel: the private /ssu log listing eight sessions with host, vote, start and end times, length, and who ended each](/blog/assets/images/inline/erlc-discord-bot/test-channel-ssu-session-log.jpg)](/blog/assets/images/inline/erlc-discord-bot/test-channel-ssu-session-log.jpg "Open full-size screenshot")

The oldest entry is the session from Step 3's repair. **Look at test data** in the **⋯** menu showed the voters saved with each session: Alex and Sam for the first practice session, you and Sam for the latest-vote session, and none for sessions without a vote.

## Step 6: Publish it and add it to your sessions channel

When your tests pass, select **Publish v1** on the builder's **Publish @ssu as version 1?** card. Ours listed "bot.ts added" and asked for no extra permissions. camel-builder never publishes on its own.

Camel's app was already in our server, so we opened the bot page's **Channels** tab, selected **Add to a channel**, and chose our server and channel. No Discord permission screen appeared, and the bot page showed **Live in #camel-bots**. For a new installation, **Add to Discord** opens Discord's permission screen; review what it asks for before approving.

Our new bot's live data started empty. In our server, `/ssu log` answered privately: "No sessions yet. A session starts with /ssu start."

[![Discord: the private /ssu log reply saying there are no sessions yet, from the cases app identity, marked Only you can see this](/blog/assets/images/inline/erlc-discord-bot/discord-private-ssu-log.jpg)](/blog/assets/images/inline/erlc-discord-bot/discord-private-ssu-log.jpg "Open full-size screenshot")

The test channel still held eight sessions, so the live bot keeps its own records. Our reply showed **cases**, the name of another bot on Camel's app in our server; yours can show a different name. Run `/ssu setup` with your real details before your first vote.

We didn't post a live vote, because members of our community channel would press it and the startup would ping them. Run one SSU with your staff before you announce the bot.

## Permissions an ER:LC session bot needs

This bot posts and edits its own messages. It doesn't moderate, give roles, or make channels, so its role position doesn't matter, and its published version requested no extra capabilities. Discord's screen for Camel's app can include permissions other bots on that app need, and we didn't test a fresh install. camelBot doesn't ask for Administrator; don't add it.

Staff need Manage Server for setup and Manage Messages for everything else. By default, members can see `/ssu` in the command list on Camel's app; anyone lacking the permission for the chosen command is refused, as Alex was.

## Why it works without the ER:LC API key

ER:LC's API requires the [ERLC API server pack](https://apidocs.erlc.gg/introduction) and your private server's key. [PRC's security guidance](https://apidocs.erlc.gg/api-clients#security-protect-your-server-key) explains that anyone holding the key can run commands and read the server's data.

camelBot has no secret store for bots. A key in a bot's code stays in every saved version, and a key saved as bot data remains readable by the bot's owner. Don't paste yours into camel-builder's chat or a command option. If you did, regenerate it in your private server's settings, under **ER:LC API** and then **API Key**.

A key could add live player counts, server logs, and in-game commands. This bot skips all of it: staff type the join code once, in `/ssu setup`.

## Cautions for ER:LC and Roblox servers

- **Post the join code where it belongs.** Everyone who can read the startup's channel sees the code, so run sessions in a channel meant for your members.
- **Never ask for Roblox passwords or cookies.** This bot doesn't touch Roblox. Treat anyone asking for a password or a `.ROBLOSECURITY` cookie as a scammer.
- **Keep departments fictional.** Use your own department names, not real agencies' names or badges.
- **Don't look official.** Don't call the bot official, and don't use the ER:LC or Roblox logo as its picture.

## Troubleshooting

| Symptom | Why | Fix |
| --- | --- | --- |
| `/vote` isn't in the menu | On Camel's app, commands start with the bot's name | Type `/ssu`, then pick `vote` |
| A staff member is refused | They don't have Manage Messages | Give their role Manage Messages, or ask camel-builder for another permission |
| "There's already an open vote" | One vote at a time per server | Let it pass or fail first |
| `/ssu start` says a session is running | The last one wasn't ended | Run `/ssu end` first |

## Limits to know

| Limit | Value |
| --- | --- |
| This bot's votes | 2 to 30 voters needed, 1 to 120 minutes |
| Waiting alarms | 200 per bot, from 1 second to a year ahead |
| Discord messages | 2,000 characters, about 80 mentions |
| This bot's commands | 5: setup, vote, start, end and log |

We didn't test a 30-person vote, so try a big one with your staff. The full list is in [Permissions and limits](https://camelai.com/docs/camelbot/limits).

## Ideas to try (untested)

We haven't built or tested these follow-ups.

**A session ping list,** since a role ping isn't possible:

```text
Add a "Notify me" button to the vote panel and a /notify command so members can join or leave a session ping list. /start mentions everyone on the list as well as the voters, once each.
```

**A reminder to open a vote.** Schedules run in UTC, so name your time zone and recheck it when the clocks change:

```text
Every Friday and Saturday at 7 pm US Eastern, if no session has started that day, remind staff in this channel to open an SSU vote.
```

**A boost post for a quiet server:**

```text
Add /boost (Manage Messages): while a session is running, post a "players needed" message with the join code, at most once every 30 minutes. Count each boost on that session's log entry.
```

## FAQ

**Can an ER:LC Discord bot ping my SSU role?**
Not a camelBot bot. It can't ping roles, `@here`, or `@everyone`. This one mentions the members who voted, and the ping-list idea above adds members who opt in.

**Do I need the ER:LC API for SSU votes?**
No. Everything here happens in Discord. The API adds live server data and in-game commands, but it needs your private server's key, and camelBot has no secret store for it.

**Is making an ER:LC Discord bot free?**
camelBot is free while in beta, with no credit card required. Making and testing use your camel-builder allowance. This bot doesn't use AI, so it doesn't draw on a bot's daily AI allowance.

## Next steps

Ready to make your own ER:LC Discord bot? Start from this example, send the follow-ups from Step 3, then describe what you'd like to change.

[Build this bot](https://bots.camelai.com/new?prompt=Build%20an%20ER%3ALC%20session%20bot%20for%20our%20Roblox%20police%20roleplay%20server.%0A%0A%2Fsetup%20(Manage%20Server%20only)%20saves%20our%20private%20server's%20name%2C%20its%20join%20code%20and%20our%20rules%20link%2C%20and%20answers%20privately.%0A%0A%2Fvote%2C%20%2Fstart%2C%20%2Fend%20and%20%2Flog%20need%20Manage%20Messages.%0A%0A%2Fvote%20takes%20votes%20needed%20(2%20to%2030)%20and%20minutes%20(1%20to%20120).%20It%20posts%20an%20SSU%20vote%20here%20with%20%22I'll%20join%22%20and%20%22Remove%20my%20vote%22%20buttons%2C%20the%20count%20like%203%2F5%2C%20and%20the%20deadline%20as%20a%20Discord%20timestamp.%20One%20vote%20per%20member%3B%20answer%20presses%20privately.%20When%20the%20count%20is%20reached%2C%20close%20the%20vote%2C%20turn%20its%20buttons%20off%2C%20and%20mention%20whoever%20started%20it%20to%20say%20it%20passed.%20At%20the%20deadline%2C%20close%20it%20as%20failed.%0A%0A%2Fstart%20posts%20the%20startup%3A%20server%20name%2C%20join%20code%2C%20rules%20link%2C%20host%20and%20start%20time.%20If%20a%20vote%20passed%20since%20the%20last%20session%2C%20mention%20everyone%20who%20voted%20in%20it.%0A%0A%2Fend%20posts%20the%20shutdown%20and%20how%20long%20the%20session%20ran.%0A%0ARefuse%20a%20second%20open%20vote%2C%20%2Fstart%20while%20a%20session%20is%20running%2C%20and%20%2Fend%20when%20none%20is.%0A%0A%2Flog%20privately%20shows%20the%20last%2010%20sessions%3A%20host%2C%20votes%2C%20start%20and%20end%20times%2C%20and%20who%20ended%20it.%0A%0ASave%20every%20vote%2C%20voter%20and%20session.&ref=blog "cta:build-example-end")

Want to start with a different idea?

[Build your own bot](https://bots.camelai.com/new?ref=blog "cta:build-own-bot")

For one-press-per-member buttons with a deadline, see the [giveaway bot tutorial](https://camelai.com/blog/discord-giveaway-bot). For department applications, start from the [ticket bot guide](https://camelai.com/blog/discord-ticket-bot). For more ideas, visit our [Discord bot maker](https://camelai.com/discord) page.
