feat: add slack api command#538
Conversation
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 Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
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.
srtaalej
left a comment
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
qq: does this cover retries on both 429 and 503 returns 🤔
There was a problem hiding this comment.
Good catch! This test only covered 429. Commit 12630c8 adds a new test to explicitly cover the 503 case as well. 🙇🏻
srtaalej
left a comment
There was a problem hiding this comment.
left 2 testing-related comments/ questions but this is working great ! ⭐ ⭐ ⭐ such a handy command
zimeg
left a comment
There was a problem hiding this comment.
@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! 🎁
| 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") |
There was a problem hiding this comment.
🏁 praise: Amazing find! I didn't know about this option!
There was a problem hiding this comment.
Yea, I think we could use this in a few other commands to clean up some resolution logic. 👌🏻
| 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 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🦠 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"
}
| if err == nil && token != "" { | ||
| return token, nil | ||
| } | ||
| } |
There was a problem hiding this comment.
| } | |
| } | |
| if err != nil { | |
| return "", err | |
| } |
🗣️ issue: To catch interrupts if the suggestion adjacent seems right?
There was a problem hiding this comment.
👾 ramble: This might also guard against unknown --app flag?
| } | ||
|
|
||
| if flags.include { | ||
| fmt.Fprintf(cmd.OutOrStdout(), "HTTP %d\n", resp.StatusCode) |
There was a problem hiding this comment.
🎨 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...
There was a problem hiding this comment.
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.
|
|
||
| if flags.include { | ||
| fmt.Fprintf(cmd.OutOrStdout(), "HTTP %d\n", resp.StatusCode) | ||
| for key, values := range resp.Header { |
There was a problem hiding this comment.
🧮 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...
There was a problem hiding this comment.
🧪 quibble: I understand these cases cover different expectations but I'm curious if we can use table tests to share setup between most?
There was a problem hiding this comment.
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! 🙇🏻
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.

Changelog
Summary
Adds a new top-level
slack apicommand that lets users call any Slack API method without leaving the terminal.Key features:
--tokenflag--appflagSLACK_BOT_TOKENenv varSLACK_USER_TOKENenv varslack api chat.postMessage channel=C123 text="hi"slack api chat.postMessage '{"channel":"C123","text":"hi"}'--json <string>and--data <string>slack api chat.postMessage --data 'channel=C123 text="hi"'slack api chat.postMessage --json '{"channel":"C123","text":"hi"}'-X)-H)--include)Open Questions
Preview
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
--includeAll Response Headers:2026-05-10-slack-cli-api-6.mov
Testing
slack api --help- shows correct token priority and examplesslack api auth.test --token xoxb-...- returns auth infoslack 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 bodyslack api chat.postMessage '{"channel":"C123","text":"hi"}'- auto-detects JSONslack api auth.test --include- shows HTTP status and response headersslack api auth.test --token xoxb... | cat- outputs compact JSON (non-TTY)go test ./cmd/api/... ./internal/api/...- all unit tests passRequirements