Skip to main content

Webhooks

Webhooks allow you to receive real-time HTTP notifications when events occur in your FLTR account. Build event-driven workflows without polling the API.

Overview

FLTR webhooks send HTTP POST requests to your endpoint when:
  • Documents are uploaded or deleted
  • Datasets are created, updated, or deleted
  • Document processing completes
  • Errors occur during processing
Coming Soon: The webhook system is currently in design phase. This documentation describes the planned functionality. Contact us to join the beta.

Use Cases

Processing Notifications

Get notified when document indexing completes

Sync Systems

Keep external systems in sync with FLTR changes

Trigger Workflows

Start automation workflows on FLTR events

Monitor Activity

Track dataset usage and changes in real-time

Event Types

document.uploaded

Fired when a new document is successfully uploaded.
{
  "event": "document.uploaded",
  "timestamp": "2024-01-10T12:00:00Z",
  "data": {
    "document_id": "doc_abc123",
    "dataset_id": "ds_xyz789",
    "filename": "product-docs.pdf",
    "size_bytes": 1048576,
    "status": "processing",
    "metadata": {
      "title": "Product Documentation",
      "category": "docs"
    }
  }
}

document.processed

Fired when document chunking and embedding is complete.
{
  "event": "document.processed",
  "timestamp": "2024-01-10T12:01:30Z",
  "data": {
    "document_id": "doc_abc123",
    "dataset_id": "ds_xyz789",
    "chunks_created": 47,
    "processing_time_ms": 8450,
    "status": "ready",
    "embedding_model": "text-embedding-3-small"
  }
}

document.deleted

Fired when a document is deleted.
{
  "event": "document.deleted",
  "timestamp": "2024-01-10T12:05:00Z",
  "data": {
    "document_id": "doc_abc123",
    "dataset_id": "ds_xyz789",
    "deleted_by": "user_xyz123"
  }
}

dataset.created

Fired when a new dataset is created.
{
  "event": "dataset.created",
  "timestamp": "2024-01-10T11:00:00Z",
  "data": {
    "dataset_id": "ds_xyz789",
    "name": "Product Knowledge Base",
    "description": "Documentation for all products",
    "is_public": false,
    "created_by": "user_xyz123"
  }
}

dataset.updated

Fired when dataset metadata is updated.
{
  "event": "dataset.updated",
  "timestamp": "2024-01-10T12:10:00Z",
  "data": {
    "dataset_id": "ds_xyz789",
    "changes": {
      "name": "Updated Product KB",
      "description": "All product documentation"
    },
    "updated_by": "user_xyz123"
  }
}

dataset.deleted

Fired when a dataset is deleted.
{
  "event": "dataset.deleted",
  "timestamp": "2024-01-10T13:00:00Z",
  "data": {
    "dataset_id": "ds_xyz789",
    "document_count": 42,
    "deleted_by": "user_xyz123"
  }
}

document.processing_failed

Fired when document processing encounters an error.
{
  "event": "document.processing_failed",
  "timestamp": "2024-01-10T12:02:00Z",
  "data": {
    "document_id": "doc_abc123",
    "dataset_id": "ds_xyz789",
    "error": {
      "code": "invalid_pdf",
      "message": "PDF file is corrupted or encrypted",
      "details": "Unable to extract text from PDF"
    }
  }
}

Creating Webhooks

Via Dashboard (Coming Soon)

  1. Log in to www.tryfltr.com
  2. Go to SettingsWebhooks
  3. Click Create Webhook
  4. Configure:
    • URL: Your endpoint (must be HTTPS in production)
    • Events: Select which events to receive
    • Secret: Auto-generated signature secret
  5. Click Create

Via API (Planned)

curl -X POST https://api.fltr.com/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/fltr",
    "events": [
      "document.uploaded",
      "document.processed",
      "document.processing_failed"
    ],
    "description": "Production webhook",
    "active": true
  }'
Response:
{
  "webhook_id": "wh_abc123",
  "url": "https://your-app.com/webhooks/fltr",
  "events": ["document.uploaded", "document.processed", "document.processing_failed"],
  "secret": "whsec_xyz789...",
  "created_at": "2024-01-10T12:00:00Z",
  "active": true
}

Receiving Webhooks

Basic Endpoint

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

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_xyz789..."

@app.route('/webhooks/fltr', methods=['POST'])
def fltr_webhook():
    # Verify signature
    signature = request.headers.get('X-FLTR-Signature')
    body = request.get_data()

    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return jsonify({'error': 'Invalid signature'}), 401

    # Process event
    event = request.json

    if event['event'] == 'document.processed':
        handle_document_processed(event['data'])
    elif event['event'] == 'document.processing_failed':
        handle_processing_error(event['data'])

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

def handle_document_processed(data):
    print(f"Document {data['document_id']} processed with {data['chunks_created']} chunks")
    # Your logic here

def handle_processing_error(data):
    print(f"Processing failed: {data['error']['message']}")
    # Send alert, retry, etc.

Security

Signature Verification

FLTR signs every webhook with HMAC-SHA256:
X-FLTR-Signature: <hex-encoded-signature>
Always verify signatures to ensure requests came from FLTR:
import hmac
import hashlib

def verify_signature(secret, signature, body):
    expected = hmac.new(
        secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected)

# Usage
if not verify_signature(WEBHOOK_SECRET, signature, request.body):
    raise Exception("Invalid signature")

Best Practices

