Skip to main content

Channels

Discord

Post to Discord from your app with discord() — one webhook URL, zero config, the same shape as mail() and sms().


import { discord } from "postboi"

await discord({ message: "New release is live 🎉" })
import { discord } from "postboi"

await discord({ message: "New release is live 🎉" })

Same shape as Slack: the webhook URL carries the destination channel, so one env var is the whole setup.

Setup

The fast way: bunx postboi init --chat, pick Discord, choose Connect in the browser. Discord’s own consent screen asks which server and channel to post to, and the webhook lands in your env file without you ever seeing it. Signed in to Postboi, it also syncs to your team, so nobody else sets it up at all.

Or create a webhook yourself under Server Settings → Integrations → Webhooks (or a channel’s own settings), and paste it:

# .env
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/…
# .env
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/…

Treat the URL as a secret — anyone holding it can post to your channel. It’s exactly the kind of credential team sync exists for.

Titles

title renders as bold markdown above the message:

await discord({ title: "Build failed", message: "3 tests failing on main" })
await discord({ title: "Build failed", message: "3 tests failing on main" })

Posting to more than one channel

to overrides the webhook per message:

await discord([
	{ to: process.env.DISCORD_ALERTS, message: "🔴 Checkout is down" },
	{ to: process.env.DISCORD_RELEASES, message: "v1.4.2 is out" },
])
await discord([
	{ to: process.env.DISCORD_ALERTS, message: "🔴 Checkout is down" },
	{ to: process.env.DISCORD_RELEASES, message: "v1.4.2 is out" },
])

Each gets its own result — one failure never loses the rest.

Development posts for real

Unlike SMS, Discord is not intercepted in development: posting to your own server while building is usually the point. With no DISCORD_WEBHOOK_URL configured, messages are captured by the dev inbox (or logged) instead of erroring.

Worth knowing

  • Discord caps a message at 2000 characters. Postboi truncates on code points (an emoji never splits into garbage) rather than letting the whole post be rejected — which matters when you’re piping in a stack trace.
  • In a multi-channel send(), the chat leg posts to whichever platform chat.provider names in your config. Your own code just calls discord().