Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Slack By James Joyner IV · · 9 min read

Slack API Error Guide: 'not_allowed_token_type' Wrong Token for Endpoint

Fix the Slack API not_allowed_token_type error: diagnose bot-vs-user token mismatches, app-level vs Web API tokens, and endpoints that demand a user token, with curl.

  • #slack
  • #troubleshooting
  • #errors
  • #authentication

Overview

The not_allowed_token_type error means the token authenticated correctly, but the endpoint does not accept that kind of token. Slack issues several token types — bot (xoxb-), user (xoxp-), and app-level (xapp-) — and many methods only accept one of them. A common case is an endpoint that requires a user token being called with a bot token, or a Socket-Mode app-level token being used for a Web API call. The credential is valid; it is simply the wrong type for that method.

You will see this in the JSON body:

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

It occurs the moment you call a method whose token-type requirement differs from the token you presented — even though auth.test with the same token succeeds.

Symptoms

  • A specific endpoint returns not_allowed_token_type while others work.
  • auth.test succeeds with the same token, proving it is valid.
  • The failing endpoint is documented as requiring a user token (or vice versa).
  • A Socket-Mode (xapp-) token is being used for HTTP Web API calls.
curl -s -X POST https://slack.com/api/users.profile.set \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"profile":{"status_text":"in a meeting"}}'
{
    "ok": false,
    "error": "not_allowed_token_type"
}

Common Root Causes

1. A user-only endpoint called with a bot token

Methods like users.profile.set, search.messages, and parts of stars.* require a user token. A bot token (xoxb-) is rejected.

curl -s "https://slack.com/api/auth.test" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN"
{
    "ok": true,
    "user_id": "U0BOTBOT01",
    "bot_id": "B0BOTID001"
}

A non-null bot_id confirms this is a bot token — wrong for user-only methods.

2. A bot-only endpoint called with a user token

The reverse also happens: some bot-oriented methods reject xoxp- user tokens.

curl -s "https://slack.com/api/auth.test" \
  -H "Authorization: Bearer $SLACK_USER_TOKEN"
{
    "ok": true,
    "user_id": "U0REALUSER",
    "bot_id": null
}

A null bot_id marks this as a user token.

3. An app-level token used for a Web API call

xapp- tokens exist only to open Socket Mode connections. Passing one to a Web API endpoint fails by type.

printf '%s' "$SLACK_TOKEN" | cut -c1-5
curl -s -X POST https://slack.com/api/chat.postMessage \
  -H "Authorization: Bearer $SLACK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"channel":"C0123456789","text":"hi"}'
xapp-
{
    "ok": false,
    "error": "not_allowed_token_type"
}

4. The wrong token variable wired into the client

Two tokens (bot and user) exist for the same app, and the code passes the wrong env var to a type-restricted method.

printf 'bot=%s user=%s\n' \
  "$(printf '%s' "$SLACK_BOT_TOKEN" | cut -c1-5)" \
  "$(printf '%s' "$SLACK_USER_TOKEN" | cut -c1-5)"
bot=xoxb- user=xoxp-

Confirm which variable the failing call actually uses.

5. The required user scope was never requested, so no user token exists

If the app only has bot scopes, installation never mints a user token, leaving you no valid credential for user-only endpoints.

curl -s "https://slack.com/api/apps.permissions.scopes.list" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN"
{
    "ok": true,
    "scopes": {
        "bot": ["chat:write", "channels:read"],
        "user": []
    }
}

An empty user array means there is no user token to use.

6. Enterprise/org token type mismatch

Some admin.* methods require an org-level user token; a workspace bot token is the wrong type.

curl -s "https://slack.com/api/admin.users.list?team_id=T0AAAAAAA" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN"
{
    "ok": false,
    "error": "not_allowed_token_type"
}

Diagnostic Workflow

Step 1: Identify the token type you are sending

printf '%s' "$SLACK_TOKEN" | cut -c1-5
curl -s "https://slack.com/api/auth.test" -H "Authorization: Bearer $SLACK_TOKEN"

Prefix xoxb-/non-null bot_id = bot; xoxp-/null bot_id = user; xapp- = app-level (Socket Mode only).

