When you need to interact with Slack APIs programmatically, the traditional approach requires creating a Slack app, configuring OAuth scopes, and managing bot tokens. But what if you could skip all that bureaucracy and use your existing browser session? This article shows you how to leverage Slack’s browser session tokens with the official Go SDK.

What Are xoxd and xoxc Tokens?

When you use Slack in your web browser, your session is authenticated using two special tokens:

  • xoxd token: A session cookie token that proves your browser session is authenticated
  • xoxc token: An API token used for making requests on behalf of your user account

Unlike traditional Slack app tokens (xoxb- or xoxp-), these tokens are automatically generated when you log into Slack via your browser and have the same permissions as your user account.

Why Use Browser Session Tokens?

Traditional Approach: Slack App Required

  • Create a Slack app in your workspace
  • Configure OAuth scopes and permissions
  • Go through app installation and approval process
  • Manage bot tokens and user tokens
  • Limited by app-specific permissions

Browser Token Approach: No App Needed

  • ✅ Use your existing user permissions
  • ✅ No app creation or approval process
  • ✅ Access all APIs your user account can access
  • ✅ Works immediately with any Slack workspace you’re a member of
  • ✅ Perfect for personal automation and quick prototyping

How to Extract Your Tokens

Step 1: Open Browser Developer Tools

  1. Open Slack in your web browser
  2. Press F12 to open Developer Tools
  3. Go to the Network tab

Step 2: Make a Slack API Request

  1. Perform any action in Slack (send a message, react to something, etc.)
  2. Look for API requests in the Network tab

Step 3: Extract Tokens

From any API request, you’ll find:

  • xoxd token: In the Cookie header as d=xoxd-...
  • xoxc token: In the request payload as "token":"xoxc-..."

Quick Test with cURL

Before implementing in Go, test your tokens with cURL:

#!/bin/bash

# Your extracted tokens
XOXD_TOKEN="xoxd-YOUR_XOXD_TOKEN_HERE"
XOXC_TOKEN="xoxc-YOUR_XOXC_TOKEN_HERE"

# Test authentication
curl 'https://your-workspace.slack.com/api/auth.test' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -b "d=${XOXD_TOKEN}" \
  --data-raw "token=${XOXC_TOKEN}"

A successful response looks like:

{
  "ok": true,
  "url": "https://your-workspace.slack.com/",
  "team": "Your Team",
  "user": "your.username",
  "team_id": "T1234567",
  "user_id": "U1234567"
}

Go Implementation

Step 1: Install Dependencies

go mod init your-project
go get github.com/slack-go/slack

Step 2: Create Environment File

Create a .env file:

SLACK_XOXD_TOKEN=xoxd-YOUR_XOXD_TOKEN_HERE
SLACK_XOXC_TOKEN=xoxc-YOUR_XOXC_TOKEN_HERE
SLACK_API_URL=https://your-workspace.slack.com/api/

Step 3: Complete Go Implementation

package main

import (
	"bufio"
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/slack-go/slack"
)

// Load environment variables from .env file
func loadEnv(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		parts := strings.SplitN(line, "=", 2)
		if len(parts) == 2 {
			os.Setenv(parts[0], parts[1])
		}
	}

	return scanner.Err()
}

// Custom HTTP client that adds xoxd token as cookie
type customHTTPClient struct {
	client    *http.Client
	xoxdToken string
}

func (c *customHTTPClient) Do(req *http.Request) (*http.Response, error) {
	// Add the xoxd token as a cookie
	if c.xoxdToken != "" {
		req.Header.Set("Cookie", fmt.Sprintf("d=%s", c.xoxdToken))
	}

	// Add browser-like headers
	req.Header.Set("Origin", "https://app.slack.com")
	req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; SlackGoClient/1.0)")

	return c.client.Do(req)
}

