Guides
Ticket system
Post a panel with an "Open a ticket" button. Each click opens a private thread for the member and your staff.
You will build a support ticket system:
- A panel message with an Open a ticket button.
- A click opens a private thread. Only the member and your staff can see it.
- Inside, a Close ticket button deletes the thread when the problem is solved.
Works on every plan.
What you need#
- A channel for the panel, for example
#support. - The bot needs these permissions in that channel: Create Private Threads, Send Messages in Threads and Manage Threads.
- Staff need Manage Threads to see every ticket.
How the parts fit#
| Part | Kind | Job |
|---|---|---|
/ticket-panel | Slash command | Posts the panel once |
Tickets | Event: Button Click | Runs when someone clicks a ticket button |
The panel button stays on the message for weeks. Each click runs the event. The event knows which button was clicked by its custom ID, a short name you give each button.
1. The panel command#
Open Create → Slash Command.
| Field | Value |
|---|---|
| Command Name | ticket-panel |
| Description | Post the ticket panel |
Open the permissions part of the form. In Discord permissions, pick Manage Server. Only staff see the command.
Under Behavior, turn on Only the user sees the reply.
Pre-hook:
panel = ComponentBuilder()
panel.add_button("Open a ticket", style="primary", emoji="🎫", custom_id="ticket_open")
send_message(
channel,
message=Message(
title="Need help?",
description="Press the button below. A private ticket opens for you and our staff.",
color="#5865F2",
),
components=panel,
)
set_content("Panel posted.")2. The button event#
Open Create → Event.
| Field | Value |
|---|---|
| Name | Tickets |
| Type | Button Click / Select Menu Change |
| Custom ids | ticket_open, ticket_close |
Code:
# Answer Discord first. It waits only 3 seconds.
defer_response(ephemeral=True)
if custom_id == "ticket_open":
thread = create_thread(
channel,
name=f"ticket-{user.username}",
type="private",
initial_message=f"{user.mention} thanks for opening a ticket. Tell us how we can help.",
)
close = ComponentBuilder()
close.add_button("Close ticket", style="danger", custom_id="ticket_close")
send_message(thread, "Staff: press this when the ticket is solved.", components=close)
respond_interaction(interaction, f"Your ticket is open: {thread.mention}")
elif custom_id == "ticket_close":
if not has_permission(user, "manage_threads"):
respond_interaction(interaction, "Only staff can close tickets.")
else:
delete_channel(channel)What it does:
defer_response(ephemeral=True)tells Discord "working on it". The answer only shows to the clicker.create_thread(..., type="private")makes the private thread. Mentioning the member ininitial_messageadds them to it.- The Close ticket button has its own custom ID, so the same event handles it.
delete_channel(channel)deletes the thread. Herechannelis the thread the button is in.
Watch out
Each custom ID must be in the event's Custom ids box. A button whose ID is not listed does nothing, and Discord shows "This interaction failed".
3. Test it#
- Run
/ticket-panelin#support. - Press Open a ticket. A link to your new thread appears.
- Open the thread and press Close ticket. The thread is deleted.
- Try Close ticket from an account without Manage Threads. It says only staff can close.
Errors from your code show in Logs → Error Logs on the dashboard.
Ideas#
- Ask why first. Open a form when the button is clicked, then make the thread with the answer. See Forms.
- Log closed tickets. Before
delete_channel, postsend_message("ticket-log", f"{channel.name} closed by {user.mention}"). - One ticket per member. Save the open thread ID with
update_member_var("ticket", thread.id)and check it before making a new one. See Storage. - Stop spam clicks. In a button event,
wait = cooldown("1 minute", key=user.id)gives the time left, orFalsewhen the member may click. Stop with a message whenwaitis set.