Discord
Discord needs no bot and no application. A webhook url from a channel's settings is the whole credential, and it can do far more than most people expect.
Copy this
Create a webhook
In Discord: Channel settings → Integrations → Webhooks → New Webhook, then Copy Webhook URL.
Configure it
PIGEON_DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/123.../abc...
export default defineNuxtConfig({
modules: ['nuxt-pigeon'],
nuxtPigeon: {
channels: {
discord: true,
},
},
})
Send
export default defineEventHandler(async () => {
const message = await discord.send('Deploy failed', {
embeds: [
{
title: 'main',
description: 'The build step failed after 40s.',
color: 0x00dc82,
},
],
})
return { id: message.id }
})
A green embed appears in your channel.
.env, never in nuxt.config, and errors from this channel deliberately name
Discord instead of the address so it never lands in a log.Every option
| Option | What it does |
|---|---|
embeds | Up to 10, the rich boxes. Colour is decimal, 0x00dc82 |
media | Up to 10 files. Fetched and attached as multipart |
username, avatarUrl | Override the webhook's name and picture per message |
allowedMentions | Passed through untouched, so @everyone can be defused |
threadId | Post into an existing thread |
threadName | Create a thread, in a forum or media channel |
appliedTags | Forum tags |
components | Buttons and selects. Application owned webhooks only |
poll | A poll, passed through untouched |
tts | Read aloud for whoever has that on |
flags | For example 4 to suppress link previews |
wait | Defaults to true, so you get the message back and can edit it |
webhookUrl | Another webhook for this call. A url is a channel, so this is how a second server or channel is reached. edit and delete need it again, it is never kept in the handle |
Images inside an embed
Attach the file, then point the embed at it by name:
await discord.send('The failing step', {
media: [{ url: 'https://example.com/shot.png', filename: 'shot.png' }],
embeds: [{ title: 'Build log', image: { url: 'attachment://shot.png' } }],
})
Spoilers
Only on a real attachment, through the filename:
await discord.send('Careful', {
media: [{ url: '…/shot.png', spoiler: true }],
})
The module renames it to SPOILER_shot.png, which is the only thing Discord looks at.
On an embed.image.url a spoiler is ignored, with no error.
What will cost you an hour
- An edit drops every attachment it does not name. Discord's rule, from API v10.
Turned around here:
editkeeps them unless you passmedia: []. wait: falsemeans no id. Discord then answers204and says nothing, so the message cannot be edited or deleted. The default istruefor that reason.- A message link needs the guild id, which the webhook answer does not carry. So
urlstays empty on this channel, rather than being guessed. - Deleting twice is a 404, unlike Bluesky where it is idempotent.
- Receiving is not possible with a webhook. Reading messages needs a Gateway
connection and the privileged
MESSAGE_CONTENTintent, which means a process that stays alive. Not offered here.
Read more
- Execute Webhook - every field
- Edit Webhook Message - the attachment rule
- Embed structure
Telegram
Send and receive Telegram messages in Nuxt: bot token, chat id, photos, inline keyboards, editing, deleting, and updates by webhook instead of long polling.
Slack
Send Slack messages from Nuxt with an incoming webhook or a bot token: threads, file uploads, editing, deleting, and receiving through the Events API.