March 2025 Update: We’ve enhanced our webhook implementation to provide a unified request_response_url field that contains both request and response data in a single object. This improves performance and simplifies data retrieval. Learn more.
When building LLM applications, you often need to react to events in real-time, track usage patterns, or trigger downstream actions based on AI interactions. Webhooks provide instant notifications when LLM requests complete, allowing you to automate workflows, score responses, and integrate AI activity with external systems.

Why use Webhooks

  • Real-time evaluation: Automatically score and evaluate LLM responses for quality, safety, and relevance
  • Data pipeline integration: Stream LLM data to external systems, data warehouses, or analytics platforms
  • Automated workflows: Trigger downstream actions like notifications, content moderation, or process automation

Quick Start

1

Set up webhook endpoint

Navigate to the webhooks page and add your webhook URL:webhook name
Your webhook endpoint should accept POST requests.
2

Configure events and filters

Select which events trigger webhooks and add any property filters:webhook configuration
You can also create webhooks programmatically using our REST API.
3

Secure your endpoint

Copy the HMAC key from the dashboard and validate webhook signatures:
import crypto from "crypto";

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac("sha256", secret);
  hmac.update(JSON.stringify(payload));
  const calculatedSignature = hmac.digest("hex");
  
  return crypto.timingSafeEqual(
    Buffer.from(calculatedSignature, "hex"),
    Buffer.from(signature, "hex")
  );
}

Configuration Options

Configure your webhook behavior through the dashboard or REST API:

Basic Settings

SettingDescriptionDefault
Destination URLURL where webhook payloads are sentNone
Sample RatePercentage of requests that trigger webhooks (0-100)100%
Include DataInclude enhanced metadata and S3 URLsEnabled

Advanced Settings

SettingDescriptionDefault
Property FiltersOnly send webhooks for requests with specific propertiesNone

Use Cases

Monitor AI responses for regulatory compliance and policy violations:
export default async function handler(req, res) {
  const { request_id, request_response_url, user_id, metadata } = req.body;
  
  // Verify webhook signature
  if (!verifySignature(req.body, req.headers["helicone-signature"], process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: "Unauthorized" });
  }
  
  // Fetch complete interaction data
  const response = await fetch(request_response_url);
  const { request, response: llmResponse } = await response.json();
  
  const userMessage = request.messages[0].content;
  const aiResponse = llmResponse.choices[0].message.content;
  
  // Check for PII in user input
  const piiDetected = await detectPII(userMessage);
  if (piiDetected.found) {
    await complianceAlerts.sendPIIAlert({
      requestId: request_id,
      userId: user_id,
      piiTypes: piiDetected.types,
      content: userMessage
    });
  }
  
  // Monitor AI response for policy violations
  const policyCheck = await checkCompliancePolicy(aiResponse);
  if (policyCheck.violations.length > 0) {
    await complianceAlerts.sendPolicyViolation({
      requestId: request_id,
      violations: policyCheck.violations,
      severity: policyCheck.severity,
      content: aiResponse
    });
  }
  
  // Log compliance metrics
  await complianceLogger.log({
    requestId: request_id,
    timestamp: new Date().toISOString(),
    piiDetected: piiDetected.found,
    policyViolations: policyCheck.violations.length,
    complianceScore: policyCheck.score
  });
  
  return res.status(200).json({ message: "Compliance check completed" });
}

Understanding Webhooks

Webhook Payload Structure

Webhooks deliver structured data about completed LLM requests: Standard payload:
{
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "user_id": "user-123", // Only if set in original request
  "request_body": "truncated-request-data",
  "response_body": "truncated-response-data"
}
Enhanced payload (when include_data is enabled):
{
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "user_id": "user-123",
  "request_body": "truncated-request-data", 
  "response_body": "truncated-response-data",
  "request_response_url": "https://s3-url-containing-full-data",
  "model": "gpt-4o-mini",
  "provider": "openai",
  "metadata": {
    "cost": 0.0015,
    "promptTokens": 10,
    "completionTokens": 15,
    "totalTokens": 25,
    "latencyMs": 1200
  }
}

Request/Response URL Data

The request_response_url contains complete, untruncated data:
// Fetch complete data
const response = await fetch(request_response_url);
const { request, response: llmResponse } = await response.json();

// Access full request data
console.log("Model:", request.model);
console.log("Messages:", request.messages);
console.log("Parameters:", request.temperature, request.max_tokens);

// Access full response data  
console.log("Response:", llmResponse.choices[0].message.content);
console.log("Usage:", llmResponse.usage);
console.log("Finish reason:", llmResponse.choices[0].finish_reason);

Security Best Practices

Always verify webhook signatures:
function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac("sha256", secret);
  hmac.update(JSON.stringify(payload));
  const calculatedSignature = hmac.digest("hex");
  
  return crypto.timingSafeEqual(
    Buffer.from(calculatedSignature, "hex"),
    Buffer.from(signature, "hex")
  );
}

// In your webhook handler
const isValid = verifyWebhookSignature(
  req.body,
  req.headers["helicone-signature"],
  process.env.HELICONE_WEBHOOK_SECRET
);

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

Performance Considerations

URL expiration:
  • request_response_url expires after 30 minutes
  • Always use request_response_url for complete data
Webhook timeouts:
  • Webhook delivery times out after 2 minutes
Payload size limits:
  • Request/response bodies are truncated at 10KB in webhook payload