Overview
Webhooks provide a way to send messages to channels without requiring a full bot connection. They’re perfect for:
External Integrations : GitHub, Sentry, monitoring services
Event Notifications : CI/CD pipelines, deployment alerts
Automated Messages : Scheduled announcements, RSS feeds
Message Impersonation : Send messages with custom names and avatars
Webhooks are ideal for one-way communication. For interactive features, use a full bot with Gateway connection.
Creating Webhooks
Via REST API
Create a webhook in a channel:
POST / api / v1 / channels / { channel_id } / webhooks
Content - Type : application / json
Authorization : Bot { bot_token }
{
"name" : "GitHub Notifications" ,
"avatar" : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA..."
}
Webhook name (1-80 characters)
Base64-encoded image for the webhook avatar
Response:
{
"id" : "123456789012345678" ,
"type" : 1 ,
"guild_id" : "234567890123456789" ,
"channel_id" : "345678901234567890" ,
"creator_id" : "456789012345678901" ,
"name" : "GitHub Notifications" ,
"avatar" : "a1b2c3d4e5f6g7h8i9j0" ,
"token" : "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ12"
}
The webhook token is only shown once during creation. Store it securely.
Webhook Limits
Fluxer enforces limits on webhook creation:
Per Guild : 50 webhooks (default, configurable via limits)
Per Channel : 10 webhooks (default, configurable via limits)
// From WebhookService.tsx
const MAX_WEBHOOKS_PER_GUILD = 50 ;
const MAX_WEBHOOKS_PER_CHANNEL = 10 ;
Webhook Structure
Webhook Object
interface Webhook {
id : string ; // Webhook ID
type : number ; // Webhook type (1 = Incoming)
guild_id : string ; // Guild the webhook belongs to
channel_id : string ; // Channel the webhook posts to
creator_id : string ; // User who created the webhook
name : string ; // Webhook name
avatar : string | null ; // Webhook avatar hash
token : string ; // Webhook token for authentication
}
Webhook Types
Type Name Description 1 Incoming Standard webhook for sending messages 2 Channel Follower Webhook created by following a channel
Executing Webhooks
Basic Message
Send a message using a webhook:
curl -X POST "https://api.fluxer.app/v1/webhooks/{webhook_id}/{webhook_token}" \
-H "Content-Type: application/json" \
-d '{
"content": "Hello from webhook!"
}'
Webhook execution does NOT require a bot token - the webhook token in the URL provides authentication.
Custom Username and Avatar
Override the default webhook name and avatar:
POST / api / v1 / webhooks / { webhook_id } / { webhook_token }
Content - Type : application / json
{
"content" : "Deployment successful!" ,
"username" : "Deploy Bot" ,
"avatar_url" : "https://example.com/deploy-bot-avatar.png"
}
Message content (up to 2000 characters)
Override the webhook’s default name
Override the webhook’s default avatar (external URL)
With Embeds
Send rich embed messages:
POST / api / v1 / webhooks / { webhook_id } / { webhook_token }
Content - Type : application / json
{
"embeds" : [
{
"title" : "New Deployment" ,
"description" : "Version 2.1.0 deployed to production" ,
"color" : 3066993 ,
"fields" : [
{
"name" : "Status" ,
"value" : "✅ Success" ,
"inline" : true
},
{
"name" : "Duration" ,
"value" : "2m 34s" ,
"inline" : true
}
],
"timestamp" : "2026-03-04T12:00:00.000Z"
}
]
}
With Files
Attach files to webhook messages:
const formData = new FormData ();
formData . append ( 'content' , 'Build logs attached' );
formData . append ( 'file' , fs . createReadStream ( 'build.log' ), 'build.log' );
await fetch (
`https://api.fluxer.app/v1/webhooks/ ${ webhookId } / ${ webhookToken } ` ,
{
method: 'POST' ,
body: formData
}
);
Managing Webhooks
List Channel Webhooks
Retrieve all webhooks in a channel:
GET / api / v1 / channels / { channel_id } / webhooks
Authorization : Bot { bot_token }
Requires MANAGE_WEBHOOKS permission.
List Guild Webhooks
Retrieve all webhooks in a guild:
GET / api / v1 / guilds / { guild_id } / webhooks
Authorization : Bot { bot_token }
Requires MANAGE_WEBHOOKS permission.
Get Webhook
Retrieve a specific webhook:
// With bot token
GET / api / v1 / webhooks / { webhook_id }
Authorization : Bot { bot_token }
// With webhook token (no auth required)
GET / api / v1 / webhooks / { webhook_id } / { webhook_token }
Update Webhook
Modify webhook properties:
PATCH / api / v1 / webhooks / { webhook_id }
Content - Type : application / json
Authorization : Bot { bot_token }
{
"name" : "Updated Webhook Name" ,
"avatar" : null , // Remove avatar
"channel_id" : "567890123456789012" // Move to different channel
}
New avatar (base64) or null to remove
Move webhook to a different channel (must be in same guild)
Delete Webhook
Permanently delete a webhook:
// With bot token
DELETE / api / v1 / webhooks / { webhook_id }
Authorization : Bot { bot_token }
// With webhook token (no auth required)
DELETE / api / v1 / webhooks / { webhook_id } / { webhook_token }
Requires MANAGE_WEBHOOKS permission (when using bot token).
GitHub Integration
Fluxer provides built-in support for GitHub webhook events:
Setup GitHub Webhook
Create a Fluxer webhook in your desired channel
In your GitHub repository, go to Settings → Webhooks → Add webhook
Set the Payload URL:
https://api.fluxer.app/v1/webhooks/{webhook_id}/{webhook_token}/github
Set Content type to application/json
Select events to send (e.g., Pushes, Pull requests, Issues)
Supported GitHub Events
Fluxer automatically formats these GitHub events:
push - Code pushes
pull_request - PR opened, closed, merged
issues - Issue opened, closed, reopened
issue_comment - Comments on issues
release - New releases
star - Repository starred
fork - Repository forked
watch - Repository watched
Example GitHub Payload
When a push occurs, Fluxer receives:
POST / api / v1 / webhooks / { webhook_id } / { webhook_token } / github
X - GitHub - Event : push
X - GitHub - Delivery : 12345678 - 1234 - 1234 - 1234 - 123456789 abc
{
"ref" : "refs/heads/main" ,
"commits" : [
{
"message" : "Fix bug in user service" ,
"author" : { "name" : "John Doe" },
"url" : "https://github.com/org/repo/commit/abc123"
}
],
"repository" : {
"name" : "my-repo" ,
"full_name" : "org/my-repo"
},
"pusher" : { "name" : "johndoe" }
}
Fluxer automatically transforms this into a formatted embed:
{
"title" : "[org/my-repo] 1 new commit to main" ,
"description" : "[`abc123`](https://github.com/org/repo/commit/abc123) Fix bug in user service - John Doe" ,
"color" : 0 x 6e5494 ,
"author" : {
"name" : "johndoe" ,
"url" : "https://github.com/johndoe"
},
"timestamp" : "2026-03-04T12:00:00.000Z"
}
Sentry Integration
Fluxer also supports Sentry webhook events:
Setup Sentry Webhook
Create a Fluxer webhook
In Sentry, go to Settings → Integrations → WebHooks
Set the Callback URL:
https://api.fluxer.app/v1/webhooks/{webhook_id}/{webhook_token}/sentry
Enable the integration
Supported Sentry Events
issue - New issues
error - Error events
event.alert - Alert triggered
Sentry events are automatically formatted into embeds with:
Error title and message
Stack trace preview
Environment and release information
Direct link to Sentry issue
Rate Limits
Webhook executions are rate limited:
Per Webhook : 5 messages per 2 seconds
Per Channel : 30 messages per 60 seconds (shared with regular messages)
Exceeding rate limits returns a 429 status code with a Retry-After header indicating when you can retry.
Handling Rate Limits
async function executeWebhook (
webhookId : string ,
webhookToken : string ,
data : any
) {
const response = await fetch (
`https://api.fluxer.app/v1/webhooks/ ${ webhookId } / ${ webhookToken } ` ,
{
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( data )
}
);
if ( response . status === 429 ) {
const retryAfter = parseInt (
response . headers . get ( 'Retry-After' ) || '1000'
);
console . log ( `Rate limited, retrying after ${ retryAfter } ms` );
await new Promise ( resolve => setTimeout ( resolve , retryAfter ));
return executeWebhook ( webhookId , webhookToken , data );
}
if ( ! response . ok ) {
throw new Error ( `Webhook execution failed: ${ response . statusText } ` );
}
return response . json ();
}
Webhook Events
When webhooks are created, updated, or deleted, Fluxer dispatches a WEBHOOKS_UPDATE event to the Gateway:
{
"op" : 0 ,
"t" : "WEBHOOKS_UPDATE" ,
"d" : {
"channel_id" : "345678901234567890"
}
}
Bots can listen for this event to refresh their webhook cache.
Best Practices
Secure Webhook Tokens
Treat webhook tokens like passwords:
Store in environment variables
Never commit to version control
Rotate regularly
Use separate webhooks for different services
Implement Retry Logic
Handle transient failures gracefully: async function executeWebhookWithRetry (
webhookId : string ,
webhookToken : string ,
data : any ,
maxRetries = 3
) {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
try {
return await executeWebhook ( webhookId , webhookToken , data );
} catch ( error ) {
if ( i === maxRetries - 1 ) throw error ;
await new Promise ( resolve =>
setTimeout ( resolve , 1000 * Math . pow ( 2 , i ))
);
}
}
}
Validate Webhook Signatures
For GitHub webhooks, verify the signature: import crypto from 'crypto' ;
function verifyGitHubSignature (
payload : string ,
signature : string ,
secret : string
) : boolean {
const hmac = crypto . createHmac ( 'sha256' , secret );
const digest = 'sha256=' + hmac . update ( payload ). digest ( 'hex' );
return crypto . timingSafeEqual (
Buffer . from ( signature ),
Buffer . from ( digest )
);
}
Use Embeds for Structure
Embeds provide better formatting than plain text:
Color-code by event type (success=green, error=red)
Use fields for key-value pairs
Include timestamps
Add clickable links
Monitor Webhook Usage
Track webhook executions: const webhookMetrics = new Map < string , {
executions : number ;
failures : number ;
lastUsed : Date ;
}>();
function recordWebhookExecution (
webhookId : string ,
success : boolean
) {
if ( ! webhookMetrics . has ( webhookId )) {
webhookMetrics . set ( webhookId , {
executions: 0 ,
failures: 0 ,
lastUsed: new Date ()
});
}
const metrics = webhookMetrics . get ( webhookId ) ! ;
metrics . executions ++ ;
if ( ! success ) metrics . failures ++ ;
metrics . lastUsed = new Date ();
}
Common Use Cases
CI/CD Notifications
// Deployment notification
const deploymentWebhook = async (
webhookId : string ,
webhookToken : string ,
deployment : {
version : string ;
environment : string ;
status : 'success' | 'failure' ;
duration : number ;
}
) => {
const color = deployment . status === 'success' ? 0x57F287 : 0xED4245 ;
const emoji = deployment . status === 'success' ? '✅' : '❌' ;
await executeWebhook ( webhookId , webhookToken , {
username: 'Deploy Bot' ,
embeds: [{
title: ` ${ emoji } Deployment ${ deployment . status } ` ,
fields: [
{ name: 'Version' , value: deployment . version , inline: true },
{ name: 'Environment' , value: deployment . environment , inline: true },
{ name: 'Duration' , value: ` ${ deployment . duration } s` , inline: true }
],
color ,
timestamp: new Date (). toISOString ()
}]
});
};
Monitoring Alerts
// Server monitoring alert
const monitoringWebhook = async (
webhookId : string ,
webhookToken : string ,
alert : {
service : string ;
metric : string ;
threshold : number ;
current : number ;
}
) => {
await executeWebhook ( webhookId , webhookToken , {
username: 'Monitoring' ,
embeds: [{
title: '🚨 Alert: Threshold Exceeded' ,
description: ` ${ alert . service } - ${ alert . metric } ` ,
fields: [
{ name: 'Current Value' , value: alert . current . toString (), inline: true },
{ name: 'Threshold' , value: alert . threshold . toString (), inline: true },
{ name: 'Difference' , value: `+ ${ alert . current - alert . threshold } ` , inline: true }
],
color: 0xED4245 ,
timestamp: new Date (). toISOString ()
}]
});
};
Troubleshooting
Webhook returns 404 Not Found
Verify the webhook ID and token are correct
Check if the webhook was deleted
Ensure the URL format is correct
Webhook returns 401 Unauthorized
For authenticated endpoints, verify your bot token is valid
For webhook execution, ensure the webhook token is correct
Check that you’re using the right authentication method
Webhook returns 403 Forbidden
Verify you have MANAGE_WEBHOOKS permission
Check if the channel still exists
Ensure the webhook hasn’t been moved to a channel you can’t access
Check rate limits (429 status code)
Verify the channel hasn’t been deleted
Ensure message content isn’t empty
Check for validation errors in embeds
Ensure the URL is publicly accessible
Verify the image format (PNG, JPG, GIF)
Check file size (max 8MB)
Use HTTPS URLs only
Next Steps
Webhook API Reference Complete webhook endpoint documentation
Embed Reference Learn about creating rich embeds
GitHub Integration Detailed GitHub webhook setup
Rate Limits Understanding rate limits