FLTR will only send webhooks to HTTPS endpoints in production. HTTP is allowed for development/testing with localhost URLs only.
Never trust webhook payloads without signature verification. Attackers could forge requests.
Rotate secrets periodically (every 90 days) or immediately if compromised.
Respond with 200 OK within 5 seconds. Process events asynchronously:
@app.route('/webhooks/fltr', methods=['POST'])
def webhook():
    # Verify signature
    if not verify_signature(...):
        return jsonify({'error': 'Invalid'}), 401

    # Queue for async processing
    queue.enqueue(process_webhook, request.json)

    # Return immediately
    return jsonify({'received': True}), 200
Use event IDs to detect and ignore duplicates:
processed_events = set()

def handle_event(event):
    event_id = event['id']  # Each event has unique ID

    if event_id in processed_events:
        return  # Already processed

    # Process event
    process(event)

    # Mark as processed
    processed_events.add(event_id)

Retry Logic

How Retries Work

If your endpoint fails (non-200 status or timeout), FLTR will retry:
  • Retry attempts: Up to 3 retries
  • Backoff: Exponential (1min, 5min, 30min)
  • Timeout: 5 seconds per request
  • Failure: Webhook marked as failed after 3 failures

Handling Retries

Return these status codes appropriately:
CodeMeaningFLTR Action
200-299SuccessMark delivered, no retry
400-499Client errorNo retry (likely bad webhook config)
500-599Server errorRetry with backoff
TimeoutNo responseRetry with backoff
Example: Temporary vs Permanent Errors
@app.route('/webhooks/fltr', methods=['POST'])
def webhook():
    try:
        process_event(request.json)
        return jsonify({'received': True}), 200

    except TemporaryError as e:
        # Retry - return 500
        return jsonify({'error': str(e)}), 503

    except PermanentError as e:
        # Don't retry - return 400
        return jsonify({'error': str(e)}), 400

Testing Webhooks

Local Testing with ngrok

Expose local endpoint for testing:
# Start your local server
python app.py  # Running on :5000

# Start ngrok
ngrok http 5000

# Use ngrok URL as webhook endpoint
# https://abc123.ngrok.io/webhooks/fltr

Manual Testing

Simulate webhook events:
curl -X POST http://localhost:5000/webhooks/fltr \
  -H "Content-Type: application/json" \
  -H "X-FLTR-Signature: $(echo -n '{"event":"document.processed"}' | openssl dgst -sha256 -hmac 'whsec_xyz789' | cut -d' ' -f2)" \
  -d '{
    "event": "document.processed",
    "timestamp": "2024-01-10T12:00:00Z",
    "data": {
      "document_id": "doc_test123",
      "dataset_id": "ds_test456",
      "chunks_created": 10,
      "status": "ready"
    }
  }'

Test Mode (Coming Soon)

FLTR will provide test mode webhooks:
curl -X POST https://api.fltr.com/v1/webhooks/test \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "webhook_id": "wh_abc123",
    "event": "document.processed"
  }'

Monitoring Webhooks

Webhook Logs (Planned)

View webhook delivery attempts in the dashboard:
  • Delivery status (success/failed)
  • Response codes and times
  • Error messages
  • Retry attempts
  • Event payloads

Alerts

Set up alerts for webhook failures:
@app.route('/webhooks/fltr', methods=['POST'])
def webhook():
    try:
        process_event(request.json)
        return jsonify({'received': True}), 200

    except Exception as e:
        # Alert your team
        send_alert(f"Webhook processing failed: {str(e)}")

        # Return 500 to trigger retry
        return jsonify({'error': 'Internal error'}), 500

Common Use Cases

1. Slack Notifications

Get notified when documents are processed:
import requests

def handle_document_processed(data):
    requests.post(
        'https://hooks.slack.com/services/YOUR/WEBHOOK/URL',
        json={
            'text': f"✅ Document processed: {data['document_id']}",
            'attachments': [{
                'fields': [
                    {'title': 'Chunks', 'value': str(data['chunks_created'])},
                    {'title': 'Time', 'value': f"{data['processing_time_ms']}ms"}
                ]
            }]
        }
    )

2. Database Sync

Keep external database in sync:
def handle_dataset_created(data):
    db.datasets.insert({
        'fltr_id': data['dataset_id'],
        'name': data['name'],
        'description': data['description'],
        'synced_at': datetime.now()
    })

def handle_dataset_deleted(data):
    db.datasets.delete({
        'fltr_id': data['dataset_id']
    })

3. Workflow Trigger

Start automation on document upload:
def handle_document_uploaded(data):
    # Trigger workflow (e.g., Zapier webhook)
    requests.post(
        'https://hooks.zapier.com/hooks/catch/...',
        json={
            'document_id': data['document_id'],
            'dataset_id': data['dataset_id'],
            'action': 'process_new_document'
        }
    )

4. Error Monitoring

Track processing failures:
def handle_processing_failed(data):
    # Log to error tracking service
    sentry_sdk.capture_message(
        f"FLTR document processing failed",
        level="error",
        extras={
            'document_id': data['document_id'],
            'error_code': data['error']['code'],
            'error_message': data['error']['message']
        }
    )

    # Retry or alert team
    if data['error']['code'] == 'temporary_error':
        queue_retry(data['document_id'])
    else:
        alert_team(data)

Webhook Headers

FLTR sends these headers with every webhook:
Content-Type: application/json
X-FLTR-Signature: <hmac-sha256-signature>
X-FLTR-Event: <event-type>
X-FLTR-Delivery: <delivery-attempt-id>
User-Agent: FLTR-Webhooks/1.0
Access in your handler:
event_type = request.headers.get('X-FLTR-Event')
delivery_id = request.headers.get('X-FLTR-Delivery')

Rate Limits

Webhook deliveries don’t count toward your API rate limit. However:
  • Max 100 webhooks per account
  • Max 1000 deliveries per hour per webhook
  • Webhooks failing >80% over 24h will be auto-disabled

Next Steps