diff --git a/src/content/changelog/chatbot/2026-07-18-module-pages.mdx b/src/content/changelog/chatbot/2026-07-18-module-pages.mdx
new file mode 100644
index 0000000..8e7de28
--- /dev/null
+++ b/src/content/changelog/chatbot/2026-07-18-module-pages.mdx
@@ -0,0 +1,14 @@
+---
+title: New module docs, Giveaways and Contests
+description: Four new chatbot module pages — Song Request, Giveaways, Contests, and Fourthwall Giveaways — with payout math, settings, and real bot messages, all verified against the source.
+date: 2026-07-18
+---
+
+Four chatbot modules that previously had no documentation now have full pages:
+
+- **[Song Request](/chatbot/modules/songrequest)** — how the request queue works, all 13 chat commands with their permission levels, and every dashboard setting from queue moderation to tipped requests.
+- **[Giveaways](/chatbot/modules/giveaways)** — the ticket system, `!ticket max`, subscriber luck, and the full lifecycle from start to drawing winners.
+- **[Contests](/chatbot/modules/contests)** — prediction betting with `!bet`, including the exact payout formula (winners take their stake back plus a proportional share of the losing pool).
+- **[Fourthwall Giveaways](/chatbot/modules/fourthwall)** — the instant chat giveaways that start when someone gifts merch from a connected Fourthwall shop.
+
+Every command syntax, bot message, and settings table was checked against the chatbot source before publishing.
diff --git a/src/content/docs/chatbot/commands/default/songrequest.mdx b/src/content/docs/chatbot/commands/default/songrequest.mdx
index 11a14d8..bb1b557 100644
--- a/src/content/docs/chatbot/commands/default/songrequest.mdx
+++ b/src/content/docs/chatbot/commands/default/songrequest.mdx
@@ -35,12 +35,12 @@ Or using the alias:
## Parameters
diff --git a/src/content/docs/chatbot/modules/contests.mdx b/src/content/docs/chatbot/modules/contests.mdx
new file mode 100644
index 0000000..2c0da32
--- /dev/null
+++ b/src/content/docs/chatbot/modules/contests.mdx
@@ -0,0 +1,64 @@
+---
+title: Contests Module
+sidebar:
+ label: Contests
+description: "Run StreamElements betting contests where viewers wager loyalty points on an outcome with !bet, and winners split the losing pool proportionally."
+keywords:
+- StreamElements contests
+- Twitch predictions
+- betting contest
+- loyalty points betting
+- "!bet command"
+platforms: [twitch]
+---
+
+import ChatExample from '@components/ChatExample.astro';
+
+Contests are prediction-style bets: you define an outcome with two or more options, viewers back an option with loyalty points, and when you pick the winning option the losing bets are paid out to the winners.
+
+## How It Works
+
+1. Create the contest in the dashboard under **Loyalty → Contests**: a title, the options (each with a display title and the command viewers type), how many minutes betting stays open, and the minimum and maximum bet.
+2. The bot announces the contest with its options. Viewers bet with [`!bet`](/chatbot/commands/default/bet) — points are deducted immediately, and each viewer can bet once per contest.
+3. When the timer runs out (or you close it early), betting stops and the bot posts a per-option breakdown.
+4. You pick the winning option in the dashboard. Winners get their stake back plus a share of the losing pool; everyone else loses their bet. You can also refund the contest instead, returning every stake.
+
+
+
+## Payouts
+
+Payouts are proportional to the bet: a winner receives
+
+```
+payout = (total pool ÷ winning option's pool) × their bet
+```
+
+That's always at least their stake back, plus their share of what the losers put in. A 10-point bet pays exactly twice what a 5-point bet pays. Losing bets are gone — they fund the winners.
+
+## Configuration
+
+| Setting | Description |
+|---------|-------------|
+| Options | Each option has a display title and a chat command (what viewers type after `!bet`). |
+| Duration | How many minutes viewers have to bet. |
+| Minimum / Maximum bet | Bounds for a single bet, in points. |
+| Send confirmation when entering via chat | When on (the default), the bot confirms each bet in chat. |
+
+## Related
+
+- [`!bet`](/chatbot/commands/default/bet): Place a bet on an option.
+- [`!contest`](/chatbot/commands/default/contest): Show the running contest, its options, and the time left.
+- [Contests websocket topic](/websockets/topics/channel-contests): Realtime contest events for developers.
+
+## FAQ
+
+**Q: Can a viewer change or add to their bet?**
+A: No — one bet per viewer per contest. The first `!bet` locks in both the option and the amount.
+
+**Q: What amounts does `!bet` accept?**
+A: Plain numbers, `k`/`m` suffixes (`5k` = 5000), percentages of the viewer's balance (`25%`), and `all` — as long as the result stays within the contest's minimum and maximum bet.
diff --git a/src/content/docs/chatbot/modules/fourthwall.mdx b/src/content/docs/chatbot/modules/fourthwall.mdx
new file mode 100644
index 0000000..0b1d4b0
--- /dev/null
+++ b/src/content/docs/chatbot/modules/fourthwall.mdx
@@ -0,0 +1,43 @@
+---
+title: Fourthwall Giveaways Module
+sidebar:
+ label: Fourthwall Giveaways
+description: "How StreamElements chat giveaways work when someone gifts Fourthwall merch to your chat: the join keyword, entry window, and winner announcement."
+keywords:
+- Fourthwall giveaway
+- StreamElements Fourthwall
+- merch giveaway
+- gifted merch
+- Twitch merch drop
+platforms: [twitch]
+---
+
+import ChatExample from '@components/ChatExample.astro';
+
+When a supporter gifts merch from your connected [Fourthwall](https://fourthwall.com) shop to the chat, the chatbot turns it into an instant giveaway: it announces the gift, viewers type a keyword to enter, and the bot announces the winners when time runs out.
+
+## How It Works
+
+1. A supporter gifts one or more merch items to the chat through your Fourthwall shop. This is what starts the giveaway — there is no chat command to start one.
+2. The bot announces the giveaway with the join keyword and a countdown:
+
+
+
+3. Viewers enter by typing the keyword — anywhere in a message, in any casing. Each viewer is entered once, no matter how many times they type it.
+4. When the entry window closes, winners are drawn and announced along with how to redeem the gift. If nobody entered, the bot says so instead.
+
+## Requirements
+
+- A [Fourthwall shop connected to StreamElements](https://streamelements.com/dashboard/merch) — without it, the module has nothing to react to.
+- Twitch only.
+
+:::note
+Fourthwall giveaways don't appear under **Chatbot → Modules** — they're controlled from within the Fourthwall merch integration. The giveaway itself — items, duration, and keyword — comes from the Fourthwall gift.
+:::
+
+## Related
+
+- [Giveaways module](/chatbot/modules/giveaways): StreamElements' own giveaways, where viewers buy tickets with loyalty points.
+- [Chat Alerts module](/chatbot/modules/chatalerts): Posts chat messages for regular (non-gifted) merch purchases.
diff --git a/src/content/docs/chatbot/modules/giveaways.mdx b/src/content/docs/chatbot/modules/giveaways.mdx
new file mode 100644
index 0000000..7223804
--- /dev/null
+++ b/src/content/docs/chatbot/modules/giveaways.mdx
@@ -0,0 +1,62 @@
+---
+title: Giveaways Module
+sidebar:
+ label: Giveaways
+description: "Run StreamElements giveaways where viewers buy tickets with loyalty points via !ticket or the giveaway page, with subscriber luck and free-ticket options."
+keywords:
+- StreamElements giveaways
+- Twitch giveaway
+- loyalty points giveaway
+- ticket giveaway
+- subscriber luck
+platforms: [twitch]
+---
+
+import ChatExample from '@components/ChatExample.astro';
+
+Giveaways let viewers spend loyalty points on tickets for a chance to win a prize you define. The chatbot announces the giveaway, sells tickets in chat, and celebrates the winner; the giveaway itself is created and drawn from the dashboard.
+
+## How It Works
+
+1. Create the giveaway in the dashboard under **Loyalty → Giveaways** — set the title, description, preview image, ticket cost, and limits — and start it.
+2. The bot announces it in chat. Viewers buy tickets with [`!ticket`](/chatbot/commands/default/ticket) (or `!enter`) or on your public giveaway page. Every ticket has an equal chance of winning.
+3. When you're ready, close the giveaway to stop ticket sales and draw a winner from the dashboard. You can draw as many winners as you like, one at a time.
+4. The bot announces each winner in chat, along with the chance they had of winning.
+
+
+
+`!ticket max` buys as many tickets as the viewer's points allow, up to the per-user maximum.
+
+## Configuration
+
+Settings when creating a giveaway:
+
+| Setting | Description |
+|---------|-------------|
+| Ticket cost | Loyalty points per ticket. A cost of 0 makes entry free. |
+| Give everyone their first ticket for free | Each viewer's first ticket costs nothing, even when a ticket cost is set. |
+| Max Tickets | Caps how many tickets a single viewer can hold. |
+| Subscriber luck | Multiplies subscribers' chances, from 1× (no special treatment) up to 10× — 2× is equivalent to holding twice the tickets. |
+| Only allow subscribers to enter | Non-subscribers who try to enter from chat are turned away. |
+| Send confirmation when buying tickets via chat | When on, the bot confirms each chat entry. Off by default, to keep chat quiet during busy giveaways. |
+
+If something goes wrong, you can refund the giveaway — every entrant gets their points back. Individual entries can also be refunded.
+
+## Related
+
+- [`!ticket`](/chatbot/commands/default/ticket): Buy giveaway tickets from chat.
+- [`!giveaway`](/chatbot/commands/default/giveaway): Check the currently running giveaway and get the entry link.
+- [Giveaways websocket topic](/websockets/topics/channel-giveaway): Realtime giveaway events for developers.
+- Not to be confused with the [Raffle module](/chatbot/modules/raffle), where entry is free via `!join` and the prize is the points themselves.
+
+## FAQ
+
+**Q: What can viewers win?**
+A: Whatever you put up — the prize is the title and description you write. StreamElements picks the winner; delivering the prize is up to you.
+
+**Q: Do viewers get their points back if they don't win?**
+A: No — tickets are spent. Points only come back if you refund the giveaway (or a specific entry).
diff --git a/src/content/docs/chatbot/modules/index.mdx b/src/content/docs/chatbot/modules/index.mdx
index 69f45bd..3d66fa4 100644
--- a/src/content/docs/chatbot/modules/index.mdx
+++ b/src/content/docs/chatbot/modules/index.mdx
@@ -24,6 +24,7 @@ Chatbot modules are a set of powerful tools designed to enhance viewer interacti
Enable and configure each module in your StreamElements dashboard. Once enabled, modules work in one of two ways:
- **Command-triggered** modules start when someone types a command in chat (for example, `!bingo`, `!duel`, or `!queue join`). Emote Combo and Emote Pyramid are the exception among the chat games: they react automatically to emotes posted in chat.
+- **Dashboard-started** events — Giveaways and Contests — are created from the dashboard, and viewers take part from chat with `!ticket` or `!bet`.
- **Automatic** modules post messages on their own when something happens on your stream, such as a new follower (Chat Alerts) or the stream going live (Live Announcement). No commands are required.
## Available Modules
@@ -43,9 +44,23 @@ Enable and configure each module in your StreamElements dashboard. Once enabled,
+### Loyalty events
+
+
+
+
+
+
+### Media
+
+
+
+
+
### Automation
+
diff --git a/src/content/docs/chatbot/modules/songrequest.mdx b/src/content/docs/chatbot/modules/songrequest.mdx
new file mode 100644
index 0000000..344c46c
--- /dev/null
+++ b/src/content/docs/chatbot/modules/songrequest.mdx
@@ -0,0 +1,86 @@
+---
+title: Song Request Module
+sidebar:
+ label: Song Request
+description: "Let viewers request YouTube songs and videos with !sr, moderate the queue, and control playback — the StreamElements Song Request (Media Request) module."
+keywords:
+- song request
+- media request
+- StreamElements song request
+- "!sr command"
+- YouTube queue
+- Twitch music requests
+platforms: [twitch]
+---
+
+import ChatExample from '@components/ChatExample.astro';
+
+The Song Request module lets viewers add YouTube songs and videos to a shared queue from chat, while you (and your mods) control playback. In the dashboard it's called **Media Request**.
+
+## How It Works
+
+1. A viewer requests media with [`!songrequest`](/chatbot/commands/default/songrequest) (`!sr`), passing a YouTube URL, video ID, or search terms — searches resolve to the first YouTube result. Viewers can also request from your StreamElements profile page or the tipping page.
+2. The request lands in the queue — or, with **Queue moderation** enabled, in a Pending Approval list until you or a mod with dashboard access approve it.
+3. Songs play through the Media Request player: on your dashboard, on the stream via the overlay widget, and on your public request page at `streamelements.com//mediarequest`.
+
+
+
+:::note
+YouTube is the only media source — there is no Spotify or SoundCloud integration. And although StreamElements supports several chat platforms, the song request chat commands are Twitch-only.
+:::
+
+## Commands
+
+Viewer commands (available to everyone):
+
+| Command | What it does |
+|---------|--------------|
+| [`!songrequest` / `!sr`](/chatbot/commands/default/songrequest) | Request a song by URL, video ID, or search |
+| [`!song`](/chatbot/commands/default/song) | Show the currently playing song |
+| [`!next`](/chatbot/commands/default/next) | Show the next song in the queue |
+| [`!when`](/chatbot/commands/default/when) | Show when your requested song will play |
+| [`!wrongsong`](/chatbot/commands/default/wrongsong) | Remove your own most recent request |
+| [`!voteskip`](/chatbot/commands/default/voteskip) | Vote to skip the current song (only when skip voting is enabled) |
+| [`!volume`](/chatbot/commands/default/volume) | Show the player volume |
+
+Moderator commands (permission level 500+):
+
+| Command | What it does |
+|---------|--------------|
+| [`!skip`](/chatbot/commands/default/skip) | Skip the current song |
+| [`!pause`](/chatbot/commands/default/pause) / [`!play`](/chatbot/commands/default/play) | Pause or resume playback |
+| [`!songqueue`](/chatbot/commands/default/songqueue) | Link the public request page with the full queue |
+| [`!removesong`](/chatbot/commands/default/removesong) | Remove a song by requester name or YouTube URL |
+| [`!srclear`](/chatbot/commands/default/srclear) | Clear the entire queue |
+
+## Configuration
+
+Song Request doesn't appear under **Chatbot → Modules** — it's controlled entirely from the dashboard's **Media Request** section. The main setting groups:
+
+| Setting | Description |
+|---------|-------------|
+| Viewer requests | Master toggle for requests via `!sr`, your profile page, and the tipping page. |
+| Queue moderation | Requires each request to be approved from the Pending Approval section before it enters the playlist. |
+| Queue limit / per-user limits | Cap the total queue size and the number of free or paid requests per viewer (0 = unlimited). |
+| Maximum request duration | Reject media longer than the configured length, with separate limits for free and paid requests. |
+| Backup playlist | A YouTube playlist that plays whenever the request queue is empty. |
+| YouTube filters | Limit requests to music, blacklist specific videos or tags, and set an automatic filter based on video rating and view count. |
+| Loyalty cost | Charge loyalty points per request, with an optional subscriber discount. Users at or above the exempt user level (Moderator by default) bypass the cost and other limitations. |
+| Minimum user level | Restrict who may request at all. |
+| Tipped requests | Prioritize requests attached to tips, price them per second of media, and set a minimum tip amount. |
+| Skip voting | Enable `!voteskip` and set how many votes a skip requires. |
+
+## FAQ
+
+**Q: Does requesting a song cost points?**
+A: Only if you set a loyalty cost. If a viewer can't afford it, the bot answers with the cost and their balance, and nothing is queued. Subscribers can get a percentage discount, and anyone at or above the exempt user level requests for free.
+
+**Q: Where do approved requests actually play?**
+A: In the Media Request player — on your dashboard, in the on-stream overlay widget if you've added one, and on your public `mediarequest` page. The chatbot only manages the queue; it doesn't play audio itself.
+
+## Related
+
+- [Loyalty System](https://support.streamelements.com/hc/en-us/articles/10474478470290-Loyalty-System-Overview) — required if you charge points for requests.