Skip to main content

Overview

Monitor webhook deliveries to debug issues, track success rates, and ensure your integration is working correctly.

Authentication

Requires Platform API Key in the X-API-Key header.

Request

GET https://api.syncline.run/v1/platform/webhook/logs

Query Parameters

limit
number
Number of delivery records to return (default: 50, max: 200)
event
string
Filter by event type (e.g., “meeting.scheduled”, “user.calendar_connected”)
status
string
Filter by status: “success” or “failed”

Response

{
  "deliveries": [
    {
      "id": "wh_del_abc123",
      "event": "meeting.scheduled",
      "timestamp": "2025-01-20T10:35:00Z",
      "status": "success",
      "response_code": 200,
      "attempts": 1,
      "duration_ms": 145,
      "url": "https://yourapp.com/webhooks/syncline"
    },
    {
      "id": "wh_del_def456",
      "event": "user.calendar_connected",
      "timestamp": "2025-01-20T10:30:00Z",
      "status": "failed",
      "response_code": 500,
      "attempts": 3,
      "duration_ms": 5000,
      "error": "Connection timeout after 3 attempts",
      "url": "https://yourapp.com/webhooks/syncline"
    }
  ],
  "stats": {
    "total_sent": 1247,
    "success_rate": 0.987,
    "avg_response_time_ms": 156,
    "last_30_days": {
      "success": 1231,
      "failed": 16
    }
  },
  "pagination": {
    "next_cursor": "wh_del_xyz789",
    "has_more": true
  }
}

Response Fields

deliveries
array
Array of webhook delivery records
stats
object
Aggregate statistics about webhook health

Delivery Object

id
string
Unique delivery identifier
event
string
Event type (e.g., “meeting.scheduled”)
status
string
“success” or “failed”
response_code
number
HTTP status code returned by your endpoint
attempts
number
Number of delivery attempts (max 3)
duration_ms
number
Response time in milliseconds
error
string
Error message if delivery failed

Use Cases

Debug Failed Deliveries

// Check recent failed webhooks
async function debugWebhookFailures() {
  const response = await fetch(
    'https://api.syncline.run/v1/platform/webhook/logs?status=failed&limit=20',
    {
      headers: { 'X-API-Key': process.env.SYNCLINE_API_KEY }
    }
  );

  const { deliveries } = await response.json();

  deliveries.forEach(delivery => {
    console.log(`❌ Failed: ${delivery.event} at ${delivery.timestamp}`);
    console.log(`   Error: ${delivery.error}`);
    console.log(`   Attempts: ${delivery.attempts}`);
    console.log(`   Response Code: ${delivery.response_code}`);
  });
}

Monitor Webhook Health

// Check webhook success rate
async function checkWebhookHealth() {
  const { stats } = await fetch(
    'https://api.syncline.run/v1/platform/webhook/logs',
    {
      headers: { 'X-API-Key': process.env.SYNCLINE_API_KEY }
    }
  ).then(r => r.json());

  if (stats.success_rate < 0.95) {
    await alertTeam({
      title: '⚠️ Webhook Health Alert',
      message: `Webhook success rate is ${(stats.success_rate * 100).toFixed(1)}%`,
      stats: stats.last_30_days
    });
  }

  return {
    health: stats.success_rate > 0.95 ? 'healthy' : 'degraded',
    success_rate: stats.success_rate,
    avg_response_time: stats.avg_response_time_ms,
    recent_failures: stats.last_30_days.failed
  };
}

Track Specific Events

// Monitor meeting scheduling webhooks
async function trackMeetingWebhooks() {
  const response = await fetch(
    'https://api.syncline.run/v1/platform/webhook/logs?event=meeting.scheduled&limit=100',
    {
      headers: { 'X-API-Key': process.env.SYNCLINE_API_KEY }
    }
  );

  const { deliveries } = await response.json();

  const analysis = {
    total: deliveries.length,
    successful: deliveries.filter(d => d.status === 'success').length,
    avg_response_time: deliveries.reduce((sum, d) => sum + d.duration_ms, 0) / deliveries.length,
    slow_deliveries: deliveries.filter(d => d.duration_ms > 1000).length
  };

  console.log('Meeting webhook analysis:', analysis);
  return analysis;
}

Alert on Performance Issues

// Set up automated monitoring
async function monitorWebhookPerformance() {
  const { stats, deliveries } = await getWebhookLogs();

  // Alert on high failure rate
  if (stats.success_rate < 0.90) {
    await sendAlert({
      severity: 'high',
      message: `Webhook failure rate: ${((1 - stats.success_rate) * 100).toFixed(1)}%`,
      failed_count: stats.last_30_days.failed
    });
  }

  // Alert on slow response times
  if (stats.avg_response_time_ms > 2000) {
    await sendAlert({
      severity: 'medium',
      message: `Webhook response time: ${stats.avg_response_time_ms}ms (above 2s threshold)`
    });
  }

  // Alert on recent failures
  const recentFailures = deliveries
    .filter(d => d.status === 'failed')
    .filter(d => Date.now() - new Date(d.timestamp).getTime() < 3600000); // Last hour

  if (recentFailures.length > 5) {
    await sendAlert({
      severity: 'high',
      message: `${recentFailures.length} webhook failures in the last hour`,
      failures: recentFailures.map(f => ({
        event: f.event,
        error: f.error,
        timestamp: f.timestamp
      }))
    });
  }
}

