Connect Slack
Verified against the Slack console, August 2026
@kody/slack talks Slack with a user token. Create your own Slack app with
User Token Scopes, register Kody's redirect URI, then finish on
/connect/oauth. The helpers reject bot-only grants (xoxb- / auth.test with
bot_id and no user_id).
What you get
Once connected, you can ask Kody things like:
- "List the public channels I can see in Slack."
- "Post this deploy summary to #eng-alerts."
- "Find recent messages mentioning the billing outage."
Exact capabilities depend on the User Token Scopes granted at connect time.
Create a user-token Slack app
- Confirm there is no existing user-token Slack connection:
integrationList. A connection namedslackthat only has a bot grant does not count — leave it and use a distinct name such asslack-user. - Create a Slack app at https://api.slack.com/apps with User Token
Scopes. User-token Slack uses
oauth/v2_user+oauth.v2.user.access, not the bot-tokenoauth/v2+oauth.v2.access. - Register the redirect URI
https://kody.codes/connect/oauth. - Ask the user for the client id. They paste the client secret on
/connect/oauth— never in chat. - Open
/connect/oauth(or/connect/oauth?provider=slack-userwhenslackis already a bot grant). Fill:- Name:
slackwhen free, otherwise a distinct name - Authorization URL:
https://slack.com/oauth/v2/authorize - Token URL:
https://slack.com/api/oauth.v2.user.access - Client ID: the Slack app client id
- Client secret: paste on the form
- Scopes: comma-separated User Token Scopes
- PKCE: off (confidential client)
- Allowed hosts:
slack.com,files.slack.com
- Name:
- After they authorize,
./smoke-teston@kody/slack(or the forked package) must report a user identity. A bot token is a failed setup, not a package bug.
See the OAuth guide for query parameters, confidential exchange, and reconnect behavior.
Verify
Run this smoke test in execute after connecting (adjust the integration name
if you chose something other than slack):
import { createAuthenticatedFetch } from 'kody:runtime'
export default async function main() {
const slackFetch = await createAuthenticatedFetch('slack')
const response = await slackFetch('https://slack.com/api/auth.test', {
method: 'POST',
})
if (!response.ok) {
throw new Error(
`Slack smoke test HTTP failed: ${response.status} ${await response.text()}`,
)
}
const data = (await response.json()) as {
ok?: boolean
error?: string
user?: string
user_id?: string
bot_id?: string
team?: string
}
if (!data.ok) {
throw new Error(`Slack auth.test failed: ${data.error ?? 'unknown'}`)
}
if (!data.user_id || data.bot_id) {
throw new Error(
'Slack auth.test returned a bot grant. @kody/slack needs a user token.',
)
}
return { user: data.user, userId: data.user_id, team: data.team }
}After the connection exists, call Slack through
createAuthenticatedFetch('slack') (or the connection name you chose). Do not
store Slack OAuth tokens as named secrets.
Troubleshooting
invalid_auth/token_revoked: reconnect with your/connect/oauthURL.- Missing channels or post failures: the granted User Token Scopes do not cover the API method. Reconnect with a Slack app that includes them.
redirect_urimismatches: the registered redirect must be exactlyhttps://kody.codes/connect/oauth.auth.testshowsbot_idand nouser_id: this is a bot token. Do not retry@kody/slackhelpers. Connect a user-token Slack app instead (use a distinct name such asslack-userifslackis already the bot connection).
Do not
- Do not send the user to
/account/secrets/newfor Slack OAuth tokens. - Do not
secretSetSlack access or refresh tokens. - Do not paste tokens or the Slack client secret in chat.
- Do not treat a bot-token Slack connection as a working
@kody/slacksetup. - Do not invent
xoxp-/xoxb-token collection outside/connect/oauth.