Discord Activities: Launch OpenClaw HTML Widgets in Discord Channels

Learn how to publish interactive, self-contained HTML widgets into Discord channels using Discord Activities. This guide covers prerequisites, setup, and configuration for OpenClaw agents.

Read this when

  • Setting up or troubleshooting Discord Activity widgets

Discord Activities allow an agent to publish an interactive, self-contained HTML widget directly into the current Discord channel. The message includes an Open widget button; selecting it opens the widget inside Discord.

This feature is disabled by default. OpenClaw only registers the Activity HTTP routes, the show_widget agent tool, and the launch button handler when channels.discord.activities is set and a client secret is resolved. The deprecated discord_widget alias remains available for one release cycle.

Prerequisites

  • A working OpenClaw Discord bot
  • A public HTTPS hostname that can reach the OpenClaw gateway
  • Permission to configure Activities and OAuth2 for the bot's Discord application

Any HTTPS reverse proxy or tunnel works. A named Cloudflare Tunnel provides a stable hostname without directly exposing the gateway port.

# ~/.cloudflared/config.yml
tunnel: openclaw-discord
credentials-file: /home/you/.cloudflared/TUNNEL-ID.json
ingress:
  - hostname: openclaw.example.com
    service: http://127.0.0.1:18789
  - service: http_status:404
cloudflared tunnel login
cloudflared tunnel create openclaw-discord
cloudflared tunnel route dns openclaw-discord openclaw.example.com
cloudflared tunnel run openclaw-discord

Keep standard gateway authentication enabled. Only the Activity prefix is public, and the plugin handles OAuth validation, Activity instance membership, channel binding, sessions, and one-time document capabilities on its own.

Setup

Expose the gateway over HTTPS

Launch your tunnel or reverse proxy and confirm that https://openclaw.example.com/discord/activity/ reaches the gateway after Activities configuration is added. Replace the example hostname with your own.

Enable Activities in Discord

Open the existing bot application in the Discord Developer Portal. Go to Activities, enable Activities, and create a URL mapping:

  • prefix: ROOT (/)
  • target: openclaw.example.com/discord/activity

The target is the public hostname followed by /discord/activity, without a trailing slash.

Copy the OAuth2 client secret

Navigate to OAuth2 in the Developer Portal. Discord requires at least one redirect URI, so add a local placeholder like the loopback address if the application has none yet; the Embedded App SDK manages the Activity return flow. Copy or reset the application client secret. Treat this as a credential: do not paste it into chat, logs, or a committed configuration file.

Configure OpenClaw

Add one block to the Discord account that should offer widgets:

{
  channels: {
    discord: {
      token: "${DISCORD_BOT_TOKEN}",
      activities: {
        clientSecret: "${DISCORD_CLIENT_SECRET}",
        // Optional. Defaults to the bot application ID learned at startup.
        applicationId: "YOUR_DISCORD_APPLICATION_ID",
      },
    },
  },
}

You can omit clientSecret from the block when DISCORD_CLIENT_SECRET is set. The block itself must remain present to opt in.

Normal Discord access settings stay separate. For instance, allowFrom still determines who can DM the agent; it does not control who can open a widget already posted in a channel.

Restart and test

Restart the gateway. In a Discord conversation, ask the agent to display an interactive widget. The agent calls show_widget; click Open widget on the posted message.

Security model

  • OAuth identifies the Discord user before widget metadata is returned.
  • Discord's Get Activity Instance API must confirm that the OAuth user is present in the current Activity instance. The instance channel must match the channel where the widget was posted.
  • Anyone Discord allows into that channel can open its widgets. To restrict the audience, use Discord channel permissions. OpenClaw command and DM allowlists do not grant or remove access to already-posted channel content.
  • OAuth sessions expire after 15 minutes. Widget document capabilities expire after 60 seconds and work once.
  • Widgets expire after seven days, with a maximum of 64 retained per Discord plugin instance.
  • Widget HTML is authored by your agent and should be treated as trusted content. Do not embed secrets you would not want a buggy widget to expose.
  • The widget can navigate within its own nested frame. The sandbox="allow-scripts" iframe blocks top-level navigation, popups, and same-origin access, while its Content Security Policy blocks network connections and external resources. These controls are defense-in-depth, not a security boundary against the agent that authored the widget.
  • When Activities is disabled, /discord/activity is not registered at all.

The public Activity shell and token-exchange route become reachable through your tunnel when enabled. They do not expose widget HTML without a valid OAuth session and one-time document capability.

Troubleshooting

The Activity says “Gateway offline”

  • verify the tunnel is running and routes to the gateway's actual bind port
  • verify the Developer Portal target includes /discord/activity
  • restart the gateway after changing Discord or OpenClaw configuration
  • check gateway logs for the one-line warning about a missing Activities client secret

Discord opens a blank page or reports blocked:csp

  • verify the URL mapping uses ROOT and does not add a second /discord/activity segment
  • confirm the shell, shell.js, and SDK module all return through the Discord proxy
  • inspect gateway logs for requests under /discord/activity/

Widget network requests are intentionally blocked. Inline all CSS, JavaScript, images, and data needed by the widget.

“Widget unavailable”

Launch the button from the channel where the agent posted it. OpenClaw tracks launches server-side when clicked, so a fresh launch record can resolve the exact widget even when Discord omits or mangles the button's custom ID. When neither the custom ID nor a launch record resolves, OpenClaw opens the most recently posted live widget in that channel. Older widgets remain addressable through buttons that preserve their custom ID.

“You cannot launch Activities in this channel”

Discord does not launch Activities from forum-post threads. OpenClaw can post the widget message and button there, but launch the Activity from a regular text channel instead. This restriction comes from Discord, not OpenClaw.