Repository: slack-go/slack
Pull request #1422 (2025-05-17 20:25:33)

feat!: expose `UploadToURL` (from `uploadToURL`)
I've also added an example on how to do this. You can find it in [./examples/files/multiple_files](./examples/files/multiple_files).

BREAKING CHANGE: renamed `AltText` to `AltTxt` and `SnippetText` to `SnippetType`.


Patch:
diff --git a/examples/files/files.go b/examples/files/files.go
index 0eb136a..1f622ec 100644
--- a/examples/files/files.go
+++ b/examples/files/files.go
@@ -1,30 +1,41 @@
 package main
 
 import (
+	"context"
 	"fmt"
+	"os"
 
 	"github.com/slack-go/slack"
 )
 
 func main() {
-	api := slack.New("YOUR_TOKEN_HERE")
-	params := slack.FileUploadParameters{
-		Title: "Batman Example",
-		//Filetype: "txt",
-		File: "example.txt",
-		//Content:  "Nan Nan Nan Nan Nan Nan Nan Nan Batman",
+	token, ok := os.LookupEnv("SLACK_BOT_TOKEN")
+	if !ok {
+		fmt.Println("Missing SLACK_BOT_TOKEN in environment")
+		os.Exit(1)
 	}
-	file, err := api.UploadFile(params)
+	api := slack.New(token, slack.OptionDebug(true))
+
+	ctx := context.Background()
+
+	// Upload a file
+	params := slack.UploadFileV2Parameters{
+		Title:    "Batman Example",
+		Filename: "example.txt",
+		File:     "example.txt",
+		FileSize: 38,
+	}
+	file, err := api.UploadFileV2Context(ctx, params)
 	if err != nil {
 		fmt.Printf("%s\n", err)
 		return
 	}
-	fmt.Printf("Name: %s, URL: %s\n", file.Name, file.URL)
+	fmt.Printf("ID: %s, title: %s\n", file.ID, file.Title)
 
 	err = api.DeleteFile(file.ID)
 	if err != nil {
 		fmt.Printf("%s\n", err)
 		return
 	}
-	fmt.Printf("File %s deleted successfully.\n", file.Name)
+	fmt.Printf("File %s deleted successfully.\n", file.ID)
 }
diff --git a/examples/files/multiple_files/batman.txt b/examples/files/multiple_files/batman.txt
new file mode 100644
index 0000000..0e3504a
--- /dev/null
+++ b/examples/files/multiple_files/batman.txt
@@ -0,0 +1,1 @@
+Nan Nan Nan Nan Nan Nan Nan Nan Batman
diff --git a/examples/files/multiple_files/multiple_files.go b/examples/files/multiple_files/multiple_files.go
new file mode 100644
index 0000000..87541f2
--- /dev/null
+++ b/examples/files/multiple_files/multiple_files.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"os"
+
+	"github.com/slack-go/slack"
+)
+
+func main() {
+	token, ok := os.LookupEnv("SLACK_BOT_TOKEN")
+	if !ok {
+		fmt.Println("Missing SLACK_BOT_TOKEN in environment")
+		os.Exit(1)
+	}
+	channelID := "CXXXXXXXX" // Replace with your channel ID
+
+	api := slack.New(token, slack.OptionDebug(true))
+	ctx := context.Background()
+
+	files := []slack.UploadFileV2Parameters{
+		{
+			Title:    "Batman Example",
+			Filename: "batman.txt",
+			File:     "batman.txt",
+			FileSize: 39,
+		},
+		{
+			Title:    "Superman Example",
+			Filename: "superman.txt",
+			File:     "superman.txt",
+			FileSize: 37,
+		},
+	}
+
+	uploads := []*slack.GetUploadURLExternalResponse{}
+	filesToComplete := []slack.FileSummary{}
+
+	for _, file := range files {
+		u, err := api.GetUploadURLExternalContext(ctx, slack.GetUploadURLExternalParameters{
+			AltTxt:   "An alt text for superheroes",
+			FileName: file.Filename,
+			FileSize: file.FileSize,
+		})
+		if err != nil {
+			fmt.Printf("%s\n", err)
+			return
+		}
+		uploads = append(uploads, u)
+	}
+
+	for i, file := range files {
+		fmt.Printf("Uploading file %s to %s\n", file.Filename, uploads[i].UploadURL)
+
+		err := api.UploadToURL(ctx, slack.UploadToURLParameters{
+			UploadURL: uploads[i].UploadURL,
+			Filename:  file.Filename,
+			File:      file.File,
+		})
+		if err != nil {
+			fmt.Printf("%s\n", err)
+			return
+		}
+		filesToComplete = append(filesToComplete, slack.FileSummary{
+			ID:    uploads[i].FileID,
+			Title: file.Title,
+		})
+	}
+
+	c, err := api.CompleteUploadExternalContext(ctx, slack.CompleteUploadExternalParameters{
+		Files:   filesToComplete,
+		Channel: channelID,
+	})
+
+	if err != nil {
+		fmt.Printf("%s\n", err)
+		return
+	}
+
+	fmt.Printf("Files uploaded successfully: %+v\n", c.Files)
+}
diff --git a/examples/files/multiple_files/superman.txt b/examples/files/multiple_files/superman.txt
new file mode 100644
index 0000000..62bb723
--- /dev/null
+++ b/examples/files/multiple_files/superman.txt
@@ -0,0 +1,1 @@
+Whoosh Whoosh Whoosh Whoosh Superman
diff --git a/files.go b/files.go
index 866b490..45a39f8 100644
--- a/files.go
+++ b/files.go
@@ -178,14 +178,14 @@ type UploadFileV2Parameters struct {
 	Channel         string
 	ThreadTimestamp string
 	AltTxt          string
-	SnippetText     string
+	SnippetType     string
 }
 
 type GetUploadURLExternalParameters struct {
-	AltText     string
+	AltTxt      string
 	FileSize    int
 	FileName    string
-	SnippetText string
+	SnippetType string
 }
 
 type GetUploadURLExternalResponse struct {
@@ -194,7 +194,7 @@ type GetUploadURLExternalResponse struct {
 	SlackResponse
 }
 
-type uploadToURLParameters struct {
+type UploadToURLParameters struct {
 	UploadURL string
 	Reader    io.Reader
 	File      string
@@ -375,7 +375,11 @@ func (api *Client) ListFilesContext(ctx context.Context, params ListFilesParamet
 
 // UploadFile uploads a file.
 //
-// Deprecated: Use [Client.UploadFileV2] instead. This will stop functioning on March 11, 2025.
+// Deprecated: Use [Client.UploadFileV2] instead.
+//
+// Per Slack Changelog, specifically [https://api.slack.com/changelog#entry-march_2025_1](this entry),
+// this will stop functioning on November 12, 2025.
+//
 // For more details, see: https://api.slack.com/methods/files.upload#markdown
 func (api *Client) UploadFile(params FileUploadParameters) (file *File, err error) {
 	return api.UploadFileContext(context.Background(), params)
@@ -383,7 +387,11 @@ func (api *Client) UploadFile(params FileUploadParameters) (file *File, err erro
 
 // UploadFileContext uploads a file and setting a custom context.
 //
-// Deprecated: Use [Client.UploadFileV2Context] instead. This will stop functioning on March 11, 2025.
+// Deprecated: Use [Client.UploadFileV2Context] instead.
+//
+// Per Slack Changelog, specifically [https://api.slack.com/changelog#entry-march_2025_1](this entry),
+// this will stop functioning on November 12, 2025.
+//
 // For more details, see: https://api.slack.com/methods/files.upload#markdown
 func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParameters) (file *File, err error) {
 	// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
@@ -529,11 +537,11 @@ func (api *Client) GetUploadURLExternalContext(ctx context.Context, params GetUp
 		"filename": {params.FileName},
 		"length":   {strconv.Itoa(params.FileSize)},
 	}
-	if params.AltText != "" {
-		values.Add("initial_comment", params.AltText)
+	if params.AltTxt != "" {
+		values.Add("alt_txt", params.AltTxt)
 	}
-	if params.SnippetText != "" {
-		values.Add("thread_ts", params.SnippetText)
+	if params.SnippetType != "" {
+		values.Add("snippet_type", params.SnippetType)
 	}
 	response := &GetUploadURLExternalResponse{}
 	err := api.postMethod(ctx, "files.getUploadURLExternal", values, response)
@@ -544,8 +552,9 @@ func (api *Client) GetUploadURLExternalContext(ctx context.Context, params GetUp
 	return response, response.Err()
 }
 
-// uploadToURL uploads the file to the provided URL using post method
-func (api *Client) uploadToURL(ctx context.Context, params uploadToURLParameters) (err error) {
+// UploadToURL uploads the file to the provided URL using post method
+// This is not a Slack API method, but a helper function to upload files to the URL
+func (api *Client) UploadToURL(ctx context.Context, params UploadToURLParameters) (err error) {
 	values := url.Values{}
 	if params.Content != "" {
 		contentReader := strings.NewReader(params.Content)
@@ -565,6 +574,7 @@ func (api *Client) CompleteUploadExternalContext(ctx context.Context, params Com
 	if err != nil {
 		return nil, err
 	}
+
 	values := url.Values{
 		"token": {api.token},
 		"files": {string(filesBytes)},
@@ -611,16 +621,16 @@ func (api *Client) UploadFileV2Context(ctx context.Context, params UploadFileV2P
 	}
 
 	u, err := api.GetUploadURLExternalContext(ctx, GetUploadURLExternalParameters{
-		AltText:     params.AltTxt,
+		AltTxt:      params.AltTxt,
 		FileName:    params.Filename,
 		FileSize:    params.FileSize,
-		SnippetText: params.SnippetText,
+		SnippetType: params.SnippetType,
 	})
 	if err != nil {
 		return nil, err
 	}
 
-	err = api.uploadToURL(ctx, uploadToURLParameters{
+	err = api.UploadToURL(ctx, UploadToURLParameters{
 		UploadURL: u.UploadURL,
 		Reader:    params.Reader,
 		File:      params.File,


Test patch:
diff --git a/files_test.go b/files_test.go
index 0fb9b2b..cf3a801 100644
--- a/files_test.go
+++ b/files_test.go
@@ -330,8 +330,8 @@ func TestGetUploadURLExternalContext(t *testing.T) {
 			params: GetUploadURLExternalParameters{
 				FileSize:    10,
 				FileName:    "test.txt",
-				AltText:     "test-alt-text",
-				SnippetText: "test-snippet-text",
+				AltTxt:      "test-alt-text",
+				SnippetType: "test-snippet-type",
 			},
 			wantSlackResponse: []byte(`{"ok":true,"file_id":"RandomID","upload_url":"http://test-server/abc"}`),
 			wantResponse: GetUploadURLExternalResponse{