Skip to content
Blocks

Complete Guide — Support Ticket System

Building a private support system on Discord requires orchestrating channel creation, explicit permission configuration, interactive components (Discord buttons), and clean deletion upon closing.

[!CAUTION] Deprecated legacy functions: Do not use $newTicket or $closeTicket for production bots. These legacy helpers create public channels without access restrictions and do not reliably clean up text channels. Exclusively use the proven modular pattern documented below.


1. Ticket System Architecture

To ensure total confidentiality for conversations between the user and staff:

[ Parent Category: 🔒 Private Support ]  <── @everyone: View Channel = DENIED
       │
       ├── [ Ticket Channel: #ticket-username ]
       │      ├── Staff & Bot (Inherited permissions: View, Send, Manage)
       │      ├── Ticket Creator (Explicit permission: View, Send, History)
       │      │
       │      └── [ Welcome message with Embed & Red Button "Close Ticket" ]
  1. Private Parent Category: Set up a Discord category where @everyone has View Channel disabled. The bot and moderation roles are granted administrative or management rights.
  2. Channel Creation: When /ticket executes, create a text channel nested under this category.
  3. Explicit Permissions: Apply an explicit permission overwrite for the member who triggered the command (allow: 68608 = View Channel + Send Messages + Read Message History).
  4. Welcome Message & Button: Immediately send a welcome embed into the channel with an interactive button (customId: close_ticket, Danger / Red style).
  5. Ephemeral Confirmation: Reply to the slash command with an ephemeral message (visible only to the author) containing a clickable channel link <#ID>.
  6. Closing & Deletion: When the button is clicked, acknowledge the interaction, wait 3 seconds for confirmation, then permanently delete the channel.

2. Step 1: /ticket Command (Creation & Welcome)

SLASH COMMAND: /ticket Trigger
Triggered when a member uses the slash command /ticket on the server.
CREATE A CHANNEL Key: ticket_chan
Channel name
ticket-((user.username))
Channel type
Text (0)
Parent Category (Support Category ID)
123456789012345678
EDIT PERMISSIONS Depends: ticket_chan
Channel ID
((action.ticket_chan))
Target (User)
((user.id))
Allowed permissions (Bitmask or flags)
68608 (View Channel + Send + Read History)
SEND A MESSAGE (Ticket Channel) Depends: ticket_chan
Target channel
((action.ticket_chan))
Embed — Title & Description
🎫 Support & Help — Welcome <@((user.id))>!
Components (Action button)
Red Button [Close Ticket] | customId: close_ticket
RESPOND TO INTERACTION (Slash Reply) Terminal
Text content
✅ Your ticket has been created: <#((action.ticket_chan))>
Ephemeral Reply (visible only to you)

BDScript code executed by the engine:

;; 1. Create text channel under the private category
$var[ticketChan;$createChannel[ticket-$username;text;123456789012345678]]

;; 2. Grant permissions to creator (+viewchannel, +sendmessages, +readmessagehistory)
$editChannelPerms[$var[ticketChan];$authorID;+viewchannel;+sendmessages;+readmessagehistory]

;; 3. Send welcome embed with close button to the new channel
$useChannel[$var[ticketChan]]
$title[Support & Help]
$description[Hello <@$authorID>! Please describe your issue here.\nA support team member will assist you shortly.\n\nTo close this ticket, click the red button below.]
$color[#5865F2]
$addButton[no;close_ticket;Close Ticket;danger]

;; 4. Reset channel and acknowledge the slash command ephemerally (no double $sendMessage!)
$useChannel[]
$ephemeral
✅ Your support ticket has been created: <#$var[ticketChan]>

Discord Visual Preview: Immediate Confirmation

Here is what the user immediately sees in the channel where they ran /ticket:

🤖
Bot Creator Assistant BOT ✔ Today at 2:32 PM
✅ Your support ticket has been created: #ticket-jeremy
Only you can see this • Dismiss message

Discord Visual Preview: Dedicated Private Channel

Here is what is posted inside the #ticket-jeremy channel:

🤖
Bot Creator Assistant BOT ✔ Today at 2:32 PM
🎫 Support & Help
Hello @Jeremy! Please describe your issue below.
A member of the support team will take care of your request.

When your issue is resolved, click the button below to close the ticket.

3. Step 2: Closing the Ticket (Button Click or /ticket-close)

When a member or moderator clicks the Close Ticket button (customId: close_ticket), Discord emits the interactionCreate event.

BUTTON CLICK: close_ticket Trigger
Triggered when the red button with Custom ID close_ticket is clicked.
RESPOND TO INTERACTION Acknowledgment
Notice message
🔒 Ticket closure requested by ((user.username)). Deleting channel in 3 seconds...
Ephemeral
WAIT Timer
Wait duration
3s
DELETE CHANNEL Destruction
Channel to delete
((channel.id))

BDScript interaction handler:

;; 1. Immediately acknowledge the click to prevent the Discord 3s timeout
🔒 Close requested by $username. Deleting this channel in 3 seconds...

;; 2. Clean wait timer
$wait[3s]

;; 3. Permanently delete the ticket channel
$deleteChannels[$channelID]

Discord Visual Preview: Closure Notification

🤖
Bot Creator Assistant BOT ✔ Today at 2:35 PM
🔒 Close requested by Jeremy. Deleting this channel in 3 seconds...

4. Complete JSON Representation (Runtime Action Engine)

Here is the exact JSON structure stored in the database and executed sequentially by the native Dart runtime:

Ticket Creation

[
  {
    "type": "createChannel",
    "key": "ticket_chan",
    "enabled": true,
    "payload": {
      "name": "ticket-((user.username))",
      "type": "text",
      "categoryId": "123456789012345678"
    }
  },
  {
    "type": "editChannelPermissions",
    "depend_on": ["ticket_chan"],
    "enabled": true,
    "payload": {
      "channelId": "((action.ticket_chan))",
      "targetType": "member",
      "targetId": "((user.id))",
      "allow": "68608",
      "deny": "0"
    }
  },
  {
    "type": "sendMessage",
    "depend_on": ["ticket_chan"],
    "enabled": true,
    "payload": {
      "channelId": "((action.ticket_chan))",
      "content": "Hello <@((user.id))>!",
      "embeds": [
        {
          "title": "🎫 Support & Help",
          "description": "Welcome to your private support channel. Please describe your issue below.",
          "color": "#5865F2"
        }
      ],
      "components": {
        "items": [
          {
            "type": "actionRow",
            "components": [
              {
                "type": "button",
                "customId": "close_ticket",
                "label": "Close Ticket",
                "style": 4
              }
            ]
          }
        ]
      }
    }
  },
  {
    "type": "respondWithMessage",
    "depend_on": ["ticket_chan"],
    "enabled": true,
    "payload": {
      "content": "✅ Your support ticket has been created: <#((action.ticket_chan))>",
      "ephemeral": true
    }
  }
]

Ticket Closing

[
  {
    "type": "respondWithMessage",
    "enabled": true,
    "payload": {
      "content": "🔒 Close requested. Deleting this channel in 3 seconds...",
      "ephemeral": false
    }
  },
  {
    "type": "wait",
    "enabled": true,
    "payload": {
      "duration": "3s"
    }
  },
  {
    "type": "removeChannel",
    "enabled": true,
    "payload": {
      "channelId": "((channel.id))"
    }
  }
]

5. Lightweight Alternative: Tickets via Private Threads

If your server is approaching Discord’s 500-channel limit, or if you prefer keeping history organized without creating entire text channels, you can use Private Threads:

  1. Create a general ticket landing channel #support-tickets.
  2. When /ticket is run, invoke the createThread action with type: "privateThread".
  3. Add the user with addThreadMember.
  4. To archive and close the ticket without destroying it, use updateChannel with archived: true and locked: true.