Back to Blog
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
October 1, 2026
October 1, 2026
3 min read
Share:
slack api
developers
python

unfurl_links and unfurl_media are chat.postMessage flags that control the link previews under a bot's message. In our tests on 1 October 2026, a bot message with neither flag set unfurled every link. Setting unfurl_links: false removed the preview for every kind of link we tried, including images and YouTube. Setting unfurl_media: false removed the preview for YouTube and for a direct image link, but not for a normal web page. To show your own preview for your own domain, subscribe to link_shared and answer with chat.unfurl; that part is further down.

We posted each URL from a bot token in a fresh test channel, waited 20 seconds, and read the attachments of each message with conversations.history. The previews are what Slack web showed.

Every combination, measured

Flags on chat.postMessageWeb page (github.com)YouTube videoDirect .png link
none (defaults)previewpreview with playerimage
unfurl_links: true, unfurl_media: truepreviewpreview with playerimage
unfurl_links: true, unfurl_media: falsepreviewnonenone
unfurl_links: false, unfurl_media: truenonenonenone
unfurl_links: false, unfurl_media: falsenonenonenone
Five bot messages with the same GitHub link: the first three show the GitHub preview card, the two with unfurl_links false show none

The row people get wrong is unfurl_links: false, unfurl_media: true. You might expect it to keep images and videos and drop page cards. It dropped everything. We ran that row again in a new channel, first for the image and then for the video, and once with form-encoded "false" strings instead of JSON booleans. Still no preview. Sending only unfurl_media: true gave the image preview back.

Bot messages with a direct image link: shown with both flags true, and no image when either flag is false

So:

  • • To post with no previews at all, send unfurl_links: false.
  • • To keep page cards but drop images and video players, send unfurl_media: false.
  • • There is no flag combination that keeps images and videos but drops page cards.
  • These flags only change messages your app posts. A person who wants to hide previews in their own Slack uses the Preferences settings in how to turn off link previews in Slack.

    Custom previews with link_shared and chat.unfurl

    For links to a domain you own, your app can draw the preview itself. The app needs:

  • • the domain under App unfurl domains (in the manifest: features.unfurl_domains);
  • • the links:read scope and the link_shared bot event;
  • • the links:write scope to call chat.unfurl.
  • When a person posted a slack.green link, our Socket Mode app received this event (the envelope and its verification token left out):

    {
      "type": "link_shared",
      "user": "U0B7L4YK420",
      "channel": "C0C5QDNUJ7M",
      "message_ts": "1790838686.165969",
      "links": [
        {
          "url": "https://slack.green/en/blog/how-to-turn-off-link-previews-in-slack",
          "domain": "slack.green"
        }
      ],
      "source": "conversations_history",
      "unfurl_id": "C0C5QDNUJ7M.1790838686.165969.8a41eb552b7c0409ff60e6928bc15987e3ea529f4070a0e0de431be274d5a766",
      "is_bot_user_member": true
    }

    This is the handler we ran with Bolt for Python:

    @app.event("link_shared")
    def on_link(event, client):
        unfurls = {}
        for link in event["links"]:
            unfurls[link["url"]] = {"blocks": [
                {"type": "section", "text": {"type": "mrkdwn",
                 "text": f"*Custom preview from the app*\n<{link['url']}|{link['url'].split('/')[-1]}>"}},
                {"type": "context", "elements": [{"type": "mrkdwn",
                 "text": "Unfurled by chat.unfurl from a link_shared event"}]},
            ]}
        client.chat_unfurl(channel=event["channel"], ts=event["message_ts"], unfurls=unfurls,
                           source=event.get("source"), unfurl_id=event.get("unfurl_id"))

    chat.unfurl returned {"ok": true} and the preview replaced Slack's own card for that link:

    A person's message with a slack.green link, and under it the custom preview posted by the app with chat.unfurl

    Three things we saw that the reference does not spell out:

  • • When the app ignored a link_shared event, the message got no preview at all. Slack did not fall back to its own card for the domain, even after 30 minutes.
  • • When the bot itself posted a slack.green link, no link_shared event came, and there was no preview either.
  • • Wrong input to chat.unfurl gave these errors:
  • Our mistakeError
    A URL that is not in the messagecannot_unfurl_url
    The right channel, a ts that does not existcannot_find_message
    unfurls that is not valid JSONinvalid_unfurls_format

    Setting up the bot token and the Socket Mode connection is covered in Slack bot token and Slack Socket Mode in Python.

Never appear "away" on Slack again

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

  • • unfurl_links: false in the post, or unfurl_media: false for an image or a video link. See the table.
  • • The link's domain is registered to an app that does not answer link_shared. In our test that link stayed bare.
  • • The same URL was already unfurled in that channel. Our bot posted the GitHub link again about 40 minutes after the first tests, with default flags, and it got no preview. A different GitHub URL posted at the same time did.
  • FAQ

    Not web pages, by default. A new GitHub link sent through our app's incoming webhook with no flags showed no preview, the opposite of chat.postMessage. The same kind of link with "unfurl_links": true in the webhook JSON got the preview. A legacy webhook behaved the same way. Webhook setup is in how to create a Slack webhook.

    Can I remove a preview after the message is posted?

    Not with the flags. We called chat.update on a bot message that had a GitHub preview, with the same text and unfurl_links: false. It returned ok and the preview stayed. Updating messages is covered in Slack chat.update, tested.

    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 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
    Guide

    Slack Bot Not Responding to @Mentions? 9 Causes We Reproduced

    We broke a Slack bot nine ways on 1 October 2026, in Socket Mode and over HTTP, and wrote down what the person in Slack saw and what the app received each time. The table of causes, symptoms and fixes comes from those runs.

    Slack Green Team