Skip to content

feat: add slack api command#538

Merged
mwbrooks merged 13 commits into
mainfrom
mwbrooks-api-command
May 13, 2026
Merged

feat: add slack api command#538
mwbrooks merged 13 commits into
mainfrom
mwbrooks-api-command

Conversation

@mwbrooks
Copy link
Copy Markdown
Member

@mwbrooks mwbrooks commented May 10, 2026

Changelog

Added slack api <method> [key=value ...] [flags] command for calling any Slack API method directly from the Slack CLI.

Examples:

  • slack api chat.postMessage channel=C123 text="hi"
  • slack api chat.postMessage '{"channel":"C123","text":"hi"}'

Summary

Adds a new top-level slack api command that lets users call any Slack API method without leaving the terminal.

Key features:

  • Token resolution
    1. --token flag
    2. --app flag
    3. SLACK_BOT_TOKEN env var
    4. SLACK_USER_TOKEN env var
    5. App Prompt (only in project)
  • Body Format Auto-detection:
    • Form-Encoded: slack api chat.postMessage channel=C123 text="hi"
    • JSON: slack api chat.postMessage '{"channel":"C123","text":"hi"}'
  • Body Format Flags: --json <string> and --data <string>
    • Form-Encoded: slack api chat.postMessage --data 'channel=C123 text="hi"'
    • JSON: slack api chat.postMessage --json '{"channel":"C123","text":"hi"}'
  • HTTP Options Flags
    • Method override (-X)
    • Custom headers (-H)
    • Show Response headers (--include)
  • Output formatting
    • Pretty-printed JSON in TTY
    • Compact for piped output / scripting
  • Retry support
    • Automatic retry on 429/503 with Retry-After

Open Questions

  • I originally had support for a team prompt, but removed it because it uses our Slack CLI tooling token, which has limited scope access. Sounds good?

Preview

$ slack api auth.test --token xoxb-...
{
    "ok": true,
    "url": "https://myworkspace.slack.com/",
    "team": "My Workspace",
    "user": "bot",
    "team_id": "T12345",
    "user_id": "U12345"
}

Post Message as Form Data (with app selection prompt):

2026-05-10-slack-cli-api-1.mov

Post Message as JSON (with app selection prompt):

2026-05-10-slack-cli-api-2.mov

Post Message with Custom Token --token:

2026-05-10-slack-cli-api-3.mov

JSON Responses are Pretty-Printed in TTY:

2026-05-10-slack-cli-api-5.mov

JSON Responses are Condense in Scripting/Non-TTY:

2026-05-10-slack-cli-api-4.mov

--include All Response Headers:

2026-05-10-slack-cli-api-6.mov

Testing

  • slack api --help - shows correct token priority and examples
  • slack api auth.test --token xoxb-... - returns auth info
  • slack api auth.test --app local - installs app and uses bot token (in project)
  • SLACK_BOT_TOKEN=xoxb-... slack api auth.test - uses env var (outside project)
  • slack api auth.test - prompts interactively when no flags/env set (in project)
  • slack api chat.postMessage channel=C123 text="hi" - sends form-encoded body
  • slack api chat.postMessage '{"channel":"C123","text":"hi"}' - auto-detects JSON
  • slack api auth.test --include - shows HTTP status and response headers
  • slack api auth.test --token xoxb... | cat - outputs compact JSON (non-TTY)
  • go test ./cmd/api/... ./internal/api/... - all unit tests pass

Requirements

Adds a new top-level `slack api <method> [key=value ...] [flags]` command
that calls any Slack API method with automatic token resolution, body
format detection, and response formatting.

Token resolution priority: --token flag, --app/--team flags (via
AppSelectPrompt in project), SLACK_BOT_TOKEN env, SLACK_USER_TOKEN env,
interactive prompt fallback.

Supports form-encoded key=value params, JSON auto-detection, --json and
--data flags, custom headers (-H), HTTP method override (-X), response
header display (--include), and TTY-aware pretty printing.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 10, 2026

Codecov Report

❌ Patch coverage is 81.78138% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.57%. Comparing base (251a341) to head (ee76fd7).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
cmd/api/api.go 86.24% 14 Missing and 12 partials ⚠️
internal/api/raw_request.go 66.66% 11 Missing and 8 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #538      +/-   ##
==========================================
+ Coverage   71.32%   71.57%   +0.25%     
==========================================
  Files         222      224       +2     
  Lines       18714    18961     +247     
==========================================
+ Hits        13347    13571     +224     
- Misses       4187     4191       +4     
- Partials     1180     1199      +19     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mwbrooks mwbrooks self-assigned this May 10, 2026
@mwbrooks mwbrooks added enhancement M-T: A feature request for new functionality changelog Use on updates to be included in the release notes semver:minor Use on pull requests to describe the release version increment labels May 10, 2026
@mwbrooks mwbrooks added this to the Next Release milestone May 10, 2026
mwbrooks added 5 commits May 10, 2026 16:46
Token resolution now errors if no token is found rather than falling
back to a workspace selection prompt. Users must provide a token via
--token, --app, or SLACK_BOT_TOKEN/SLACK_USER_TOKEN env vars.
@mwbrooks mwbrooks marked this pull request as ready for review May 11, 2026 01:25
@mwbrooks mwbrooks requested a review from a team as a code owner May 11, 2026 01:25
Copy link
Copy Markdown
Contributor

@srtaalej srtaalej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great! ⭐ thanks for adding the test commands in the description 🫂
the command is working great except

lack api auth.test | cat

the test implies there should be JSON output but slack api auth.test is an interactive prompt so it errors out instead. perhaps we update the pr description? the command should be
lack api auth.test --token xoxb-... | cat
lack api auth.test --app A... | cat
right?

