Skip to main content

Build a Slack Bot for File Sharing with the EasySend API

March 27, 2026 - EasySend Team

Slack is where most teams already talk about files. The problem is that Slack itself is a poor place to host them. Files uploaded to a free workspace disappear after 90 days. Attachments above 1GB are blocked. And shared files cannot leave the workspace without screenshots or downloads, which breaks any external collaboration. A Slack bot backed by the EasySend API solves the problem in a single slash command. The user types /share, the bot uploads to EasySend and a public download link appears in the channel.

This guide walks through the full build from creating the Slack app to deploying the webhook. It assumes you have Node.js 18 or newer and a server or serverless platform where you can run a webhook. Every line is reproducible and the EasySend API requires no key, no OAuth and no signup.

What You Will Build

A Slack slash command /share that accepts an uploaded file or a URL, posts it to EasySend over HTTPS and returns a short download link the channel can use. The bot is workspace-scoped, the message that includes the link is ephemeral by default and the entire round trip takes under two seconds for typical files. The same pattern works for Discord, Microsoft Teams and Mattermost with minor protocol changes.

Step 1: Create the Slack App

Open api.slack.com/apps in a browser and sign in with the workspace where you want to deploy the bot. Click Create New App and choose From an app manifest. App manifests let you declare permissions, slash commands and event subscriptions in a single YAML file rather than clicking through ten settings pages. Pick the target workspace and paste in the manifest from the next step.

Step 2: Paste the Manifest

The manifest below declares the bot user, the OAuth scopes, the slash command and the event subscription. Replace your-domain.example.com with the public host that will receive Slack requests once you deploy the webhook.

display_information:
  name: EasySend
  description: Share files outside Slack with one slash command
  background_color: "#00d4ff"
features:
  bot_user:
    display_name: EasySend
    always_online: true
  slash_commands:
    - command: /share
      url: https://your-domain.example.com/slack/commands
      description: Upload a file to EasySend and post the link
      usage_hint: "[file or URL]"
      should_escape: false
oauth_config:
  scopes:
    bot:
      - chat:write
      - chat:write.public
      - commands
      - files:read
      - files:write
settings:
  event_subscriptions:
    request_url: https://your-domain.example.com/slack/events
    bot_events:
      - file_shared
      - app_mention
  interactivity:
    is_enabled: true
    request_url: https://your-domain.example.com/slack/interactive
  org_deploy_enabled: false
  socket_mode_enabled: false
  token_rotation_enabled: false

Step 3: Understand the OAuth Scopes

Each scope unlocks a specific capability. chat:write lets the bot post messages in channels it is a member of. chat:write.public extends that to public channels without explicit invites. commands enables the slash command. files:read is required to download attachments the user shared when invoking /share with a file. files:write is needed if you later want the bot to upload a thumbnail back to Slack. Avoid wildcard scopes. Slack will warn users about overly broad permissions during install and a noisy consent screen kills installs.

Step 4: Configure Event Subscriptions

Slash commands work over HTTP requests Slack sends to your webhook. Event subscriptions are separate. The bot listens for file_shared (someone dropped a file into a channel) and app_mention (someone tagged @EasySend) so the same handler can react without a slash. Slack verifies the events endpoint with a one-time URL challenge so your handler must echo back the challenge field on the first request.

Step 5: Install to Workspace and Capture the Bot Token

From the app settings page open OAuth and Permissions and click Install to Workspace. Slack walks you through the consent screen. After approval the page shows a Bot User OAuth Token that starts with xoxb-. Copy it into an environment variable named SLACK_BOT_TOKEN. Also copy the Signing Secret from the Basic Information page into SLACK_SIGNING_SECRET. The signing secret lets your webhook verify that requests really came from Slack.

Step 6: Deploy the Webhook (Node.js)

The full handler below uses the Slack Bolt framework. It validates the request signature, downloads the file Slack stored, streams it to the EasySend API and posts the resulting link back to the channel.

// server.js
import { App, ExpressReceiver } from '@slack/bolt';
import fetch from 'node-fetch';
import FormData from 'form-data';

const receiver = new ExpressReceiver({
  signingSecret: process.env.SLACK_SIGNING_SECRET,
});

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  receiver,
});

