Skip to main content

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..."
}
name
string
required
Webhook name (1-80 characters)
avatar
string | null
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

TypeNameDescription
1IncomingStandard webhook for sending messages
2Channel FollowerWebhook 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"
}
content
string
Message content (up to 2000 characters)
username
string
Override the webhook’s default name
avatar_url
string
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
}
name
string
New webhook name
avatar
string | null
New avatar (base64) or null to remove
channel_id
string
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

  1. Create a Fluxer webhook in your desired channel
  2. In your GitHub repository, go to Settings → Webhooks → Add webhook
  3. Set the Payload URL:
    https://api.fluxer.app/v1/webhooks/{webhook_id}/{webhook_token}/github
    
  4. Set Content type to application/json
  5. 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-123456789abc

{
  "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": 0x6e5494,
  "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

  1. Create a Fluxer webhook
  2. In Sentry, go to Settings → Integrations → WebHooks
  3. Set the Callback URL:
    https://api.fluxer.app/v1/webhooks/{webhook_id}/{webhook_token}/sentry
    
  4. 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

1

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
2

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))
      );
    }
  }
}
3

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)
  );
}
4

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
5

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

  • Verify the webhook ID and token are correct
  • Check if the webhook was deleted
  • Ensure the URL format is correct
  • 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
  • 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

Build docs developers (and LLMs) love