Skip to content

Instantly share code, notes, and snippets.

@firatkucuk
Last active February 4, 2026 21:36
Show Gist options
  • Select an option

  • Save firatkucuk/ee898bc919021da621689f5e47e7abac to your computer and use it in GitHub Desktop.

Select an option

Save firatkucuk/ee898bc919021da621689f5e47e7abac to your computer and use it in GitHub Desktop.
Deletes slack public/private channel messages, private chat messages and channel thread replies.
#!/usr/bin/env node
// Channel ID is on the the browser URL.: https://mycompany.slack.com/messages/MYCHANNELID/
// Pass it as a parameter: node ./delete-slack-messages.js CHANNEL_ID
// CONFIGURATION #######################################################################################################
const token = 'SLACK TOKEN';
// Legacy tokens are no more supported.
// Please create an app or use an existing Slack App
// Add following scopes in your app from "OAuth & Permissions"
// - channels:history
// - groups:history
// - im:history
// - mpim:history
// - chat:write
// VALIDATION ##########################################################################################################
if (token === 'SLACK TOKEN') {
console.error('Token seems incorrect. Please open the file with an editor and modify the token variable.');
}
let channel = '';
if (process.argv[0].indexOf('node') !== -1 && process.argv.length > 2) {
channel = process.argv[2];
} else if (process.argv.length > 1) {
channel = process.argv[1];
} else {
console.log('Usage: node ./delete-slack-messages.js CHANNEL_ID');
process.exit(1);
}
// GLOBALS #############################################################################################################
const https = require('https')
const historyApiUrl = `/api/conversations.history?channel=${channel}&count=1000&cursor=`;
const deleteApiUrl = '/api/chat.delete';
const repliesApiUrl = `/api/conversations.replies?channel=${channel}&ts=`
let delay = 300; // Delay between delete operations in milliseconds
// ---------------------------------------------------------------------------------------------------------------------
const sleep = delay => new Promise(r => setTimeout(r, delay));
const request = (path, data) => new Promise((resolve, reject) => {
const options = {
hostname: 'slack.com',
port : 443,
path : path,
method : data ? 'POST' : 'GET',
headers : {
'Authorization': `Bearer ${token}`,
'Content-Type' : 'application/json; charset=utf-8',
'Accept' : 'application/json'
}
};
const req = https.request(options, res => {
let body = '';
res.on('data', chunk => (body += chunk));
res.on('end', () => resolve(JSON.parse(body)));
});
req.on('error', reject);
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
// ---------------------------------------------------------------------------------------------------------------------
async function deleteMessages(threadTs, messages) {
if (messages.length == 0) {
return;
}
const message = messages.shift();
if (message.thread_ts !== threadTs) {
await fetchAndDeleteMessages(message.thread_ts, ''); // Fetching replies, it will delete main message as well.
} else {
const response = await request(deleteApiUrl, {channel: channel, ts: message.ts});
if (response.ok === true) {
console.log(message.ts + (threadTs ? ' reply' : '') + ' deleted!');
} else if (response.ok === false) {
console.log(message.ts + ' could not be deleted! (' + response.error + ')');
if (response.error === 'ratelimited') {
await sleep(1000);
delay += 100; // If rate limited error caught then we need to increase delay.
messages.unshift(message);
}
}
}
await sleep(delay);
await deleteMessages(threadTs, messages);
}
// ---------------------------------------------------------------------------------------------------------------------
async function fetchAndDeleteMessages(threadTs, cursor) {
const response = await request((threadTs ? repliesApiUrl + threadTs + '&cursor=' : historyApiUrl) + cursor);
if (!response.ok) {
console.error(response.error);
return;
}
if (!response.messages || response.messages.length === 0) {
return;
}
await deleteMessages(threadTs, response.messages);
if (response.has_more) {
await fetchAndDeleteMessages(threadTs, response.response_metadata.next_cursor);
}
}
// ---------------------------------------------------------------------------------------------------------------------
fetchAndDeleteMessages(null, '');
@kadiiskiFFW

kadiiskiFFW commented Feb 26, 2021

Copy link
Copy Markdown

Hey firatkucuk, i'm using the same thing.
I added it in the token. Added the APP to the slack workspace, I'm admin there, added all of the listed scopes and added the channel ID from the URL.

Still I get "invalid_auth". :(

image

And these are my scopes as well:

image

@firatkucuk

Copy link
Copy Markdown
Author

Hey firatkucuk, i'm using the same thing.
I added it in the token. Added the APP to the slack workspace, I'm admin there, added all of the listed scopes and added the channel ID from the URL.

Still I get "invalid_auth". :(

image

And these are my scopes as well:

image

TBH, it should work with those settings could you please add those bot scopes as well:

image

@kadiiskiFFW

Copy link
Copy Markdown

Hey thanks for the help.

I have set the BOT permissions same as yours:
image

I have just tested the script both with User OAuth Token and Bot User OAuth Token.

I have also reinstalled the app - still not working. I'm so sorry for losing your time.

@firatkucuk

Copy link
Copy Markdown
Author

@kadiiskiFFW No problem. I'll try to create an app from scratch in my spare time. It was old-style tokens and was really easy to use then slack changed to tokens with scopes. Now it's really hard to adjust things.

@kadiiskiFFW

Copy link
Copy Markdown

@firatkucuk thank you very much for the effort!

@lorvent

lorvent commented Feb 26, 2021

Copy link
Copy Markdown

FWIW, this was not working for me too but i gave up because it is not that important for me.
my understanding is that, if a message is created by bot using webhook, then it is unable to delete such messages but i need it for exact same purpose.

thanks.

@firatkucuk

Copy link
Copy Markdown
Author

I have created a clone app that is identical to the working one and seems there's an issue on the slack side. I have contacted slack support.

@firatkucuk

Copy link
Copy Markdown
Author

@kadiiskiFFW and @lorvent for the new application they are no longer support tokens from query string so I've updated the code using as Authorization Bearer header.

@akintt

akintt commented Mar 16, 2021

Copy link
Copy Markdown

@firatkucuk teşekkürler hocam sorunsuz hallettik.

@firatkucuk

Copy link
Copy Markdown
Author

@akintt super.

@ensargunesdogdu

Copy link
Copy Markdown

Thanks, Fırat :) Very useful snippet.

@harrywebster

Copy link
Copy Markdown

How crazy that this work around is needed... surely it would be easier for Slack to offer this rather than getting hundreds of users spamming them with delete API calls.

Thanks for this script, it made the reset far, far, easier... even if i did need to leave it running over the weekend.

@shadowmodder

Copy link
Copy Markdown

+1 much-needed feature. Thank you for the script!.

@Bumpyshot

Bumpyshot commented Jul 13, 2021

Copy link
Copy Markdown

Hey, thanks for the script but i have a little problem...
I have a channel_not_found when i run the code, yet i have the right id of the channel
Any idea ?

@firatkucuk

Copy link
Copy Markdown
Author

@Bumpyshot
channel id is at the end of the URL: https://COMPANY.slack.com/archives/CHANNEL_ID
if you're sure about that in that case maybe your token has no adequate access for that operation.

@JonathanNobrega

Copy link
Copy Markdown

How to use this script?

First: Get your Slack Api Token

  1. Login to Your Apps
  2. Create a new app and select the workplace you would like to connect to
  3. From the sidebar, go to “OAuth & Permissions” page
  4. Under “Scopes” section -> “User Token Scopes” select the following scopes:
    channels:history
    groups:history
    im:history
    mpim:history
    chat:write
  5. Under “OAuth Tokens for Your Workspace” section, click “Install App to Workspace” and follow the instructions
  6. Now, under “OAuth Tokens for Your Workspace” section, copy the “User OAuth Token”: this is your token

How to get the Channel ID which I want to delete the messages?

  1. Go to your Slack from the web, and navigate to the desired channel
  2. You can see the channel ID from the url (https://app.slack.com/client/{TEAM_ID}/{CHANNEL_ID})
  3. Copy the last part of the URL, this is your CHANNEL_ID

Now download the script in this page, and insert your token inside the file (under SLACK TOKEN), then save

Now, run the script: node ./delete-slack-messages.js CHANNEL_ID

(Instructions adapted from: https://gist.github.com/gummi-io/f3dbfebfcd5fd1fc4e42da1c0e2b41c8)

@im-syk

im-syk commented Nov 5, 2021

Copy link
Copy Markdown

@JonathanNobrega
Thanks, your discription helped me!

@im-syk

im-syk commented Nov 5, 2021

Copy link
Copy Markdown

Please tell me.
Is it possible to specify the period to be deleted by this script?

@doschkinow

Copy link
Copy Markdown

thanks to @firatkucuk and @JonathanNobrega , this script worked fine for me!

@JonathanNobrega

Copy link
Copy Markdown

Glad to help @im-syk and @doschkinow!

@erickacevedor

Copy link
Copy Markdown

Hey! When deleting messages from a DM, I got the message:

vada {
  ok: false,
  error: 'cant_delete_message',
  warning: 'missing_charset',
  response_metadata: { warnings: [ 'missing_charset' ] }
}
1650841222.763739 could not be deleted! (cant_delete_message)

Any ideas?

@razikallayi

Copy link
Copy Markdown

@JonathanNobrega

Thanks, I was using the bot token. Changed to user token and reinstalled the app. It worked.

@alevaldiviezo

Copy link
Copy Markdown

Is there any chance to run this script just with 'node ./slack-delete-messages', without the channel id in the terminal?

@firatkucuk

Copy link
Copy Markdown
Author

@alevaldiviezo what do you want to achieve? Deleting all messages in all channels or just running without channel_id.?

let channel = 'put your channel id here';

// ------------------- delete following section from here
if (process.argv[0].indexOf('node') !== -1 && process.argv.length > 2) {
    channel = process.argv[2];
} else if (process.argv.length > 1) {
    channel = process.argv[1];
} else {
    console.log('Usage: node ./delete-slack-messages.js CHANNEL_ID');
    process.exit(1);
}
// ------------------- to here

@alevaldiviezo

Copy link
Copy Markdown

Hi, thanks for your reply, I got it, the idea is to run the script as a second process with the fork command, many thanks I got it now.

@firatkucuk

firatkucuk commented Feb 5, 2024 via email

Copy link
Copy Markdown
Author

@firatkucuk

firatkucuk commented Feb 5, 2024 via email

Copy link
Copy Markdown
Author

@firatkucuk

firatkucuk commented Feb 5, 2024 via email

Copy link
Copy Markdown
Author

@mrjones-plip

mrjones-plip commented May 1, 2024

Copy link
Copy Markdown

Excellent script - thanks @firatkucuk !

@im-syk et al. - if anyone is interested in a version of this that deletes last 30 days, I made a version that does that. To change how long it keeps things for, update the keepTime variable as needed.

@vdiazh

vdiazh commented Jun 18, 2024

Copy link
Copy Markdown

Thanks a lot, all work fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment