GitScrum / Docs

Best Practices

Production-ready webhook handling: respond fast, handle duplicates, queue processing, error handling, and monitoring.

Follow these practices to build reliable, production-ready webhook handlers.

Respond Quickly

Return a 2xx status code as fast as possible. GitScrum expects a response within 30 seconds. If your processing takes longer, acknowledge receipt immediately and process asynchronously.

Queue-Based Processing

Decouple receiving from processing using a message queue:

const Queue = require('bull');
const webhookQueue = new Queue('webhooks');

app.post('/webhooks/gitscrum', (req, res) => {
  // Acknowledge immediately
  res.status(200).json({ received: true });

  // Queue for async processing
  webhookQueue.add(req.body);
});

webhookQueue.process(async (job) => {
  const event = job.data;
  // Heavy processing here: database writes, API calls, notifications
  await processWebhook(event);
});
from celery import Celery

celery_app = Celery('webhooks', broker='redis://localhost:6379')

@app.route('/webhooks/gitscrum', methods=['POST'])
def webhook():
    event = request.get_json()

    # Acknowledge immediately
    process_webhook.delay(event)

    return jsonify({"received": True}), 200

@celery_app.task
def process_webhook(event):
    # Heavy processing here
    pass

Handle Duplicates

The same event may be delivered more than once. Design your handler to be idempotent — processing the same event twice should produce the same result.

Use the UUID as Idempotency Key

const processedEvents = new Set(); // Use Redis in production

webhookQueue.process(async (job) => {
  const { data } = job.data;
  const eventId = data.uuid;

  // Skip if already processed
  if (processedEvents.has(eventId)) {
    console.log(`Skipping duplicate: ${eventId}`);
    return;
  }

  processedEvents.add(eventId);
  await processWebhook(job.data);
});

Error Handling

Catch All Exceptions

Never let your webhook handler crash. Wrap processing in error handling:

app.post('/webhooks/gitscrum', async (req, res) => {
  try {
    // Always respond 200 first
    res.status(200).json({ received: true });

    await processWebhook(req.body);
  } catch (error) {
    console.error('Webhook processing error:', error);
    // Log the error but don't crash the server
  }
});

Log Everything

Log all incoming webhooks for debugging and audit:

app.post('/webhooks/gitscrum', (req, res) => {
  const timestamp = new Date().toISOString();
  const headers = {
    'x-header': req.headers['x-header'],
    'user-agent': req.headers['user-agent'],
  };

  console.log(JSON.stringify({
    timestamp,
    headers,
    body: req.body,
  }));

  res.status(200).json({ received: true });
});

Monitoring

Track Delivery Success Rates

Monitor your webhook endpoint for:

  • Response time (keep under 5 seconds)
  • Error rates (5xx responses)
  • Payload size
  • Processing queue depth

Health Check Endpoint

Add a health check alongside your webhook endpoint:

app.get('/webhooks/health', (req, res) => {
  res.status(200).json({
    status: 'healthy',
    queue_depth: webhookQueue.getJobCount(),
    uptime: process.uptime(),
  });
});

Event Filtering

If you use the same endpoint URL for multiple events, filter by checking the payload structure:

app.post('/webhooks/gitscrum', (req, res) => {
  res.status(200).json({ received: true });

  const { data } = req.body;

  // Route based on payload structure
  if (data.workflow) {
    handleTaskEvent(data);
  } else if (data.time_box) {
    handleSprintEvent(data);
  } else if (data.acceptance_criteria) {
    handleUserStoryEvent(data);
  }
});

Environment Separation

Use different webhook endpoints for different environments:

EnvironmentEndpoint
Developmenthttps://dev.your-server.com/webhooks/gitscrum
Staginghttps://staging.your-server.com/webhooks/gitscrum
Productionhttps://your-server.com/webhooks/gitscrum

Configure separate GitScrum projects for each environment and point webhooks to the matching endpoint.

Testing with ngrok

During development, use a tunneling tool like ngrok to expose your local server:

# Start your local server on port 3000
node server.js

# In another terminal, start ngrok
ngrok http 3000

Copy the ngrok HTTPS URL and paste it into your GitScrum webhook configuration. This allows you to test webhooks against your local development server.