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

# Webhooks Local Testing

When developing webhook integrations, you need to test how your application handles Helicone events before deploying to production. Webhooks local testing uses tunneling tools like ngrok to expose your local development server to the internet, allowing Helicone to send real webhook events to your local machine for debugging and integration testing.

## Why use Webhooks Local Testing

* **Debug integration issues**: Test webhook handlers with real events before deploying to production
* **Iterate quickly**: Make changes to your webhook handler and test immediately without deployment cycles
* **Validate event handling**: Ensure your application correctly processes different webhook event types and payloads

## Quick Start

<Steps>
  <Step title="Create webhook handler">
    Set up a local server to receive webhook events:

    ```python theme={null}
    from fastapi import FastAPI, Request
    import json

    app = FastAPI()

    @app.post("/webhook")
    async def webhook_handler(request: Request):
        # Parse the webhook payload
        payload = await request.json()
        
        # Log the event for debugging
        print(f"Received event: {payload['event_type']}")
        print(f"Request ID: {payload['request_id']}")
        
        # Your webhook logic here
        # e.g., store in database, trigger notifications, etc.
        
        return {"status": "success"}

    # Run with: uvicorn main:app --reload --port 8000
    ```

    <Accordion title="Node.js webhook handler example">
      ```javascript theme={null}
      const express = require('express');
      const app = express();

      app.use(express.json());

      app.post('/webhook', (req, res) => {
        const payload = req.body;
        
        console.log(`Received event: ${payload.event_type}`);
        console.log(`Request ID: ${payload.request_id}`);
        
        // Your webhook logic here
        
        res.json({ status: 'success' });
      });

      app.listen(8000, () => {
        console.log('Webhook server running on port 8000');
      });
      ```
    </Accordion>
  </Step>

  <Step title="Set up ngrok tunnel">
    Install and configure ngrok to expose your local server:

    ```bash theme={null}
    # Install ngrok (macOS)
    brew install ngrok

    # Or download from https://ngrok.com/download

    # Start tunnel to your local server
    ngrok http 8000
    ```

    You'll see output like:

    ```
    Forwarding  https://abc123.ngrok-free.app -> http://localhost:8000
    ```

    Copy the HTTPS URL for the next step.
  </Step>

  <Step title="Configure webhook in Helicone">
    Add your ngrok URL to Helicone's webhook settings:

    1. Go to your Helicone dashboard → Settings → Webhooks
    2. Click "Add Webhook"
    3. Enter your ngrok URL with the webhook path: `https://abc123.ngrok-free.app/webhook`
    4. Select the events you want to receive
    5. Save the webhook configuration
  </Step>
</Steps>

## Use Cases

<Tabs>
  <Tab title="Development Testing">
    Test webhook integration during local development:

    <CodeGroup>
      ```python Python theme={null}
      from fastapi import FastAPI, Request, HTTPException
      import json
      import logging

      app = FastAPI()
      logger = logging.getLogger(__name__)

      @app.post("/webhook/helicone")
      async def helicone_webhook(request: Request):
          try:
              payload = await request.json()
              
              # Log full payload for debugging
              logger.info(f"Webhook payload: {json.dumps(payload, indent=2)}")
              
              # Process the webhook payload
              request_id = payload["request_id"]
              model = payload.get("model")
              cost = payload.get("cost", 0)
              user_id = payload.get("user_id")
              
              logger.info(f"Webhook for request {request_id}")
              logger.info(f"Model: {model}, Cost: {cost}")
              
              if user_id:
                  logger.info(f"User: {user_id}")
                  
              # Check if this is a high-cost request
              if cost > 1.0:
                  logger.warning(f"High cost request detected: {cost}")
              
              return {"status": "processed"}
              
          except Exception as e:
              logger.error(f"Webhook error: {str(e)}")
              raise HTTPException(status_code=500, detail=str(e))

      # Test with property filter
      # Add header: Helicone-Property-Environment: development
      ```

      ```javascript Node.js theme={null}
      const express = require('express');
      const app = express();

      app.use(express.json());

      // Webhook endpoint
      app.post('/webhook/helicone', (req, res) => {
        const payload = req.body;
        
        console.log('Webhook received:', JSON.stringify(payload, null, 2));
        
        // Process the webhook payload
        const { request_id, model, cost = 0, user_id } = payload;
        
        console.log(`Webhook for request ${request_id}`);
        console.log(`Model: ${model}, Cost: $${cost}`);
        
        if (user_id) {
          console.log(`User: ${user_id}`);
        }
        
        // Check if this is a high-cost request
        if (cost > 1.0) {
          console.warn(`High cost request detected: $${cost}`);
        }
        
        res.json({ status: 'processed' });
      });

      app.listen(8000, () => {
        console.log('Webhook server running on http://localhost:8000');
      });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Event Processing Pipeline">
    Build a complete webhook processing system:

    ```python theme={null}
    import asyncio
    from fastapi import FastAPI, Request, BackgroundTasks
    import aioredis
    import json

    app = FastAPI()
    redis = None

    @app.on_event("startup")
    async def startup():
        global redis
        redis = await aioredis.create_redis_pool('redis://localhost')

    @app.post("/webhook")
    async def webhook_handler(
        request: Request, 
        background_tasks: BackgroundTasks
    ):
        # Quick acknowledgment
        payload = await request.json()
        
        # Queue for async processing
        await redis.lpush(
            "webhook_queue", 
            json.dumps(payload)
        )
        
        # Process async
        background_tasks.add_task(
            process_webhook_event, 
            payload
        )
        
        return {"status": "queued"}

    async def process_webhook_event(payload):
        """Process webhook events asynchronously"""
        # Process completed request
        cost = payload.get("cost", 0)
        
        # Check for anomalies
        if cost > 1.0:  # High cost threshold
            await send_alert(
                f"High cost request: {cost}",
                payload
            )
        
        # Update metrics
        await update_usage_metrics(payload)
        
        # Check rate limits
        user_id = payload.get("user_id")
        if user_id:
            await check_user_limits(user_id, cost)

    async def send_alert(message, data):
        # Send to Slack, email, etc.
        pass

    async def update_usage_metrics(payload):
        # Update dashboard, analytics, etc.
        pass

    async def check_user_limits(user_id, cost):
        # Implement usage limiting logic
        pass
    ```
  </Tab>
</Tabs>

## Related Features

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/features/webhooks">
    Learn about webhook events, payloads, and production configuration
  </Card>

  <Card title="Custom Properties" icon="tag" href="/features/advanced-usage/custom-properties">
    Use property filters to control which requests trigger webhooks
  </Card>

  <Card title="User Feedback" icon="thumbs-up" href="/features/advanced-usage/feedback">
    Receive webhooks when users provide feedback on responses
  </Card>

  <Card title="Alerts" icon="bell" href="/features/alerts">
    Set up alert rules that trigger webhook notifications
  </Card>
</CardGroup>

<QuestionsSection />
