Back to Blog
Guide

Slack Socket Mode in Python: A Bolt App and the Token Errors

A 13-line Bolt for Python app that answers in Slack over Socket Mode, run against a real workspace, plus the exact errors from the wrong token, a missing connections:write scope, and two copies running at once.

Slack Green Team
October 1, 2026
October 5, 2026
5 min read
Share:
slack api
developers
python

Socket Mode lets a Slack app receive events over a WebSocket that your code opens, so you do not need a public HTTPS URL. In Python it takes two tokens: an app-level token that starts with xapp- and has the connections:write scope, which opens the socket, and the usual xoxb- bot token, which calls the Web API. We built a test app in our own Slack workspace on 1 October 2026, ran the script below, and then broke it on purpose to collect the errors. Socket Mode is also the way off the old RTM API, which a new app's tokens cannot open at all; see Slack RTM API deprecated.

Get the xapp- token

  • At api.slack.com/apps, open your app and go to Socket Mode. Turn on Enable Socket Mode.
  • Go to Basic Information, scroll to App-Level Tokens and click Generate Token and Scopes.
  • Name the token, click Add Scope, pick connections:write, and click Generate.
  • Under Event Subscriptions, subscribe to the bot events you need. For the script below that is message.channels, plus the channels:history scope.
  • Install the app and copy the Bot User OAuth Token (xoxb-) from OAuth & Permissions.
  • The app-level token dialog in Slack app settings, with the xapp- token masked and the connections:write scope

    The app-level token belongs to the app, not to a workspace install. It is not on the OAuth page with the other tokens, which is why people search for where it is. Each app can hold up to 10 of them. The other prefixes are explained in Slack bot token.

    A Bolt for Python app that answers over Socket Mode

    import os, logging
    from slack_bolt import App
    from slack_bolt.adapter.socket_mode import SocketModeHandler
    
    logging.basicConfig(level=logging.INFO)
    app = App(token=os.environ["SLACK_BOT_TOKEN"])
    
    @app.message("hello")
    def say_hello(message, say):
        say(f"Hi <@{message['user']}>, this reply came over Socket Mode.")
    
    if __name__ == "__main__":
        SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
    pip install slack_bolt
    export SLACK_BOT_TOKEN=xoxb-...
    export SLACK_APP_TOKEN=xapp-...
    python app.py

    The console log from our run (Python 3.14, slack_bolt from PyPI that day):

    INFO:slack_bolt.App:A new session has been established (session id: 0ba27433-249d-4468-b25d-cacfa5a55aac)
    INFO:slack_bolt.App:⚡️ Bolt app is running!
    INFO:slack_bolt.App:Starting to receive messages from a new connection (session id: 0ba27433-249d-4468-b25d-cacfa5a55aac)

    We typed hello in a test channel the bot was in. The reply came 0.54 seconds later, measured from the two message timestamps:

    In Slack, the user typed hello and the w1test app answered "Hi @sieun, this reply came over Socket Mode."

    The bot also logged a 404 unhandled request warning for the channel_join message when it joined the channel. That warning is harmless. It means no listener matched that event.

    Errors from the wrong token

    We changed one thing at a time and ran the same script.

    What we changedWhere it failedError
    xoxb- bot token as SLACK_APP_TOKENAt start, in apps.connections.opennot_allowed_token_type
    xoxp- user token as SLACK_APP_TOKENAt startnot_allowed_token_type
    xapp- token with only authorizations:readAt startmissing_scope, needed connections:write
    xapp- token as SLACK_BOT_TOKENOnly when it tried to replynot_allowed_token_type from chat.postMessage

    The scope error, as Bolt printed it before the traceback:

    ERROR:slack_bolt.App:Failed to retrieve WSS URL: The request to the Slack API failed. (url: https://slack.com/api/apps.connections.open)
    The server responded with: {'ok': False, 'error': 'missing_scope', 'needed': 'connections:write', 'provided': 'authorizations:read'}

    The last row is the one that wastes time. With the two tokens swapped into each other's place, the app still logged "Bolt app is running!" and received our message. auth.test accepts an xapp- token and answers {"ok": true, "app_name": "w1 test 1001", "app_id": "A0C5NDV7U0K"}, so nothing fails at startup. The error came only when say() called chat.postMessage. If the app connects but never answers, check which token is in which variable first. A missing scope on the bot token gives a different error, covered in missing_scope.

Never appear "away" on Slack again

Cloud-based. No downloads. Works 24/7 even when your laptop is off.

Why Socket Mode seems not to work

Two more things we measured:

  • • Two copies of the app running. We started the same script twice with the same tokens and sent 8 messages. Each message got exactly one reply: 5 from the first process and 3 from the second. Slack sends each event to one open connection only. An old process still running in another terminal, or on a server, takes part of your events, so your new code seems to miss messages.
  • • The app was offline. We posted hello while offline with no connection open, then started the app 5 seconds later. No reply came in the first 40 seconds. The reply arrived 61 seconds after the message, when Slack retried the delivery, the same 1-minute step we measured for Events API retries. Do not assume an event is lost after a restart, and make handlers safe to run twice.
  • Also check the usual causes: the bot must be in the channel to get message.channels events, and each event type must be ticked under Event Subscriptions. Edits, deletes and link previews arrive as message events too; the subtypes we logged are in Slack message_changed events. Socket Mode only replaces the request URL. For the HTTP side, see Slack Bolt with FastAPI, where we timed the 3-second ack limit.

    Socket Mode vs HTTP request URLs

    Socket ModeHTTP request URL
    What we set upAn xapp- token with connections:write, then one switchA public HTTPS URL that answers Slack's challenge request (what passes and fails)
    Request URL fieldNot needed. The settings page says so: "Socket Mode is enabled. You won't need to specify a Request URL."Required for events, interactivity and each slash command
    Runs behind a firewall or on a laptopYes, it only opens outbound connectionsNeeds a tunnel or a server
    Slack MarketplaceNot allowed, per Slack's docsRequired

    Socket Mode fits internal tools and local development. The same listeners over an HTTP request URL, with Flask and with AsyncApp, are on Slack Bolt for Python example. If no Socket Mode process is connected, a slash command fails with dispatch_failed. The same app can also send buttons and slash command replies that only one person sees; we tested those in Slack ephemeral messages.

    Outside Python, slack-go has a socketmode package that we ran on the Slack API in Go, while slack-ruby-client 3.2.0 has no Socket Mode client.

    FAQ

    Do I still need a signing secret with Socket Mode? No. Slack sends events over the socket your app opened with its own token, so there is no incoming HTTP request to verify. Our script never used the signing secret.

    Does the xapp- token stop working when the app is uninstalled? Not while the app exists. After we uninstalled our test app, apps.connections.open with the same xapp- token still returned a WebSocket URL. After we deleted the app, auth.test with it returned invalid_auth.

    Always Active

    Stop Jiggling Your Mouse.

    Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.

    Related Articles

    Guide

    Slack Daily Standup Without a Paid Bot: /remind, Workflow Builder and a Script

    We set up a weekday 9:30 standup in a free Slack workspace three ways: a /remind reminder, Workflow Builder, and a short script that posts the questions and collects the thread replies.

    Slack Green Team
    Guide

    Slack Tabs Under a Channel Name: What Each One Is and How to Edit Them

    We added, renamed, moved, hid and deleted the tabs under a channel name in Slack web on the Free plan, and compared them with the tabs in a DM.

    Slack Green Team
    Guide

    Leave Multiple Slack Channels at Once: 4 Ways Timed, Plus a Script

    Slack has no bulk leave button. We made 12 test channels and left them with the sidebar menu, the channel browser, /leave and a 30-line script, and timed each way.

    Slack Green Team