func main() {
	// Load environment variables
	if err := loadEnv(".env"); err != nil {
		fmt.Printf("Error loading .env file: %v\n", err)
		return
	}

	// Get tokens and API URL from environment
	xoxdToken := os.Getenv("SLACK_XOXD_TOKEN")
	xoxcToken := os.Getenv("SLACK_XOXC_TOKEN")
	apiURL := os.Getenv("SLACK_API_URL")

	if xoxdToken == "" || xoxcToken == "" || apiURL == "" {
		fmt.Println("Error: SLACK_XOXD_TOKEN, SLACK_XOXC_TOKEN, and SLACK_API_URL must be set in .env file")
		return
	}

	// Create custom HTTP client with cookie support
	customClient := &customHTTPClient{
		client:    &http.Client{},
		xoxdToken: xoxdToken,
	}

	// Create Slack client with custom HTTP client
	api := slack.New(xoxcToken,
		slack.OptionHTTPClient(customClient),
		slack.OptionAPIURL(apiURL),
	)

	// Test authentication
	fmt.Println("Testing authentication...")
	authTest, err := api.AuthTest()
	if err != nil {
		fmt.Printf("Error testing auth: %v\n", err)
		return
	}

	fmt.Printf("✅ Successfully authenticated!\n")
	fmt.Printf("User: %s\n", authTest.User)
	fmt.Printf("Team: %s\n", authTest.Team)
	fmt.Printf("Team ID: %s\n", authTest.TeamID)
	fmt.Printf("User ID: %s\n", authTest.UserID)

	// Example: Get team info
	fmt.Println("\nGetting team info...")
	teamInfo, err := api.GetTeamInfo()
	if err != nil {
		fmt.Printf("Error getting team info: %v\n", err)
		return
	}

	fmt.Printf("✅ Team: %s (Domain: %s)\n", teamInfo.Name, teamInfo.Domain)

	// Example: Get emoji list
	fmt.Println("\nGetting custom emojis...")
	emojis, err := api.GetEmoji()
	if err != nil {
		fmt.Printf("Error getting emojis: %v\n", err)
		return
	}

	fmt.Printf("✅ Found %d custom emojis\n", len(emojis))

	// Example: List channels
	fmt.Println("\nListing public channels...")
	channels, _, err := api.GetConversations(&slack.GetConversationsParameters{
		Types: []string{"public_channel"},
		Limit: 5,
	})
	if err != nil {
		fmt.Printf("Error getting channels: %v\n", err)
		return
	}

	fmt.Printf("✅ Found %d channels (showing first 5):\n", len(channels))
	for _, channel := range channels {
		fmt.Printf("  - #%s (ID: %s)\n", channel.Name, channel.ID)
	}
}

Step 4: Run Your Application

go run main.go

Expected output:

Testing authentication...
✅ Successfully authenticated!
User: your.username
Team: Your Team Name
Team ID: T1234567
User ID: U1234567

Getting team info...
✅ Team: Your Team Name (Domain: your-workspace)

Getting custom emojis...
✅ Found 1,234 custom emojis

Listing public channels...
✅ Found 5 channels (showing first 5):
  - #general (ID: C1234567)
  - #random (ID: C2345678)
  ...

The Magic: Custom HTTP Client

The key to making this work is implementing a custom HTTP client that satisfies the httpClient interface expected by the Slack Go SDK. This client automatically injects the xoxd token as a cookie for every request:

type customHTTPClient struct {
	client    *http.Client
	xoxdToken string
}

func (c *customHTTPClient) Do(req *http.Request) (*http.Response, error) {
	// Add the xoxd token as a cookie
	if c.xoxdToken != "" {
		req.Header.Set("Cookie", fmt.Sprintf("d=%s", c.xoxdToken))
	}

	// Add browser-like headers
	req.Header.Set("Origin", "https://app.slack.com")
	req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; SlackGoClient/1.0)")

	return c.client.Do(req)
}

Then we pass it to the Slack client using the OptionHTTPClient() function:

api := slack.New(xoxcToken,
	slack.OptionHTTPClient(customClient),
	slack.OptionAPIURL(apiURL),
)

Using browser session tokens with the Slack Go SDK provides a powerful way to interact with Slack APIs without the overhead of creating and managing Slack apps. This method leverages your existing user permissions and works immediately with any Slack workspace you have access to.

The combination of xoxd (cookie) and xoxc (API token) provides the same authentication mechanism that your browser uses, making it a reliable approach for automation and integration tasks.

By implementing a custom HTTP client that injects the necessary cookies, we can seamlessly use the official Slack Go SDK with browser session tokens, giving us the best of both worlds: the convenience of browser authentication and the power of a fully-featured SDK.


Remember to keep your tokens secure and be aware that they expire with your browser session!