NTF-004
Post to a Discord channel with a webhook
No bot, no token, no OAuth — a URL you POST JSON to. Embeds give structure, and the same rate guard applies.
A Discord webhook is a URL that accepts JSON and posts as a channel message. There is no bot to host and no token to rotate, which makes it the least effort monitoring channel available.
Embeds are worth using over plain text. A coloured strip and labelled fields make a stats digest scannable in a way a paragraph is not.
Anyone holding the URL can post to your channel, so treat it as a secret and keep it in settings.
function discord_notify(string $title, string $description, array $fields = [], string $level = 'info'): bool
{
$url = setting('discord_webhook');
if ($url === '' || !str_starts_with($url, 'https://discord.com/api/webhooks/')) {
return false;
}
$colors = ['info' => 0x2E6FD6, 'good' => 0x1C6B3F, 'warn' => 0xD9822B, 'crit' => 0x9E332B];
$embed = [
'title' => mb_substr($title, 0, 250),
'description' => mb_substr($description, 0, 2000),
'color' => $colors[$level] ?? $colors['info'],
'timestamp' => date('c'),
'footer' => ['text' => setting('site_name')],
];
foreach (array_slice($fields, 0, 25) as $name => $value) {
$embed['fields'][] = ['name' => (string) $name, 'value' => (string) $value, 'inline' => true];
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['embeds' => [$embed]]),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 8,
CURLOPT_SSL_VERIFYPEER => true,
]);
curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $code === 204; // Discord returns 204 on success
}
Using it
A daily digest — claims, new users, payouts, revenue — posted to a private channel is the cheapest dashboard you will ever build.
Use good and crit colours consistently so the channel can be read at a glance without reading any words.
Success is HTTP 204 with an empty body, not 200. Checking for 200 makes every successful post look failed.
What bites people
The webhook URL is a credential. Anyone with it can post anything to that channel.
Discord rate limits per webhook and will return 429 with a retry-after. Respect it or you get temporarily blocked.
Embeds cap at 25 fields and 6000 characters overall. Truncate rather than letting a long message fail silently.