async function uploadToEasySend(buffer, filename) {
  const form = new FormData();
  form.append('files[]', buffer, filename);
  const res = await fetch('https://easysend.co/api/v1/upload', {
    method: 'POST',
    body: form,
    headers: form.getHeaders(),
  });
  if (!res.ok) throw new Error('EasySend upload failed: ' + res.status);
  const data = await res.json();
  return 'https://easysend.co' + data.share_url;
}

app.command('/share', async ({ command, ack, respond, client }) => {
  await ack();
  const text = (command.text || '').trim();
  if (!text) {
    await respond({
      response_type: 'ephemeral',
      text: 'Attach a file to the channel first then run /share, or pass a URL.',
    });
    return;
  }
  try {
    const fileRes = await fetch(text);
    const buffer = Buffer.from(await fileRes.arrayBuffer());
    const filename = text.split('/').pop() || 'file';
    const link = await uploadToEasySend(buffer, filename);
    await respond({
      response_type: 'in_channel',
      text: 'Uploaded to EasySend: ' + link,
    });
  } catch (err) {
    await respond({
      response_type: 'ephemeral',
      text: 'Upload failed: ' + err.message,
    });
  }
});

app.event('file_shared', async ({ event, client }) => {
  const info = await client.files.info({ file: event.file_id });
  const fileRes = await fetch(info.file.url_private_download, {
    headers: { Authorization: 'Bearer ' + process.env.SLACK_BOT_TOKEN },
  });
  const buffer = Buffer.from(await fileRes.arrayBuffer());
  const link = await uploadToEasySend(buffer, info.file.name);
  await client.chat.postMessage({
    channel: event.channel_id,
    text: 'Mirror posted to EasySend: ' + link,
  });
});

const port = process.env.PORT || 3000;
receiver.app.listen(port, () => console.log('Slack bot on ' + port));

Install dependencies with npm i @slack/bolt node-fetch form-data and start the server with node server.js. Deploy it on Render, Fly, Railway, Cloud Run or any host that gives you a public HTTPS URL.

Step 7: Python Alternative

If your stack is Python the same flow runs in fewer lines with the Slack Bolt SDK for Python.

# app.py
import os, requests
from slack_bolt import App
from slack_bolt.adapter.flask import SlackRequestHandler
from flask import Flask, request

app = App(token=os.environ['SLACK_BOT_TOKEN'],
          signing_secret=os.environ['SLACK_SIGNING_SECRET'])
flask_app = Flask(__name__)
handler = SlackRequestHandler(app)

def upload(buf, name):
    r = requests.post('https://easysend.co/api/v1/upload',
                      files={'files[]': (name, buf)})
    r.raise_for_status()
    return 'https://easysend.co' + r.json()['share_url']

@app.command('/share')
def share(ack, command, respond):
    ack()
    url = command.get('text', '').strip()
    if not url:
        respond('Pass a URL or attach a file then run /share.')
        return
    buf = requests.get(url).content
    link = upload(buf, url.rsplit('/', 1)[-1] or 'file')
    respond(response_type='in_channel', text='Shared: ' + link)

@flask_app.route('/slack/commands', methods=['POST'])
def commands():
    return handler.handle(request)

if __name__ == '__main__':
    flask_app.run(port=3000)

Step 8: Test the Bot

Invite the bot to a channel with /invite @EasySend. Run /share https://example.com/sample.pdf and confirm a link appears. Drag a file into the channel and check that the file_shared handler mirrors it to an EasySend link. If the bot is silent the most common cause is that the request URL on the slash command does not match your deployed endpoint, or the signing secret is wrong and Bolt is rejecting the request. Watch your server logs during the first test, they will tell you in plain text what went wrong.

Why EasySend for Slack Bots

Production Hardening

Before rolling the bot out to a real workspace add a request body size limit so a 10GB upload does not crash your process, add a per user rate limit to protect your hosting bill, log the resulting EasySend short link to your application logs so you can audit shares later and consider enabling encryption with a workspace level password if your team shares anything sensitive. The API docs cover the encryption parameter, custom URLs and webhook callbacks.

Related reads: build a file upload in 10 minutes, CLI automation, install the prebuilt EasySend Slack bot.

View API Docs

Get notified about new features and tips

No spam. Unsubscribe anytime.

More from the blog

How to Share Large Files for Free in 2026
Feb 10, 2026
E2E Encrypted File Sharing: Why It Matters
Feb 10, 2026
The Developer's Guide to EasySend API
Feb 11, 2026