Back to Blog
Guide

Send a Slack DM From a Bot: channel=U or conversations.open, Tested

A Slack bot can DM a user by passing the user ID as the channel in chat.postMessage, or by opening the DM with conversations.open first. We ran both, recorded where each message landed, and reproduced messages_tab_disabled and 'Sending messages to this app has been turned off'.

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

To send a direct message from a Slack bot, call chat.postMessage with the bot token and put the user's ID (U...) in channel. Slack opens the DM between the bot and that user and posts there. The other way is to call conversations.open with users=U... first and post to the D... ID it returns. We ran both on 1 October 2026 in our test workspace, and both messages landed in the same DM. Our app had the chat:write and im:write bot scopes.

The two ways, side by side

This is the script we ran with slack_sdk:

import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

bot = WebClient(token=os.environ["SLACK_BOT_TOKEN"])
USER_ID = "U0B7L4YK420"

# 1. Post with the user ID as the channel
r1 = bot.chat_postMessage(channel=USER_ID, text="Hi from the bot (channel=U...)")
print("channel=U ->", r1["channel"], r1["ts"])

# 2. Open the DM first, then post to its D ID
im = bot.conversations_open(users=USER_ID)
print("conversations.open ->", im["channel"]["id"], "already_open:", im.get("already_open"))
r2 = bot.chat_postMessage(channel=im["channel"]["id"], text="Hi from the bot (channel=D...)")
print("channel=D ->", r2["channel"], r2["ts"])

# 3. A user ID that does not exist
for call in ("postMessage", "open"):
    try:
        if call == "postMessage":
            bot.chat_postMessage(channel="U0123456789", text="x")
        else:
            bot.conversations_open(users="U0123456789")
    except SlackApiError as e:
        print(call, "unknown user ->", e.response["error"])

Output:

channel=U -> D0C5UMYKNKG 1790840151.022449
conversations.open -> D0C5UMYKNKG already_open: True
channel=D -> D0C5UMYKNKG 1790840151.658939
postMessage unknown user -> channel_not_found
open unknown user -> user_not_found

Both calls returned the same DM ID, D0C5UMYKNKG, the Messages tab of our app. The chat.postMessage response always tells you the D ID, so you can store it from the first post and skip conversations.open.

The app's Messages tab in Slack with three bot messages, including

conversations.open reported already_open: true because Slack had created the DM when we installed the app. With return_im=true it returned more of the channel object:

{
  "ok": true,
  "no_op": true,
  "already_open": true,
  "channel": {
    "id": "D0C5UMYKNKG",
    "is_im": true,
    "user": "U0B7L4YK420"
  }
}

Use conversations.open when you need the D ID before posting, for example to read the DM's history or to post a file there. Otherwise channel=U... is one call fewer.

Other recipients we tried

channel in chat.postMessage (bot token)Result
The user's IDok, posted in the app's DM with the user
The DM's D IDok, same DM
U0123456789, a user that does not existchannel_not_found
USLACKBOTok, a new DM D0C5RDMEZL5
The bot's own user IDok, a new DM D0C5YLT20NM
A deleted bot userok, a new DM D0C6R3WUZ1N

conversations.open was stricter: it returned user_not_found for the made-up ID and cannot_dm_bot for the deleted bot user. The unknown-user case is the one you will hit in practice. channel_not_found from chat.postMessage does not say whether the ID is wrong or the channel is private; our channel_not_found page lists the other causes, and Slack member ID shows where to copy a real U ID.

A user token posts as the person, not the bot. With our user token, chat.postMessage to our own user ID landed in our self-DM, D0B7D4VDCUD, not in the app's DM.

Never appear "away" on Slack again

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

When the user cannot reply

The App Home settings decide whether the DM works at all. We changed them with apps.manifest.update and reloaded Slack:

features.app_homeWhat the user sawBot chat.postMessage to the user
messages_tab_enabled: trueHome, Messages and About tabsok
messages_tab_enabled: falseHome and About only{"ok": false, "error": "messages_tab_disabled"}
messages_tab_enabled: true, messages_tab_read_only_enabled: trueMessages tab without a composerok

With the Messages tab off, the app has no DM tab at all:

The app header in Slack with only the Home and About tabs

In read-only mode the bot's message arrives, and the composer is replaced by one line:

A bot message in the app's Messages tab, and below it the line

So if your users report "Sending messages to this app has been turned off", the app has messages_tab_read_only_enabled on. Turn it off under App Home, or in the manifest, if you want replies. In that mode our user token's own post to the DM returned restricted_action_read_only_channel. The App Home tab guide has the rest of those settings.

FAQ

How do I DM several users at once?

chat.postMessage takes one channel per call, so loop over the user IDs and post once per user. Our test workspace has one human member, so we ran it for one. For posting to a channel instead, see sending a Slack message from Python.

Where does a bot DM show up for the user?

Under Agents & apps in the sidebar, in the app's Messages tab. That is where both of our test messages appeared.

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 Interactivity Payload: Real block_actions JSON and response_url Limits

We clicked a button, a static_select and a datepicker in Slack on 1 October 2026 and logged the block_actions payloads our app received. Then we measured response_url: 5 posts, then used_url; it worked at 29:50 and was dead at 30:10.

Slack Green Team
Guide

Slack unfurl_links and unfurl_media: What Each Flag Does, Tested

We posted a web page, a YouTube video and an image with every combination of unfurl_links and unfurl_media on 1 October 2026, then built a custom preview with link_shared and chat.unfurl. The flags do not split the way the names suggest.

Slack Green Team
Guide

Slack Scheduled Message Didn't Send? 5 Cases We Tested

We scheduled five messages for 4:40 PM on 1 October 2026, then archived, left and deleted their channels and deleted a thread parent before the send time. Two sent, three never did, and Slack gave no notice about the three.

Slack Green Team