How to save and export Discord support tickets as readable transcripts

A ticket transcript in Discord.js V14 is a saved record of a support channel's messages, usually exported as an HTML file or text document that users and moderators can download and keep. The most practical approach uses the discord-html-transcripts library, which converts channel messages into a formatted HTML file that looks like Discord itself. You install it alongside your bot code, call it when a ticket closes, and it generates a file you can send to users or store for records.

This guide covers the setup, the actual code to generate transcripts, where to store the files, and how to let users download them. The examples assume you already have a Discord.js V14 bot running with a ticket system in place.

Key Takeaways

  • The discord-html-transcripts package is the standard tool for turning Discord channels into downloadable HTML files that preserve formatting and images.
  • You generate a transcript by calling generateFromChannel() on the ticket channel, then save the file to your server's storage or a file hosting service.
  • The transcript is usually created when a ticket closes, and you can send the file to the user via a direct message or in the ticket channel before deletion.
  • For production bots, store transcript files on a cloud service like AWS S3 or a dedicated file server rather than keeping them on your bot's machine.
  • Always check that your bot has permission to read message history in the ticket channel, or the transcript will be incomplete or fail.

Installing and importing the transcript library

Start by installing discord-html-transcripts through npm in your project directory:

npm install discord-html-transcripts

Once installed, import it at the top of the file where you handle ticket closure. If you're using a command handler or event listener for when tickets close, add this line:

const { createTranscript } = require('discord-html-transcripts');

If you're using ES modules instead of CommonJS, use:

import { createTranscript } from 'discord-html-transcripts';

The library requires Node.js 16.9.0 or higher and discord.js V14. Check your versions with node --version and look at your package.json to confirm discord.js is listed as version 14 or later.

Generating a transcript when a ticket closes

The most common place to generate a transcript is inside the event or command that closes the ticket. Here's a basic example using a button interaction when someone clicks a "Close Ticket" button:

const transcript = await createTranscript(interaction.channel, { limit: -1, poweredBy: false });

The limit: -1 parameter tells the function to fetch all messages in the channel, not just the last 100. The poweredBy: false removes the "powered by discord-html-transcripts" footer from the HTML file. If you want to keep it, set that to true or remove the parameter entirely.

After generating the transcript, you have a Buffer object (binary data) that you need to save to a file. The simplest approach is to write it to your bot's file system:

const fs = require('fs');

fs.writeFileSync(`./transcripts/ticket-${interaction.channel.id}.html`, transcript);

This creates a folder called transcripts in your bot's root directory and saves the HTML file with the ticket channel's ID as the filename. Make sure the transcripts folder exists before running this, or create it programmatically with fs.mkdirSync('./transcripts', { recursive: true });

Sending the transcript to the user

After saving the file, send it to the user who opened the ticket. The most reliable way is to send it as a direct message, since the ticket channel is about to be deleted:

const user = interaction.user;

await user.send({ files: [{ attachment: transcript, name: `ticket-${interaction.channel.id}.html` }] });

This sends the transcript Buffer directly without needing to read it back from disk. The user receives it as a downloadable HTML file they can open in any web browser.

If you want to send it in the ticket channel before closing, use:

await interaction.channel.send({ files: [{ attachment: transcript, name: `ticket-${interaction.channel.id}.html` }] });

Then delete the channel a few seconds later to give Discord time to process the file upload. Use setTimeout(() => { interaction.channel.delete(); }, 2000); to wait 2 seconds before deletion.

Storing transcripts on cloud storage for production bots

If your bot runs on a hosting service or you want transcripts to persist beyond your bot's uptime, save them to cloud storage instead of your local file system. AWS S3 is the most common choice for this.

Install the AWS SDK:

npm install @aws-sdk/client-s3

Then configure it with your AWS credentials and upload the transcript:

const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');

const s3 = new S3Client({ region: 'us-east-1' });

const command = new PutObjectCommand({ Bucket: 'your-bucket-name', Key: `ticket-${interaction.channel.id}.html`, Body: transcript });

await s3.send(command);

Replace your-bucket-name with your actual S3 bucket name and set the region to match your bucket's location. Store your AWS access key and secret key in environment variables (a .env file), never in your code directly.

Handling errors and missing permissions

The transcript generation will fail silently or throw an error if your bot lacks the READ_MESSAGE_HISTORY permission in the ticket channel. Always wrap the transcript call in a try-catch block:

try {

const transcript = await createTranscript(interaction.channel, { limit: -1, poweredBy: false });

await interaction.user.send({ files: [{ attachment: transcript, name: `ticket-${interaction.channel.id}.html` }] });

} catch (error) {

console.error('Transcript generation failed:', error);

await interaction.reply({ content: 'Failed to generate transcript.', ephemeral: true });

}

Common causes of failure include the bot being removed from the server before the transcript finishes generating, the channel being deleted before the function completes, or the user's DMs being closed. The error message in your console will tell you which one occurred.

Customizing transcript appearance

The discord-html-transcripts library accepts additional options to change how the HTML looks. You can pass them as a second parameter to createTranscript():

const transcript = await createTranscript(interaction.channel, {

limit: -1,

poweredBy: false,

filename: `ticket-${interaction.channel.name}.html`,

saveImages: true,

});

The filename option sets what the downloaded file will be called. The saveImages: true parameter embeds images directly into the HTML file instead of linking to Discord's servers, which is useful if you want transcripts to work offline. Be aware that this increases file size significantly if the ticket has many images.

Other useful options include tz to set the timezone for message timestamps, and minify: true to reduce file size by removing unnecessary whitespace from the HTML.

Frequently Asked Questions

What if the transcript file is too large to send via Discord DM?

Discord's file upload limit is 25 MB for most users. If your transcript exceeds this, upload it to a file hosting service like Pastebin, Hastebin, or a cloud storage bucket, then send the user a download link instead of the file itself. You can also compress the HTML with minify: true to reduce size.

Can I generate transcripts for channels that are already deleted?

No. Once a channel is deleted, Discord removes all its message history and you cannot recover it. Generate and save the transcript before you delete the channel. If you need to keep transcripts long-term, generate them immediately when a ticket closes, before any deletion happens.

How do I make the transcript look different from the default Discord style?

The discord-html-transcripts library generates HTML that mimics Discord's appearance and does not expose CSS customization options directly. You can edit the HTML file after generation to change colors, fonts, or layout, but this requires manual HTML and CSS knowledge. For most use cases, the default appearance is sufficient.

Do transcripts include deleted messages or edited message history?

Transcripts show only the current state of messages at the time of generation. Deleted messages do not appear, and edited messages show only their final version, not the edit history. This is a Discord API limitation, not a library limitation.

What happens if my bot goes offline before the transcript finishes generating?

The transcript generation will fail and the user will not receive a file. For production bots, consider storing a backup of recent messages in a database so you can regenerate transcripts later if needed, or use a more robust hosting solution that does not go offline.