List Webhooks

The List Webhooks endpoint retrieves all webhook configurations associated with your authenticated application. This endpoint provides comprehensive information about each webhook, including delivery statistics and current status, enabling effective webhook management and monitoring.

Endpoint DetailsCopied!

  • URL: /api/v0/webhooks

  • Method: GET

  • Authentication: Required (API Key Authentication with Scopes)

  • Content-Type: application/json

  • Required Scope: webhook:read

AuthenticationCopied!

This endpoint requires API key authentication with specific scopes:

Required Headers

x-client-key: your-client-key
x-client-secret: your-client-secret

Required Scope

Your API key must have the webhook:read scope to access this endpoint.

Query ParametersCopied!

Optional Parameters

Parameter

Type

Description

Default

Validation

skip

number

Number of records to skip for pagination

0

Non-negative integer

take

number

Number of records to return

10

Positive integer

Request ExamplesCopied!

Basic Request

curl -X GET https://api.devdraft.com/api/v0/webhooks \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret"

With Pagination

curl -X GET "https://api.devdraft.com/api/v0/webhooks?skip=0&take=20" \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret"

JavaScript/Fetch

const response = await fetch('/api/v0/webhooks?skip=0&take=10', {
  headers: {
    'x-client-key': 'your-client-key',
    'x-client-secret': 'your-client-secret'
  }
});

const webhooks = await response.json();

ResponseCopied!

Success Response (200 OK)

Returns an array of webhook objects with delivery statistics:

[
  {
    "id": "wh_123456789",
    "name": "Payment Notifications",
    "url": "https://api.example.com/webhooks/payments",
    "isActive": true,
    "encrypted": false,
    "created_at": "2024-03-20T12:00:00.000Z",
    "updated_at": "2024-03-20T12:00:00.000Z",
    "delivery_stats": {
      "total_events": 150,
      "successful_deliveries": 145,
      "failed_deliveries": 5,
      "last_delivery": "2024-03-20T11:55:00.000Z"
    }
  },
  {
    "id": "wh_987654321",
    "name": "Order Updates",
    "url": "https://api.example.com/webhooks/orders",
    "isActive": true,
    "encrypted": true,
    "created_at": "2024-03-15T09:30:00.000Z",
    "updated_at": "2024-03-18T14:20:00.000Z",
    "delivery_stats": {
      "total_events": 89,
      "successful_deliveries": 87,
      "failed_deliveries": 2,
      "last_delivery": "2024-03-20T10:30:00.000Z"
    }
  },
  {
    "id": "wh_555666777",
    "name": "Test Webhook",
    "url": "https://webhook.site/test-endpoint",
    "isActive": false,
    "encrypted": false,
    "created_at": "2024-03-18T16:45:00.000Z",
    "updated_at": "2024-03-19T08:15:00.000Z",
    "delivery_stats": {
      "total_events": 0,
      "successful_deliveries": 0,
      "failed_deliveries": 0,
      "last_delivery": null
    }
  }
]

Empty Response (200 OK)

[]

Error Responses

401 Unauthorized - Missing Credentials
{
  "statusCode": 401,
  "message": "Client key or secret missing",
  "error": "Unauthorized",
  "details": "Please provide both x-client-key and x-client-secret headers"
}
401 Unauthorized - Invalid Credentials
{
  "statusCode": 401,
  "message": "Invalid client app credentials",
  "error": "Unauthorized",
  "details": "The provided API key or secret is invalid"
}
403 Forbidden - Missing Scope
{
  "statusCode": 403,
  "message": "Missing required scope",
  "error": "Forbidden",
  "details": "API key does not have the required webhook:read scope"
}
403 Forbidden - Inactive API Key
{
  "statusCode": 403,
  "message": "API key is inactive",
  "error": "Forbidden",
  "details": "The provided API key has been deactivated"
}

Response FieldsCopied!

Webhook Object Fields

Field

Type

Description

id

string

Unique webhook identifier (prefixed with wh_)

name

string

Human-readable webhook name

url

string

Endpoint URL where events are sent

isActive

boolean

Whether the webhook is currently active

encrypted

boolean

Whether webhook payloads are encrypted

created_at

string

ISO 8601 timestamp of webhook creation

updated_at

string

ISO 8601 timestamp of last modification

delivery_stats

object

Webhook delivery performance metrics

Delivery Statistics Object

Field

Type

Description

total_events

number

Total number of events sent to this webhook

successful_deliveries

number

Number of successful deliveries (HTTP 2xx responses)

failed_deliveries

number

Number of failed deliveries (non-2xx responses)

last_delivery

string | null

ISO 8601 timestamp of most recent delivery attempt

Use CasesCopied!

1. Webhook Management Dashboard

Display all webhooks with their status and performance:

async function loadWebhookDashboard() {
  try {
    const webhooks = await fetchWebhooks();
    
    return webhooks.map(webhook => ({
      id: webhook.id,
      name: webhook.name,
      status: webhook.isActive ? 'Active' : 'Inactive',
      successRate: webhook.delivery_stats.total_events > 0 
        ? (webhook.delivery_stats.successful_deliveries / webhook.delivery_stats.total_events * 100).toFixed(1)
        : 'N/A',
      lastDelivery: webhook.delivery_stats.last_delivery 
        ? new Date(webhook.delivery_stats.last_delivery).toLocaleDateString()
        : 'Never',
      encrypted: webhook.encrypted ? 'Yes' : 'No'
    }));
  } catch (error) {
    console.error('Failed to load webhook dashboard:', error);
    throw error;
  }
}

2. Health Monitoring

Monitor webhook health and identify issues:

async function getWebhookHealth() {
  const webhooks = await fetchWebhooks();
  
  return {
    total: webhooks.length,
    active: webhooks.filter(w => w.isActive).length,
    inactive: webhooks.filter(w => !w.isActive).length,
    withFailures: webhooks.filter(w => w.delivery_stats.failed_deliveries > 0).length,
    healthyWebhooks: webhooks.filter(w => {
      const stats = w.delivery_stats;
      if (stats.total_events === 0) return true;
      return (stats.successful_deliveries / stats.total_events) >= 0.95;
    }).length
  };
}

3. Performance Analytics

Analyze webhook performance across all endpoints:

async function getWebhookAnalytics() {
  const webhooks = await fetchWebhooks();
  
  const totalStats = webhooks.reduce((acc, webhook) => {
    const stats = webhook.delivery_stats;
    return {
      totalEvents: acc.totalEvents + stats.total_events,
      successfulDeliveries: acc.successfulDeliveries + stats.successful_deliveries,
      failedDeliveries: acc.failedDeliveries + stats.failed_deliveries
    };
  }, { totalEvents: 0, successfulDeliveries: 0, failedDeliveries: 0 });
  
  return {
    ...totalStats,
    successRate: totalStats.totalEvents > 0 
      ? (totalStats.successfulDeliveries / totalStats.totalEvents * 100).toFixed(2)
      : '0',
    failureRate: totalStats.totalEvents > 0 
      ? (totalStats.failedDeliveries / totalStats.totalEvents * 100).toFixed(2)
      : '0'
  };
}

4. Webhook Filtering and Search

Filter webhooks based on various criteria:

async function filterWebhooks(criteria) {
  const webhooks = await fetchWebhooks();
  
  return webhooks.filter(webhook => {
    if (criteria.status && webhook.isActive !== (criteria.status === 'active')) {
      return false;
    }
    
    if (criteria.encrypted !== undefined && webhook.encrypted !== criteria.encrypted) {
      return false;
    }
    
    if (criteria.nameSearch && !webhook.name.toLowerCase().includes(criteria.nameSearch.toLowerCase())) {
      return false;
    }
    
    if (criteria.hasFailures && webhook.delivery_stats.failed_deliveries === 0) {
      return false;
    }
    
    return true;
  });
}

// Usage examples
const activeWebhooks = await filterWebhooks({ status: 'active' });
const encryptedWebhooks = await filterWebhooks({ encrypted: true });
const webhooksWithFailures = await filterWebhooks({ hasFailures: true });

Integration ExamplesCopied!

React Component

import React, { useState, useEffect } from 'react';

function WebhookList() {
  const [webhooks, setWebhooks] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [page, setPage] = useState(0);
  const [pageSize] = useState(10);

  useEffect(() => {
    loadWebhooks();
  }, [page]);

  const loadWebhooks = async () => {
    try {
      setLoading(true);
      const response = await fetch(
        `/api/v0/webhooks?skip=${page * pageSize}&take=${pageSize}`,
        {
          headers: {
            'x-client-key': process.env.REACT_APP_CLIENT_KEY,
            'x-client-secret': process.env.REACT_APP_CLIENT_SECRET
          }
        }
      );

      if (!response.ok) {
        throw new Error(`Failed to load webhooks: ${response.statusText}`);
      }

      const webhookData = await response.json();
      setWebhooks(webhookData);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const getSuccessRate = (stats) => {
    if (stats.total_events === 0) return 'N/A';
    return `${((stats.successful_deliveries / stats.total_events) * 100).toFixed(1)}%`;
  };

  if (loading) return <div>Loading webhooks...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div className="webhook-list">
      <h2>Webhooks ({webhooks.length})</h2>
      
      {webhooks.length === 0 ? (
        <p>No webhooks found. Create your first webhook to get started.</p>
      ) : (
        <div className="webhook-grid">
          {webhooks.map(webhook => (
            <div key={webhook.id} className="webhook-card">
              <div className="webhook-header">
                <h3>{webhook.name}</h3>
                <span className={`status ${webhook.isActive ? 'active' : 'inactive'}`}>
                  {webhook.isActive ? 'Active' : 'Inactive'}
                </span>
              </div>
              
              <div className="webhook-details">
                <p><strong>URL:</strong> {webhook.url}</p>
                <p><strong>Encrypted:</strong> {webhook.encrypted ? 'Yes' : 'No'}</p>
                <p><strong>Created:</strong> {new Date(webhook.created_at).toLocaleDateString()}</p>
              </div>
              
              <div className="webhook-stats">
                <h4>Delivery Stats</h4>
                <p>Total Events: {webhook.delivery_stats.total_events}</p>
                <p>Success Rate: {getSuccessRate(webhook.delivery_stats)}</p>
                <p>Failed: {webhook.delivery_stats.failed_deliveries}</p>
                <p>Last Delivery: {
                  webhook.delivery_stats.last_delivery 
                    ? new Date(webhook.delivery_stats.last_delivery).toLocaleString()
                    : 'Never'
                }</p>
              </div>
            </div>
          ))}
        </div>
      )}
      
      <div className="pagination">
        <button 
          onClick={() => setPage(p => Math.max(0, p - 1))}
          disabled={page === 0}
        >
          Previous
        </button>
        <span>Page {page + 1}</span>
        <button 
          onClick={() => setPage(p => p + 1)}
          disabled={webhooks.length < pageSize}
        >
          Next
        </button>
      </div>
    </div>
  );
}

Python Service

import requests
from typing import List, Dict, Any, Optional

class WebhookManager:
    def __init__(self, client_key: str, client_secret: str, base_url: str):
        self.client_key = client_key
        self.client_secret = client_secret
        self.base_url = base_url
        
    def _get_headers(self) -> Dict[str, str]:
        return {
            'x-client-key': self.client_key,
            'x-client-secret': self.client_secret
        }
    
    def list_webhooks(self, skip: int = 0, take: int = 10) -> List[Dict[str, Any]]:
        """List all webhooks with pagination"""
        params = {'skip': skip, 'take': take}
        
        response = requests.get(
            f"{self.base_url}/api/v0/webhooks",
            headers=self._get_headers(),
            params=params
        )
        
        response.raise_for_status()
        return response.json()
    
    def get_all_webhooks(self) -> List[Dict[str, Any]]:
        """Get all webhooks by paginating through results"""
        all_webhooks = []
        skip = 0
        take = 50
        
        while True:
            batch = self.list_webhooks(skip=skip, take=take)
            if not batch:
                break
                
            all_webhooks.extend(batch)
            
            if len(batch) < take:
                break
                
            skip += take
        
        return all_webhooks
    
    def get_webhook_summary(self) -> Dict[str, Any]:
        """Get summary statistics for all webhooks"""
        webhooks = self.get_all_webhooks()
        
        total_events = sum(w['delivery_stats']['total_events'] for w in webhooks)
        successful_deliveries = sum(w['delivery_stats']['successful_deliveries'] for w in webhooks)
        failed_deliveries = sum(w['delivery_stats']['failed_deliveries'] for w in webhooks)
        
        return {
            'total_webhooks': len(webhooks),
            'active_webhooks': len([w for w in webhooks if w['isActive']]),
            'inactive_webhooks': len([w for w in webhooks if not w['isActive']]),
            'encrypted_webhooks': len([w for w in webhooks if w['encrypted']]),
            'total_events': total_events,
            'successful_deliveries': successful_deliveries,
            'failed_deliveries': failed_deliveries,
            'overall_success_rate': (successful_deliveries / total_events * 100) if total_events > 0 else 0
        }
    
    def find_unhealthy_webhooks(self, failure_threshold: float = 0.1) -> List[Dict[str, Any]]:
        """Find webhooks with high failure rates"""
        webhooks = self.get_all_webhooks()
        unhealthy = []
        
        for webhook in webhooks:
            stats = webhook['delivery_stats']
            if stats['total_events'] > 0:
                failure_rate = stats['failed_deliveries'] / stats['total_events']
                if failure_rate > failure_threshold:
                    webhook['failure_rate'] = failure_rate
                    unhealthy.append(webhook)
        
        return sorted(unhealthy, key=lambda w: w['failure_rate'], reverse=True)

# Usage
webhook_manager = WebhookManager('your-key', 'your-secret', 'https://api.devdraft.com')

# List webhooks with pagination
webhooks = webhook_manager.list_webhooks(skip=0, take=20)
print(f"Found {len(webhooks)} webhooks")

# Get summary
summary = webhook_manager.get_webhook_summary()
print(f"Total: {summary['total_webhooks']}, Active: {summary['active_webhooks']}")
print(f"Success Rate: {summary['overall_success_rate']:.1f}%")

# Find problematic webhooks
unhealthy = webhook_manager.find_unhealthy_webhooks(failure_threshold=0.05)
if unhealthy:
    print("Webhooks with high failure rates:")
    for webhook in unhealthy:
        print(f"- {webhook['name']}: {webhook['failure_rate']*100:.1f}% failure rate")

Node.js/Express

const express = require('express');
const axios = require('axios');

class WebhookService {
  constructor(clientKey, clientSecret, baseUrl) {
    this.clientKey = clientKey;
    this.clientSecret = clientSecret;
    this.baseUrl = baseUrl;
  }

  async listWebhooks(skip = 0, take = 10) {
    try {
      const response = await axios.get(
        `${this.baseUrl}/api/v0/webhooks`,
        {
          headers: {
            'x-client-key': this.clientKey,
            'x-client-secret': this.clientSecret
          },
          params: { skip, take }
        }
      );

      return response.data;
    } catch (error) {
      if (error.response) {
        throw new Error(`API Error: ${error.response.data.message}`);
      }
      throw error;
    }
  }

  async getAllWebhooks() {
    const allWebhooks = [];
    let skip = 0;
    const take = 50;

    while (true) {
      const batch = await this.listWebhooks(skip, take);
      
      if (batch.length === 0) break;
      
      allWebhooks.push(...batch);
      
      if (batch.length < take) break;
      
      skip += take;
    }

    return allWebhooks;
  }

  async getWebhookMetrics() {
    const webhooks = await this.getAllWebhooks();
    
    const metrics = webhooks.reduce((acc, webhook) => {
      const stats = webhook.delivery_stats;
      
      return {
        totalWebhooks: acc.totalWebhooks + 1,
        activeWebhooks: acc.activeWebhooks + (webhook.isActive ? 1 : 0),
        encryptedWebhooks: acc.encryptedWebhooks + (webhook.encrypted ? 1 : 0),
        totalEvents: acc.totalEvents + stats.total_events,
        successfulDeliveries: acc.successfulDeliveries + stats.successful_deliveries,
        failedDeliveries: acc.failedDeliveries + stats.failed_deliveries
      };
    }, {
      totalWebhooks: 0,
      activeWebhooks: 0,
      encryptedWebhooks: 0,
      totalEvents: 0,
      successfulDeliveries: 0,
      failedDeliveries: 0
    });

    // Calculate rates
    metrics.successRate = metrics.totalEvents > 0 
      ? (metrics.successfulDeliveries / metrics.totalEvents * 100).toFixed(2)
      : '0.00';
    
    metrics.failureRate = metrics.totalEvents > 0 
      ? (metrics.failedDeliveries / metrics.totalEvents * 100).toFixed(2)
      : '0.00';

    return metrics;
  }
}

// Express routes
const app = express();
const webhookService = new WebhookService(
  process.env.CLIENT_KEY,
  process.env.CLIENT_SECRET,
  'https://api.devdraft.com'
);

// List webhooks endpoint
app.get('/webhooks', async (req, res) => {
  try {
    const skip = parseInt(req.query.skip) || 0;
    const take = parseInt(req.query.take) || 10;
    
    const webhooks = await webhookService.listWebhooks(skip, take);
    
    res.json({
      webhooks,
      pagination: {
        skip,
        take,
        count: webhooks.length
      }
    });
  } catch (error) {
    console.error('Error listing webhooks:', error);
    res.status(500).json({ error: error.message });
  }
});

// Webhook metrics endpoint
app.get('/webhooks/metrics', async (req, res) => {
  try {
    const metrics = await webhookService.getWebhookMetrics();
    res.json(metrics);
  } catch (error) {
    console.error('Error getting webhook metrics:', error);
    res.status(500).json({ error: error.message });
  }
});

// Webhook health check endpoint
app.get('/webhooks/health', async (req, res) => {
  try {
    const webhooks = await webhookService.getAllWebhooks();
    
    const health = {
      healthy: 0,
      warning: 0,
      critical: 0,
      details: []
    };

    webhooks.forEach(webhook => {
      const stats = webhook.delivery_stats;
      let status = 'healthy';
      
      if (stats.total_events > 0) {
        const failureRate = stats.failed_deliveries / stats.total_events;
        
        if (failureRate > 0.1) {
          status = 'critical';
        } else if (failureRate > 0.05) {
          status = 'warning';
        }
      }
      
      health[status]++;
      health.details.push({
        id: webhook.id,
        name: webhook.name,
        status,
        isActive: webhook.isActive,
        failureRate: stats.total_events > 0 
          ? (stats.failed_deliveries / stats.total_events * 100).toFixed(1)
          : '0.0'
      });
    });

    res.json(health);
  } catch (error) {
    console.error('Error checking webhook health:', error);
    res.status(500).json({ error: error.message });
  }
});

Pagination Best PracticesCopied!

1. Efficient Pagination

async function getAllWebhooksPaginated() {
  const allWebhooks = [];
  let skip = 0;
  const take = 50; // Reasonable batch size
  
  while (true) {
    const batch = await fetchWebhooks(skip, take);
    
    if (batch.length === 0) break;
    
    allWebhooks.push(...batch);
    
    // If we got fewer than requested, we've reached the end
    if (batch.length < take) break;
    
    skip += take;
  }
  
  return allWebhooks;
}

2. Progressive Loading

async function loadWebhooksProgressively(onBatch) {
  let skip = 0;
  const take = 20;
  
  while (true) {
    const batch = await fetchWebhooks(skip, take);
    
    if (batch.length === 0) break;
    
    // Process each batch as it arrives
    await onBatch(batch);
    
    if (batch.length < take) break;
    
    skip += take;
  }
}

// Usage
await loadWebhooksProgressively(async (webhooks) => {
  console.log(`Processing batch of ${webhooks.length} webhooks`);
  // Update UI or process data
});

Rate LimitingCopied!

This endpoint is subject to the standard API rate limits:

  • Production: 1000 requests per hour per API key

  • Development: 100 requests per hour per API key

Security ConsiderationsCopied!

  • Scoped Access: Only webhooks belonging to your authenticated application are returned

  • Data Isolation: No access to webhooks from other applications or businesses

  • Credential Security: Always use environment variables for API credentials

  • Monitoring: Regularly review webhook delivery statistics for security anomalies

Performance TipsCopied!

1. Caching

Cache webhook data when appropriate:

const webhookCache = {
  data: null,
  timestamp: null,
  ttl: 5 * 60 * 1000 // 5 minutes
};

async function getCachedWebhooks() {
  const now = Date.now();
  
  if (webhookCache.data && 
      webhookCache.timestamp && 
      (now - webhookCache.timestamp) < webhookCache.ttl) {
    return webhookCache.data;
  }
  
  const webhooks = await fetchWebhooks();
  webhookCache.data = webhooks;
  webhookCache.timestamp = now;
  
  return webhooks;
}

2. Selective Loading

Only load the data you need:

// For overview, load basic info only
const webhookOverview = webhooks.map(w => ({
  id: w.id,
  name: w.name,
  isActive: w.isActive,
  successRate: w.delivery_stats.total_events > 0 
    ? (w.delivery_stats.successful_deliveries / w.delivery_stats.total_events * 100).toFixed(1)
    : '0'
}));