fegura.ai

Webhooks

Webhooks allow you to receive real-time notifications when events occur in your Fegura account. Instead of polling the API, webhooks push event data directly to your server as they happen.

How Webhooks Work

  1. You register a webhook endpoint URL in your Fegura settings
  2. Subscribe to the events you want to receive
  3. When an event occurs, Fegura sends an HTTP POST request to your URL
  4. Your server processes the event and returns a 2xx response

Event Types

Fegura supports the following webhook events:

EventDescription
scan.completedFired when an infrastructure scan finishes successfully
diagram.generatedFired when a diagram is generated from a scan

Webhook Payload

All webhook payloads follow a consistent structure with the event type, timestamp, and event-specific data:

{
  "event": "scan.completed",
  "timestamp": "2026-01-20T14:32:15.000Z",
  "data": {
    "scanId": "660e8400-e29b-41d4-a716-446655440000",
    "accountId": "550e8400-e29b-41d4-a716-446655440000",
    "resourceCount": 47,
    "duration": 125000
  }
}

Common Fields

ParameterTypeDescription
eventstringThe event type that triggered this webhook
timestampstringISO 8601 timestamp when the event occurred
dataobjectEvent-specific payload data

scan.completed Data

ParameterTypeDescription
scanIdstringID of the completed scan
accountIdstringFegura account connection ID
resourceCountintegerNumber of resources discovered
durationintegerScan duration in milliseconds

diagram.generated Data

ParameterTypeDescription
diagramIdstringID of the generated diagram
scanIdstringID of the scan that produced this diagram
accountIdstringFegura account connection ID
typestringDiagram type (architecture, network, dataflow, boundary, organization)

Webhook Signatures

Every webhook request includes a signature header that you should verify to ensure the request came from Fegura. This protects against spoofed requests.

Webhook Headers

Every webhook request includes the following headers:

ParameterTypeDescription
X-Fegura-SignaturestringHMAC-SHA256 signature of the request body (sha256=...)
X-Fegura-EventstringThe event type (e.g., scan.completed, diagram.generated)
X-Fegura-Delivery-IdstringUnique delivery ID for idempotency (UUID)
X-Fegura-Signature: sha256=5d41402abc4b2a76b9719d911017c592
X-Fegura-Event: scan.completed
X-Fegura-Delivery-Id: 880e8400-e29b-41d4-a716-446655440000

Verifying Signatures

To verify a webhook signature, compute the HMAC-SHA256 hash of the raw request body using your webhook secret, then compare it to the signature header.

webhook-handler.ts
import crypto from 'crypto';

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expectedSignature = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  // Use constant-time comparison to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
app.post('/webhooks/fegura', (req, res) => {
  const signature = req.headers['x-fegura-signature'];
  const payload = req.rawBody; // Raw request body as string

  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook
  const event = JSON.parse(payload);
  console.log('Received event:', event.event);

  res.status(200).send('OK');
});

Python Example

webhook_handler.py
import hmac
import hashlib

def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

# In your Flask webhook handler
@app.route('/webhooks/fegura', methods=['POST'])
def webhook_handler():
    signature = request.headers.get('X-Fegura-Signature')
    payload = request.get_data()

    if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    event = request.get_json()
    print(f"Received event: {event['event']}")

    return 'OK', 200

Retry Policy

If your endpoint doesn't return a 2xx status code, Fegura will automatically retry delivery with exponential backoff. Retries are managed by an SQS queue, so you don't need to configure anything on your end.

Best Practices

  • Always verify signatures - Never process webhooks without verifying the signature first.
  • Respond quickly - Return a 2xx response as soon as possible. Process the event asynchronously if needed.
  • Handle duplicates - Webhooks may be delivered more than once. Use the event ID for idempotency.
  • Use HTTPS - Webhook endpoints must use HTTPS for security.
  • Monitor failures - Check your webhook settings for delivery failures and errors.

Managing Webhooks

Webhooks can be managed via the API or from your Webhook Settings page.

MethodEndpointDescription
POST/api/webhooksCreate a new webhook endpoint
GET/api/webhooksList all webhook endpoints
GET/api/webhooks/{id}Get a specific webhook
PATCH/api/webhooks/{id}Update a webhook endpoint
DELETE/api/webhooks/{id}Delete a webhook endpoint
POST/api/webhooks/{id}/regenerate-secretRegenerate the webhook signing secret
POST/api/webhooks/{id}/testSend a test webhook event