Skip to content

Webhooks

Receive real-time notifications when events occur in Converra.

Setup

1. Register a Webhook

typescript
const webhook = await converra.webhooks.create({
  url: 'https://your-app.com/webhooks/converra',
  events: ['optimization.completed', 'prompt.updated', 'insights.generated'],
  description: 'Production webhook'
});

// Save the secret - only shown once!
console.log('Webhook secret:', webhook.secret);

2. Create a Handler

typescript
import { createWebhookHandler } from 'converra';

const handler = createWebhookHandler({
  secret: process.env.CONVERRA_WEBHOOK_SECRET!,

  onPromptUpdated: (data) => {
    console.log(`Prompt ${data.promptId} updated`);
  },

  onOptimizationCompleted: (data) => {
    console.log(`Optimization complete: ${data.results.improvementPercentage}% improvement`);
  },
});

3. Register the Endpoint

Express:

typescript
app.post('/webhooks/converra', express.raw({ type: 'application/json' }), async (req, res) => {
  const result = await handler(req.body, req.headers['x-converra-signature']);
  res.status(result.success ? 200 : 400).json(result);
});

Next.js App Router:

typescript
export async function POST(req: Request) {
  const body = await req.text();
  const signature = req.headers.get('x-converra-signature') || '';
  const result = await handler(body, signature);
  return Response.json(result, { status: result.success ? 200 : 400 });
}

Available Events

EventDescription
prompt.updatedPrompt content changed (e.g., variant applied)
prompt.deletedPrompt was deleted
optimization.startedOptimization process began
optimization.completedOptimization finished with results
optimization.stoppedOptimization was manually stopped
insights.generatedNew insights available for a conversation
conversation.createdNew conversation logged

Handler Options

typescript
const handler = createWebhookHandler({
  secret: process.env.CONVERRA_WEBHOOK_SECRET!,

  // Prompt events
  onPromptUpdated: (data) => {
    // data: { promptId, promptName, updateType, variantId?, appliedAt }
  },
  onPromptDeleted: (data) => {
    // data: { promptId, deletedAt }
  },

  // Optimization events
  onOptimizationStarted: (data) => {
    // data: { optimizationId, promptId, status, settings, startedAt }
  },
  onOptimizationCompleted: (data) => {
    // data: { optimizationId, promptId, results, duration, completedAt }
  },
  onOptimizationStopped: (data) => {
    // data: { optimizationId, promptId, reason, stoppedAt }
  },

  // Insight events
  onInsightsGenerated: (data) => {
    // data: { conversationId, promptId, insightsId, summary, sentiment }
  },

  // Conversation events
  onConversationCreated: (data) => {
    // data: { conversationId, promptId, status, createdAt }
  },

  // Catch-all handler
  onEvent: (event, data, metadata) => {
    console.log(`Received ${event}`, data);
  },

  // Error handler
  onError: (error, event) => {
    console.error(`Webhook error for ${event}:`, error);
  },
});

Signature Verification

The SDK automatically verifies webhook signatures using HMAC-SHA256. Always pass the raw request body (not parsed JSON) to ensure verification works correctly.

typescript
// Correct - raw body
app.post('/webhooks/converra', express.raw({ type: 'application/json' }), handler);

// Wrong - parsed body will fail signature verification
app.post('/webhooks/converra', express.json(), handler);

Common Pattern: Cache Invalidation

typescript
const handler = createWebhookHandler({
  secret: process.env.CONVERRA_WEBHOOK_SECRET!,
  onPromptUpdated: (data) => {
    converra.cache.invalidate(data.promptId);
    console.log(`Cache invalidated for prompt ${data.promptId}`);
  },
});