Skip to content

Latest commit

 

History

History
475 lines (348 loc) · 12.2 KB

File metadata and controls

475 lines (348 loc) · 12.2 KB

Slack File Upload API Documentation

Overview

This document provides detailed information about the Slack REST APIs used for uploading files to Slack channels.

Background: API Migration

Deprecated Method: files.upload

The original files.upload API method was deprecated on May 16, 2024 and will be completely sunset on November 12, 2025.

Why it was deprecated:

  • Synchronous upload process didn't scale well with large files
  • Single HTTP request had to wait for entire upload and processing
  • Not optimized for modern file sizes and upload patterns

Modern Approach: Three-Step Upload Process

The new approach uses three separate API calls for better performance and scalability:

  1. files.getUploadURLExternal - Request an upload URL
  2. HTTP POST - Upload file content to the URL
  3. files.completeUploadExternal - Finalize and optionally share

API Methods

1. files.getUploadURLExternal

Requests an upload URL and file ID from Slack.

Endpoint: https://slack.com/api/files.getUploadURLExternal

Method: POST

Authentication: Bearer token in Authorization header

Required Scopes: files:write

Request Parameters

Parameter Type Required Description
filename string Yes Name of the file being uploaded
length integer Yes Size of the file in bytes

Example Request

curl --location 'https://slack.com/api/files.getUploadURLExternal' \
  --header 'Authorization: Bearer xoxb-your-token-here' \
  --form 'filename="resume.pdf"' \
  --form 'length="245678"'
import requests

url = "https://slack.com/api/files.getUploadURLExternal"
headers = {"Authorization": "Bearer xoxb-your-token-here"}
data = {
    "filename": "resume.pdf",
    "length": "245678"
}

response = requests.post(url, headers=headers, data=data)
result = response.json()

Success Response

{
  "ok": true,
  "upload_url": "https://files.slack.com/upload/v1/abcdefghijklmnop",
  "file_id": "F012AB3CDE4"
}

Response Fields

Field Type Description
ok boolean Indicates if the request was successful
upload_url string URL to upload the file content to (Step 2)
file_id string Unique identifier for this file

Error Response

{
  "ok": false,
  "error": "invalid_auth"
}

Common Errors

Error Description Solution
invalid_auth Invalid authentication token Check your token is correct and starts with xoxb-
missing_scope Token lacks required scope Add files:write scope to your app
invalid_arguments Missing or invalid parameters Ensure filename and length are provided

2. Upload File Content

Upload the actual file content to the URL received from Step 1.

