> ## Documentation Index
> Fetch the complete documentation index at: https://docs.greenflash.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Get notified instantly when quality drops or conversations need attention.

Webhooks let you receive real-time notifications when important events happen in your conversations. Instead of constantly polling our API to check if sentiment dropped or a conversation needs attention, we push the data to you the moment it matters.

## Why webhooks matter

AI products fail in production. A user gets frustrated, sentiment tanks, your quality index drops—and you don't know until it's too late.

Webhooks solve this. They give you instant visibility into quality issues so you can intervene, automate responses, or trigger alerts before damage compounds. Whether you're escalating to human support, logging incidents in your internal systems, or building auto-recovery flows, webhooks allow for the real-time intervention in your AI product.

## Available events

Greenflash tracks quality across conversations and products. You can subscribe to the following events:

### Conversations

* **`conversation.started`**: Triggered when a new conversation starts
* **`conversation.quality_index`**: Triggered when a conversation quality index (CQI) drops below 60%
* **`conversation.sentiment`**: Triggered when a conversation's average sentiment becomes negative
* **`conversation.frustration`**: Triggered when user frustration is detected (score > 0.5)
* **`conversation.struggle`**: Triggered when user struggle is detected (score > 0.5)
* **`conversation.commercial_intent`**: Triggered when commercial intent is detected (score > 0.4 with medium or high label)
* **`conversation.rating`**: Triggered when a user gives a rating for a conversation

### Products

* **`product.quality_index`**: Triggered when a product quality index (PQI) drops below 60
* **`product.sentiment`**: Triggered when product sentiment drops below 60%

### Custom Analyses

* **`custom_analysis.guardrail_triggered`**: Triggered when a guardrail is violated
* **`custom_analysis.expectation_failed`**: Triggered when an expectation check is not met
* **`custom_analysis.evidence_found`**: Triggered when an evidence finder surfaces a match

<Info>
  **Want alerts that match your product?** [Custom Analyses](/features/custom-analyses) let you define your own guardrails, expectations, and evidence finders—then trigger webhooks when they fire.
</Info>

## Set up your webhook endpoint

Create a webhook endpoint in your settings, choose which events you want to subscribe to, and we'll start pushing event payloads to your URL.

Your endpoint should:

1. Respond with a `2xx` status code within 5 seconds
2. Verify the webhook signature (see below)
3. Process events asynchronously to avoid timeouts

We'll retry failed deliveries with exponential backoff, but reliable endpoints are key to staying on top of quality issues in real time.

## Verify webhook signatures

Every webhook we send includes three headers you must verify to ensure the request actually came from Greenflash:

* **`X-Greenflash-Signature`**: HMAC signature of the payload
* **`X-Greenflash-Timestamp`**: Unix timestamp when the webhook was sent
* **`X-Greenflash-Delivery`**: Unique delivery ID for this webhook

Here's how to verify them securely in TypeScript and Python:

<Tabs>
  <Tab title="TypeScript">
    ```typescript verify-webhook.ts theme={"theme":{"light":"github-light","dark":"vesper"}}
    import crypto from 'crypto';

    function verifyWebhook(
      body: object,         // Your parsed webhook payload
      signature: string,    // X-Greenflash-Signature header
      timestamp: string,    // X-Greenflash-Timestamp header
      deliveryId: string,   // X-Greenflash-Delivery header
      secret: string        // Your webhook signing secret
    ): boolean {
      // Convert body to canonical JSON string (no whitespace)
      const bodyString = JSON.stringify(body);

      // Hash the body
      const bodyHash = crypto
        .createHmac('sha256', secret)
        .update(bodyString)
        .digest('hex');

      // Reconstruct the signed string
      const canonical = `${timestamp}.${deliveryId}.${bodyHash}`;

      // Compute expected signature
      const digest = crypto
        .createHmac('sha256', secret)
        .update(canonical)
        .digest('hex');

      const expected = `v1=${digest}`;

      // Compare securely (prevents timing attacks)
      return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expected)
      );
    }

    // Example usage
    app.post('/webhook', (req, res) => {
      const signature = req.headers['x-greenflash-signature'];
      const timestamp = req.headers['x-greenflash-timestamp'];
      const deliveryId = req.headers['x-greenflash-delivery'];

      const isValid = verifyWebhook(
        req.body,
        signature,
        timestamp,
        deliveryId,
        process.env.WEBHOOK_SECRET
      );

      if (!isValid) {
        return res.status(401).json({ error: 'Invalid signature' });
      }

      // Process webhook...
      res.json({ received: true });
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python verify_webhook.py theme={"theme":{"light":"github-light","dark":"vesper"}}
    import hmac
    import hashlib
    import json

    def verify_webhook(
        body: dict,           # Your parsed webhook payload
        signature: str,       # X-Greenflash-Signature header
        timestamp: str,       # X-Greenflash-Timestamp header
        delivery_id: str,     # X-Greenflash-Delivery header
        secret: str           # Your webhook signing secret
    ) -> bool:
        # Convert body to canonical JSON string (no whitespace)
        body_string = json.dumps(body, separators=(',', ':'), sort_keys=False)

        # Hash the body
        body_hash = hmac.new(
            secret.encode(),
            body_string.encode(),
            hashlib.sha256
        ).hexdigest()

        # Reconstruct signed string
        canonical = f"{timestamp}.{delivery_id}.{body_hash}"

        # Compute expected signature
        digest = hmac.new(
            secret.encode(),
            canonical.encode(),
            hashlib.sha256
        ).hexdigest()
        expected = f"v1={digest}"

        # Compare securely (prevents timing attacks)
        return hmac.compare_digest(signature, expected)


    # Example usage with Flask
    @app.route('/webhook', methods=['POST'])
    def webhook():
        signature = request.headers.get('X-Greenflash-Signature')
        timestamp = request.headers.get('X-Greenflash-Timestamp')
        delivery_id = request.headers.get('X-Greenflash-Delivery')

        is_valid = verify_webhook(
            request.json,
            signature,
            timestamp,
            delivery_id,
            os.environ['WEBHOOK_SECRET']
        )

        if not is_valid:
            return {'error': 'Invalid signature'}, 401

        # Process webhook...
        return {'received': True}
    ```
  </Tab>
</Tabs>

<Warning>
  Always verify webhook signatures before processing the payload. Skipping verification exposes your endpoint to spoofed requests and potential security vulnerabilities.
</Warning>

## Test your endpoint

Once you've set up your webhook endpoint, use the "Test Delivery" button in settings to send a sample payload and verify everything works. You'll see the response status, headers, and body so you can debug before real events start flowing.

## Best practices

* **Respond quickly**: Acknowledge webhooks with a `200 OK` immediately, then process events in the background
* **Monitor failures**: Check your webhook logs in settings to catch and fix delivery issues fast
* **Validate timestamps**: Reject webhooks older than 5 minutes to prevent replay attacks

## What's next?

Now that you're receiving real-time quality events, you can:

* Auto-escalate low-sentiment conversations to human support
* Log quality incidents in your internal monitoring systems
* Build custom alerting workflows based on PQI drops
* Trigger automated recovery flows when things go wrong

Webhooks turn passive quality monitoring into active intervention. Use them to catch problems before your users give up.