Step 2: Determine the endpoint’s required type

Check the method’s docs for the token type it accepts. User-only methods (users.profile.set, search.messages) will never accept a bot token.

Step 3: Swap to the correct token

# Same call, user token instead of bot token
curl -s -X POST https://slack.com/api/users.profile.set \
  -H "Authorization: Bearer $SLACK_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"profile":{"status_text":"in a meeting"}}'
{
    "ok": true,
    "profile": {"status_text": "in a meeting"}
}

Step 4: If no user token exists, add the user scope and reinstall

curl -s "https://slack.com/api/apps.permissions.scopes.list" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN"

If the user scope array is empty, add the needed user scope, reinstall the app, and capture the new xoxp- user token.

Step 5: Verify the corrected call

curl -s "https://slack.com/api/auth.test" -H "Authorization: Bearer $SLACK_USER_TOKEN"
{
    "ok": true,
    "user_id": "U0REALUSER",
    "bot_id": null
}

Example Root Cause Analysis

A status-sync app sets each user’s Slack status from their calendar and fails on every call with not_allowed_token_type. The token is valid:

curl -s "https://slack.com/api/auth.test" -H "Authorization: Bearer $SLACK_TOKEN"
{"ok": true, "user_id": "U0BOTBOT01", "bot_id": "B0BOTID001"}

The non-null bot_id shows the app is sending its bot token, but users.profile.set requires the affected user’s token. The app config also lacks the users.profile:write user scope, so no user token was ever minted:

curl -s "https://slack.com/api/apps.permissions.scopes.list" -H "Authorization: Bearer $SLACK_TOKEN"
{"ok": true, "scopes": {"bot": ["chat:write"], "user": []}}

Fix: add users.profile:write as a user scope, have each user authorize the app (OAuth), store their xoxp- user token, and call users.profile.set with it:

curl -s -X POST https://slack.com/api/users.profile.set \
  -H "Authorization: Bearer $SLACK_USER_TOKEN" -H "Content-Type: application/json" \
  -d '{"profile":{"status_text":"In a meeting"}}'
{"ok": true, "profile": {"status_text": "In a meeting"}}

Prevention Best Practices

  • Check each method’s required token type before integrating; user-only methods never accept a bot token and the error is non-obvious.
  • Keep bot and user tokens in clearly named variables (SLACK_BOT_TOKEN, SLACK_USER_TOKEN) so the wrong one is hard to wire up.
  • Reserve xapp- app-level tokens strictly for opening Socket Mode connections — never for Web API calls.
  • Request user scopes (and run the user OAuth flow) up front when any endpoint needs a user token, so the credential actually exists.
  • Add the token type to integration tests: assert auth.test returns the expected bot_id presence for each code path.
  • For ad-hoc triage, the free incident assistant can spot a bot-vs-user token mismatch from the failing method name. See more in Slack guides.

Quick Command Reference

# What token type am I sending?
printf '%s' "$SLACK_TOKEN" | cut -c1-5
curl -s "https://slack.com/api/auth.test" -H "Authorization: Bearer $SLACK_TOKEN"

# Do I even have a user token?
curl -s "https://slack.com/api/apps.permissions.scopes.list" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN"

# Retry a user-only method with the user token
curl -s -X POST https://slack.com/api/users.profile.set \
  -H "Authorization: Bearer $SLACK_USER_TOKEN" -H "Content-Type: application/json" \
  -d '{"profile":{"status_text":"away"}}'

Conclusion

not_allowed_token_type means the token is valid but the wrong kind for the endpoint. The usual root causes:

  1. A user-only method (e.g. users.profile.set) called with a bot token.
  2. A bot-only method called with a user token.
  3. An xapp- app-level token used for a Web API call.
  4. The wrong token variable wired into a type-restricted call.
  5. No user token exists because the user scope was never requested.
  6. An admin.*/org method requiring an org-level user token.

Identify the token type via its prefix and auth.test, match it to the endpoint’s requirement, and swap (or mint) the correct token — the credential is fine; only its type needs to match.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.