attempts++
if attempts == 1 {
w.Header().Set("Retry-After", "1")
w.WriteHeader(http.StatusTooManyRequests)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qq: does this cover retries on both 429 and 503 returns 🤔

Copy link
Copy Markdown
Member Author

@mwbrooks mwbrooks May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! This test only covered 429. Commit 12630c8 adds a new test to explicitly cover the 503 case as well. 🙇🏻

Copy link
Copy Markdown
Contributor

@srtaalej srtaalej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left 2 testing-related comments/ questions but this is working great ! ⭐ ⭐ ⭐ such a handy command

Copy link
Copy Markdown
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mwbrooks Top tier PR! Incredible! 🏆 ✨

I'm eager to approve this with good findings in tests. But a few comments follow around token selection and outputs. I understand we have planned follow up but also want to suggest a workaround without tokens for the api.test method if that's not needed?

So excited to find these changes! 🎁

Comment thread cmd/api/api.go
cmd.Flags().StringVar(&flags.data, "data", "", "form-encoded request body string (e.g. \"key1=val1&key2=val2\")")
cmd.Flags().StringSliceVarP(&flags.headers, "header", "H", nil, "additional HTTP headers (format: \"Key: Value\")")
cmd.Flags().BoolVarP(&flags.include, "include", "i", false, "include HTTP status code and response headers in output")
cmd.MarkFlagsMutuallyExclusive("json", "data")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏁 praise: Amazing find! I didn't know about this option!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I think we could use this in a few other commands to clean up some resolution logic. 👌🏻

Comment thread cmd/api/api.go Outdated
Comment on lines +227 to +235
if sdkConfigExists, _ := clients.SDKConfig.Exists(); sdkConfigExists {
selected, err := prompts.AppSelectPrompt(ctx, clients, prompts.ShowAllEnvironments, prompts.ShowInstalledAppsOnly)
if err == nil && selected.App.AppID != "" {
token, err := installAndGetBotToken(ctx, clients, selected)
if err == nil && token != "" {
return token, nil
}
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦠 issue: We might want to check environment variables before prompting? I find this can be blocking from a project:

$ SLACK_BOT_TOKEN="xoxb" slack api chat.postMessage
┃ Select an app
┃ ❱ A0B3AEPJUAH devrelsandbox T038J6TH5PF

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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic catch @zimeg! 🙇🏻 Commit 6891a56 fixes the resolution order and adds a test to check the order.

# Inside a project
$ SLACK_BOT_TOKEN="xoxb..." slack api auth.test

Comment thread cmd/api/api.go
if err == nil && token != "" {
return token, nil
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}
if err != nil {
return "", err
}

🗣️ issue: To catch interrupts if the suggestion adjacent seems right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👾 ramble: This might also guard against unknown --app flag?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good find @zimeg! I'm glad we caught this before shipping it 🙇🏻 Commit ab3a387 adds a variation of your suggestion and test coverage.

Comment thread cmd/api/api.go
}

if flags.include {
fmt.Fprintf(cmd.OutOrStdout(), "HTTP %d\n", resp.StatusCode)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 quibble: I forgot how newlines are formatted but we might prefer outputs through IO with:

clients.IO.PrintInfo

Although I forget if that appends to debug logs or not...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for thinking of this. I looked into it and clients.IO.PrintInfo unfortunately has a lot of overhead - it adds a newline which makes non-TTY formatting a little less parsable and it adds tracing overhead but we don't want these responses in our logs.

I think we'll keep it as-is. But ultimately, we should really offer a cleaner, simpler client.IO print that doesn't do so much.

Comment thread cmd/api/api.go Outdated

if flags.include {
fmt.Fprintf(cmd.OutOrStdout(), "HTTP %d\n", resp.StatusCode)
for key, values := range resp.Header {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧮 suggestion: Let's sort the request and response headers of this more verbose output! I'm finding these change each time and I think that's a go internal...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Great catch @zimeg! 🙇🏻 The headers are now sorted and we have a test 🔠 🧪 Commit ad581a7.

Comment thread cmd/api/api_test.go
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 quibble: I understand these cases cover different expectations but I'm curious if we can use table tests to share setup between most?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very fair point and thanks for noticing. I've consolidated the body format tests in commit e69776c. However, I don't think we want to use table tests for the other areas - there isn't enough overlap and it becomes quite messy to read. This is a big improvement though! 🙇🏻

mwbrooks added 6 commits May 12, 2026 16:29
Environment variables (SLACK_BOT_TOKEN, SLACK_USER_TOKEN) now take
priority over the interactive app selection prompt, preventing
unexpected blocking prompts when a token is already available.
Interrupts (Ctrl+C) and app-not-found errors from the app selection
prompt now surface immediately rather than falling through to a
generic "no token found" message.
Go map iteration is non-deterministic, so headers printed with
--include would appear in a different order on each run. Sort them
alphabetically for consistent output.
Combines FormEncoded, JSONAutoDetect, JSONFlag, DataFlag, and
GETMethod tests into a single Test_runAPICommand_BodyFormats table
test with shared setup.
@mwbrooks
Copy link
Copy Markdown
Member Author

Thanks for the reviews @srtaalej and @zimeg! Solid feedback from both of you 🙇🏻 I've followed up on everything and will merge the PR now. 🚀

@mwbrooks mwbrooks merged commit ace60ea into main May 13, 2026
9 checks passed
@mwbrooks mwbrooks deleted the mwbrooks-api-command branch May 13, 2026 00:32
@zimeg
Copy link
Copy Markdown
Member

zimeg commented May 13, 2026

OIP-2109305485

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

Labels

changelog Use on updates to be included in the release notes enhancement M-T: A feature request for new functionality semver:minor Use on pull requests to describe the release version increment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants