api-reference / webhooks

Webhooks

Event-driven integration for real-time processing and monitoring.

Overview

Webhooks allow gnaw to send real-time notifications when specific events occur, enabling integration with external systems and services.

Webhook Events

Search Events

search.completed

Triggered when a search operation completes.

Payload:

{
  "event": "search.completed",
  "timestamp": "2023-03-15T12:34:58Z",
  "data": {
    "search_term": "ERROR",
    "path": "/var/log/app.log",
    "result_count": 5,
    "execution_time": "0.123s",
    "options": {
      "recursive": true,
      "ignore_case": false
    }
  }
}

search.failed

Triggered when a search operation fails.

Payload:

{
  "event": "search.failed",
  "timestamp": "2023-03-15T12:34:58Z",
  "data": {
    "search_term": "ERROR",
    "path": "/var/log/app.log",
    "error": {
      "code": "FILE_NOT_FOUND",
      "message": "File not found",
      "details": "/var/log/app.log does not exist"
    }
  }
}

Agent Events

agent.index.built

Triggered when a code index is successfully built.

Payload:

{
  "event": "agent.index.built",
  "timestamp": "2023-03-15T12:34:58Z",
  "data": {
    "target_dir": "/path/to/code",
    "index_path": "/path/to/code/.gnaw/index",
    "file_count": 1250,
    "build_time": "45.2s",
    "size_mb": 45.2
  }
}

agent.index.failed

Triggered when index building fails.

Payload:

{
  "event": "agent.index.failed",
  "timestamp": "2023-03-15T12:34:58Z",
  "data": {
    "target_dir": "/path/to/code",
    "error": {
      "code": "PERMISSION_DENIED",
      "message": "Insufficient permissions",
      "details": "Cannot write to /path/to/code/.gnaw/"
    }
  }
}

Monitoring Events

monitor.error_detected

Triggered when errors are detected in monitored files.

Payload:

{
  "event": "monitor.error_detected",
  "timestamp": "2023-03-15T12:34:58Z",
  "data": {
    "file_path": "/var/log/app.log",
    "line_number": 42,
    "line": "2023-03-15 12:34:58,789 ERROR django.request: Internal Server Error",
    "severity": "ERROR",
    "context": {
      "before": "2023-03-15 12:34:56,456 INFO app.config: Loading configuration",
      "after": "2023-03-15 12:34:59,012 INFO app.recovery: Attempting recovery"
    }
  }
}

monitor.pattern_matched

Triggered when specific patterns are matched in monitored files.

Payload:

{
  "event": "monitor.pattern_matched",
  "timestamp": "2023-03-15T12:34:58Z",
  "data": {
    "pattern": "TODO",
    "file_path": "src/main.rs",
    "line_number": 15,
    "line": "// TODO: Implement error handling",
    "context": {
      "before": "pub fn process_data() -> Result<(), Error> {",
      "after": "    Ok(())"
    }
  }
}

Webhook Configuration

Creating Webhooks

POST /api/webhooks

Create a new webhook.

Request:

{
  "name": "Error Monitor",
  "url": "https://your-app.com/webhooks/gnaw",
  "events": ["monitor.error_detected", "monitor.pattern_matched"],
  "secret": "your-webhook-secret",
  "active": true,
  "retry_count": 3,
  "retry_delay": 5
}

Response:

{
  "id": "webhook_123",
  "name": "Error Monitor",
  "url": "https://your-app.com/webhooks/gnaw",
  "events": ["monitor.error_detected", "monitor.pattern_matched"],
  "secret": "your-webhook-secret",
  "active": true,
  "retry_count": 3,
  "retry_delay": 5,
  "created_at": "2023-03-15T12:34:58Z",
  "last_triggered": null
}

Managing Webhooks

GET /api/webhooks

List all webhooks.

Response:

{
  "webhooks": [
    {
      "id": "webhook_123",
      "name": "Error Monitor",
      "url": "https://your-app.com/webhooks/gnaw",
      "events": ["monitor.error_detected"],
      "active": true,
      "created_at": "2023-03-15T12:34:58Z",
      "last_triggered": "2023-03-15T12:35:12Z"
    }
  ]
}

PUT /api/webhooks/{id}

Update a webhook.

Request:

{
  "name": "Updated Error Monitor",
  "active": false
}

DELETE /api/webhooks/{id}

Delete a webhook.

Response:

{
  "message": "Webhook deleted successfully"
}

Webhook Security

Secret Verification

All webhook payloads include a signature for verification:

# Generate signature
signature = HMAC-SHA256(payload, secret)

# Verify signature
expected_signature = HMAC-SHA256(payload, secret)
if signature == expected_signature:
    # Valid webhook
else:
    # Invalid webhook

Signature Header

X-Gnaw-Signature: sha256=abc123def456...

Example Verification

import hmac
import hashlib

def verify_webhook(payload, signature, secret):
    expected_signature = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, expected_signature)

Retry Logic

Automatic Retries

Failed webhook deliveries are automatically retried:

  • Retry Count: Configurable (default: 3)
  • Retry Delay: Exponential backoff (default: 5s, 10s, 20s)
  • Timeout: 30 seconds per attempt

Retry Headers

X-Gnaw-Retry-Count: 2
X-Gnaw-Retry-Delay: 10
X-Gnaw-Retry-After: 20

Integration Examples

Slack Integration

from flask import Flask, request
import hmac
import hashlib
import json

app = Flask(__name__)

@app.route('/webhooks/gnaw', methods=['POST'])
def gnaw_webhook():
    # Verify signature
    signature = request.headers.get('X-Gnaw-Signature')
    if not verify_signature(request.data, signature):
        return 'Unauthorized', 401
    
    # Process webhook
    data = request.json
    event = data['event']
    
    if event == 'monitor.error_detected':
        error_data = data['data']
        message = f"🚨 Error detected in {error_data['file_path']}: {error_data['line']}"
        
        # Send to Slack
        send_slack_message(message)
    
    return 'OK', 200

def verify_signature(payload, signature):
    secret = 'your-webhook-secret'
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, expected)

Discord Integration

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

const app = express();
app.use(express.json());

app.post('/webhooks/gnaw', (req, res) => {
    // Verify signature
    const signature = req.headers['x-gnaw-signature'];
    if (!verifySignature(req.body, signature)) {
        return res.status(401).send('Unauthorized');
    }
    
    // Process webhook
    const { event, data } = req.body;
    
    if (event === 'monitor.error_detected') {
        const message = `🚨 Error detected in ${data.file_path}: ${data.line}`;
        
        // Send to Discord
        sendDiscordMessage(message);
    }
    
    res.status(200).send('OK');
});

function verifySignature(payload, signature) {
    const secret = 'your-webhook-secret';
    const expected = crypto
        .createHmac('sha256', secret)
        .update(JSON.stringify(payload))
        .digest('hex');
    
    return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expected)
    );
}

Database Integration

from flask import Flask, request
import sqlite3
import json

app = Flask(__name__)

@app.route('/webhooks/gnaw', methods=['POST'])
def gnaw_webhook():
    data = request.json
    event = data['event']
    
    # Store in database
    conn = sqlite3.connect('webhooks.db')
    cursor = conn.cursor()
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS webhook_events (
            id INTEGER PRIMARY KEY,
            event TEXT,
            timestamp TEXT,
            data TEXT
        )
    ''')
    
    cursor.execute('''
        INSERT INTO webhook_events (event, timestamp, data)
        VALUES (?, ?, ?)
    ''', (event, data['timestamp'], json.dumps(data['data'])))
    
    conn.commit()
    conn.close()
    
    return 'OK', 200

Best Practices

- Always verify webhook signatures - Use HTTPS endpoints - Implement rate limiting - Monitor for abuse - Implement idempotent handlers - Use appropriate timeouts - Handle retries gracefully - Log all webhook events - Process webhooks asynchronously - Use connection pooling - Implement circuit breakers - Monitor webhook performance Webhooks are ideal for real-time monitoring and event-driven systems. For simple integration, consider using the REST API instead.