Endpoint: The upload_url from Step 1 (e.g., https://files.slack.com/upload/v1/...)

Method: POST

Authentication: None required (URL is pre-authenticated)

Content-Type: application/octet-stream

Example Request

curl --location 'https://files.slack.com/upload/v1/abcdefghijklmnop' \
  --header 'Content-Type: application/octet-stream' \
  --data-binary '@resume.pdf'
import requests

upload_url = "https://files.slack.com/upload/v1/abcdefghijklmnop"
headers = {"Content-Type": "application/octet-stream"}

with open("resume.pdf", "rb") as file:
    file_content = file.read()

response = requests.post(upload_url, headers=headers, data=file_content)

Success Response

HTTP Status: 200 OK

OK - 245678

The response body contains "OK" followed by the number of bytes uploaded.

Error Responses

HTTP Status Description
400 Bad Request - Invalid file data
403 Forbidden - Upload URL expired or invalid
413 Payload Too Large - File exceeds size limit

3. files.completeUploadExternal

Finalizes the upload and optionally shares the file to channels.

Endpoint: https://slack.com/api/files.completeUploadExternal

Method: POST

Authentication: Bearer token in Authorization header

Required Scopes: files:write

Request Parameters

Parameter Type Required Description
files JSON array Yes Array of file objects with id and optional title
channel_id string No Channel ID to share the file to
channels string No Comma-separated list of channel IDs
initial_comment string No Message text to accompany the file
thread_ts string No Thread timestamp to share file in a thread

Files Array Format

[
  {
    "id": "F012AB3CDE4",
    "title": "My Resume"
  }
]

Example Request (Share to Channel)

curl --location 'https://slack.com/api/files.completeUploadExternal' \
  --header 'Authorization: Bearer xoxb-your-token-here' \
  --form 'files="[{\"id\":\"F012AB3CDE4\",\"title\":\"My Resume\"}]"' \
  --form 'channel_id="C0123456789"' \
  --form 'initial_comment="Here is my resume for review"'
import requests
import json

url = "https://slack.com/api/files.completeUploadExternal"
headers = {"Authorization": "Bearer xoxb-your-token-here"}

files_data = [{"id": "F012AB3CDE4", "title": "My Resume"}]

data = {
    "files": json.dumps(files_data),
    "channel_id": "C0123456789",
    "initial_comment": "Here is my resume for review"
}

response = requests.post(url, headers=headers, data=data)
result = response.json()

Example Request (Upload Only, No Sharing)

# Omit channel_id to upload without sharing
data = {
    "files": json.dumps([{"id": "F012AB3CDE4", "title": "Private Document"}])
}

Success Response

{
  "ok": true,
  "files": [
    {
      "id": "F012AB3CDE4",
      "created": 1699564800,
      "timestamp": 1699564800,
      "name": "resume.pdf",
      "title": "My Resume",
      "mimetype": "application/pdf",
      "filetype": "pdf",
      "pretty_type": "PDF",
      "user": "U012A3456BC",
      "size": 245678,
      "mode": "hosted",
      "is_external": false,
      "is_public": true,
      "url_private": "https://files.slack.com/files-pri/T012-F012/resume.pdf",
      "url_private_download": "https://files.slack.com/files-pri/T012-F012/download/resume.pdf",
      "permalink": "https://yourworkspace.slack.com/files/U012/F012/resume.pdf",
      "channels": ["C0123456789"],
      "shares": {
        "public": {
          "C0123456789": [
            {
              "ts": "1699564800.123456"
            }
          ]
        }
      }
    }
  ]
}

Response Fields

Field Type Description
ok boolean Indicates if the request was successful
files array Array of file objects with detailed information
files[].id string File ID
files[].name string Original filename
files[].title string Display title
files[].size integer File size in bytes
files[].mimetype string MIME type of the file
files[].permalink string Permanent link to the file
files[].channels array List of channel IDs where file is shared

Common Errors

Error Description Solution
invalid_auth Invalid authentication token Verify your token
file_not_found File ID doesn't exist Check the file_id from Step 1
channel_not_found Channel ID is invalid Verify channel ID and bot membership
not_in_channel Bot not in the specified channel Invite bot to channel first

Complete Upload Flow

Python Example

import os
import json
import requests

class SlackFileUploader:
    BASE_URL = "https://slack.com/api"
    
    def __init__(self, token):
        self.token = token
        self.headers = {"Authorization": f"Bearer {token}"}
    
    def upload_file(self, file_path, channel_id, title=None, comment=None):
        # Get file info
        filename = os.path.basename(file_path)
        file_size = os.path.getsize(file_path)
        
        # Step 1: Get upload URL
        response = requests.post(
            f"{self.BASE_URL}/files.getUploadURLExternal",
            headers=self.headers,
            data={"filename": filename, "length": file_size}
        )
        result = response.json()
        upload_url = result["upload_url"]
        file_id = result["file_id"]
        
        # Step 2: Upload file content
        with open(file_path, "rb") as f:
            requests.post(
                upload_url,
                headers={"Content-Type": "application/octet-stream"},
                data=f.read()
            )
        
        # Step 3: Complete upload
        files_data = [{"id": file_id, "title": title or filename}]
        data = {"files": json.dumps(files_data)}
        
        if channel_id:
            data["channel_id"] = channel_id
        if comment:
            data["initial_comment"] = comment
        
        response = requests.post(
            f"{self.BASE_URL}/files.completeUploadExternal",
            headers=self.headers,
            data=data
        )
        
        return response.json()

# Usage
uploader = SlackFileUploader("xoxb-your-token")
result = uploader.upload_file(
    file_path="resume.pdf",
    channel_id="C0123456789",
    title="My Resume",
    comment="Please review my resume"
)

Rate Limits

Slack API uses rate limiting to ensure fair usage:

  • Tier 3 methods (including file upload methods): 20+ requests per minute
  • Rate limits are per workspace and per token
  • Exceeding limits returns HTTP 429 with Retry-After header

Best Practices

  1. Implement exponential backoff for retries
  2. Cache upload URLs if uploading multiple files
  3. Batch operations when possible
  4. Monitor rate limit headers in responses

File Size Limits

Plan Type Maximum File Size
Free 1 GB
Pro 5 GB
Business+ 20 GB
Enterprise Grid 20 GB

Security Considerations

Token Security

  1. Use Bot Tokens (xoxb-) instead of user tokens
  2. Never commit tokens to version control
  3. Store in environment variables or secure vaults
  4. Rotate tokens regularly
  5. Use minimum required scopes

File Security

  1. Files are scanned for malware before being made available
  2. Larger files may take longer to scan
  3. Private files (not shared to channels) are only accessible via direct link
  4. Public files are accessible to all workspace members

Troubleshooting

File Upload Fails at Step 2

Symptoms: HTTP 403 or 400 when uploading to the URL

Causes:

  • Upload URL expired (valid for limited time)
  • File size doesn't match the length parameter from Step 1
  • Network interruption during upload

Solutions:

  • Restart the process from Step 1
  • Verify file size is correct
  • Implement retry logic with fresh URLs

File Doesn't Appear in Channel

Symptoms: Upload succeeds but file not visible in channel

Causes:

  • channel_id not provided in Step 3
  • Bot not a member of the channel
  • Channel ID is incorrect

Solutions:

  • Verify you're passing channel_id parameter
  • Invite bot to channel: /invite @YourBot
  • Double-check channel ID format (starts with C)

"missing_scope" Error

Symptoms: API returns missing_scope error

Solutions:

  1. Go to api.slack.com/apps
  2. Select your app
  3. Navigate to "OAuth & Permissions"
  4. Add files:write scope under "Bot Token Scopes"
  5. Reinstall app to workspace
  6. Use the new token

Additional Resources


Changelog

November 2024

  • files.upload sunset date extended to November 12, 2025

May 2024

  • New apps can no longer use files.upload
  • files.getUploadURLExternal and files.completeUploadExternal become standard

April 2024

  • Announcement of files.upload deprecation
  • Introduction of new upload flow