// Run every 15 minutes
setInterval(monitorWebhookPerformance, 15 * 60 * 1000);

Common Failure Patterns

Connection Timeouts

{
  "status": "failed",
  "response_code": 0,
  "error": "Connection timeout after 3 attempts",
  "duration_ms": 30000
}
Solution: Check your webhook endpoint is responding within 30 seconds. Return 200 OK immediately and process asynchronously.

5xx Server Errors

{
  "status": "failed",
  "response_code": 500,
  "error": "Internal server error",
  "attempts": 3
}
Solution: Check your webhook handler logs. Common causes:
  • Unhandled exceptions
  • Database connection issues
  • Missing environment variables

401 Unauthorized

{
  "status": "failed",
  "response_code": 401,
  "error": "Invalid webhook signature",
  "attempts": 3
}
Solution: Verify your webhook signature verification code. Ensure SYNCLINE_WEBHOOK_SECRET is set correctly.

SSL/TLS Errors

{
  "status": "failed",
  "response_code": 0,
  "error": "SSL certificate verification failed",
  "attempts": 3
}
Solution: Ensure your webhook URL uses a valid SSL certificate. Self-signed certificates are not supported.

Dashboard Example

// Build a webhook monitoring dashboard
async function buildWebhookDashboard() {
  const { deliveries, stats } = await getWebhookLogs();

  return {
    overview: {
      total_sent: stats.total_sent,
      success_rate: (stats.success_rate * 100).toFixed(1) + '%',
      avg_response_time: stats.avg_response_time_ms + 'ms',
      health: stats.success_rate > 0.95 ? '🟢 Healthy' : '🟡 Degraded'
    },
    last_30_days: {
      successful: stats.last_30_days.success,
      failed: stats.last_30_days.failed,
      failure_rate: (stats.last_30_days.failed / (stats.last_30_days.success + stats.last_30_days.failed) * 100).toFixed(1) + '%'
    },
    recent_deliveries: deliveries.slice(0, 10).map(d => ({
      event: d.event,
      status: d.status === 'success' ? '✓' : '✗',
      response_time: d.duration_ms + 'ms',
      timestamp: new Date(d.timestamp).toLocaleString()
    })),
    by_event_type: groupByEventType(deliveries)
  };
}

function groupByEventType(deliveries) {
  const grouped = {};
  deliveries.forEach(d => {
    if (!grouped[d.event]) {
      grouped[d.event] = { total: 0, success: 0, failed: 0 };
    }
    grouped[d.event].total++;
    if (d.status === 'success') {
      grouped[d.event].success++;
    } else {
      grouped[d.event].failed++;
    }
  });
  return grouped;
}

Best Practices

Regular Monitoring

Check webhook logs daily to catch issues early:
// Daily health check
async function dailyWebhookHealthCheck() {
  const { stats } = await getWebhookLogs();

  const report = {
    date: new Date().toISOString().split('T')[0],
    success_rate: stats.success_rate,
    total_sent: stats.total_sent,
    avg_response_time: stats.avg_response_time_ms,
    recent_failures: stats.last_30_days.failed
  };

  // Store in database for trending
  await db.webhook_health.create(report);

  // Alert if degraded
  if (stats.success_rate < 0.95) {
    await notifyTeam(report);
  }
}

Set Up Alerts

Configure alerts for critical issues:
  • Success rate drops below 95%
  • Average response time exceeds 2 seconds
  • More than 5 failures in 1 hour
  • Any SSL/TLS certificate errors

Investigate Patterns

Look for patterns in failed deliveries:
async function analyzeFai lurePatterns() {
  const { deliveries } = await fetch(
    'https://api.syncline.run/v1/platform/webhook/logs?status=failed&limit=200',
    { headers: { 'X-API-Key': process.env.SYNCLINE_API_KEY } }
  ).then(r => r.json());

  // Group by error message
  const errorTypes = {};
  deliveries.forEach(d => {
    const errorKey = d.error.split(':')[0]; // First part of error message
    errorTypes[errorKey] = (errorTypes[errorKey] || 0) + 1;
  });

  // Most common failures
  const sorted = Object.entries(errorTypes)
    .sort(([,a], [,b]) => b - a);

  console.log('Most common webhook failures:');
  sorted.forEach(([error, count]) => {
    console.log(`  ${error}: ${count} occurrences`);
  });
}