Skip to main content

What Are Webhooks?

Webhooks allow you to receive real-time HTTP notifications whenever specific events occur in your Mindhunters workspace. Instead of continuously polling the API for updates, Mindhunters will send POST requests to your specified endpoint URL when events happen.

Why Use Webhooks?

Real-time Updates

Get instant notifications when conversations end, status changes occur, or intents are triggered.

Reduced API Calls

No need to poll the API repeatedly. Webhooks push data to you automatically.

Event-Driven Architecture

Build reactive applications that respond immediately to conversation events.

Custom Integration

Integrate Mindhunters with your existing systems and workflows seamlessly.

Setting Up Webhooks

Prerequisites

Before configuring webhooks, ensure you have:
  • Admin or developer access to your Mindhunters workspace
  • A publicly accessible HTTPS endpoint to receive webhook events
  • (Recommended) A secret key for webhook signature verification

Configuration Steps

1

Access Developer Settings

  1. Log in to your Mindhunters workspace at https://your-tenant.mindhunters.ai
  2. Click on your profile icon in the top right corner
  3. Select “Developer” from the dropdown menu
2

Navigate to Webhooks

In the Developer section, find the “Webhooks” configuration area.
3

Enter Your Webhook URL

  1. In the “Webhook URL” field, enter your endpoint URL
  2. This must be a publicly accessible HTTPS URL
  3. Example: https://api.yourcompany.com/webhooks/mihu
HTTP URLs are not supported for security reasons. Your endpoint must use HTTPS.
4

Add Secret Key (Recommended)

  1. Enter a “Secret Key” in the provided field
  2. This key will be used to sign webhook payloads
  3. Store this key securely - you’ll need it to verify webhook signatures
  4. Use a strong, random string (minimum 32 characters recommended)
Generate a secure secret key using: openssl rand -hex 32
5

Select Event Types

Choose which events you want to receive notifications for:
  • Conversation Update: Real-time updates during conversations
  • Conversation End Report: Detailed report when conversation completes
  • Conversation Status: Status changes (initiated, in-progress, ended)
  • Intent Call: Triggered when specific intents are detected
  • Text Evaluation: Results from text-based evaluations
  • Voice Evaluation: Results from voice conversation evaluations
Select only the events you need to reduce unnecessary webhook traffic.
6

Save Configuration

Click “Save” or “Update” to activate your webhook configuration.

Available Event Types

Conversation Update

Sent in real-time as the conversation progresses. Useful for live monitoring and transcription display. Event Type: conversation.update
Example Payload
{
  "event": "conversation.update",
  "timestamp": "2025-01-15T10:30:45Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "agentId": "agent-uuid-here",
    "status": "in-progress",
    "participant": {
      "number": "+1234567890",
      "name": "John Doe"
    },
    "transcript": [
      {
        "speaker": "agent",
        "text": "Hello, how can I help you today?",
        "timestamp": "2025-01-15T10:30:10Z"
      },
      {
        "speaker": "participant",
        "text": "I need help with my account.",
        "timestamp": "2025-01-15T10:30:35Z"
      }
    ],
    "currentSentiment": "neutral"
  }
}

Conversation End Report

Sent when a conversation completes. Contains comprehensive conversation analytics. Event Type: conversation.end
Example Payload
{
  "event": "conversation.end",
  "timestamp": "2025-01-15T10:35:00Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "agentId": "agent-uuid-here",
    "status": "completed",
    "duration": 285,
    "participant": {
      "number": "+1234567890",
      "name": "John Doe"
    },
    "summary": {
      "outcome": "resolved",
      "sentiment": "positive",
      "intentsDetected": ["account_inquiry", "billing_question"],
      "resolutionTime": 285,
      "customerSatisfaction": 4.5
    },
    "fullTranscript": "...",
    "recording": {
      "url": "https://recordings.mindhunters.ai/...",
      "duration": 285
    },
    "metadata": {
      "campaign": "customer-support",
      "customerId": "12345"
    }
  }
}

Conversation Status

Sent when conversation status changes (initiated → in-progress → ended/failed/missed). Event Type: conversation.status
Example Payload
{
  "event": "conversation.status",
  "timestamp": "2025-01-15T10:30:15Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "agentId": "agent-uuid-here",
    "previousStatus": "initiated",
    "currentStatus": "in-progress",
    "participant": {
      "number": "+1234567890"
    },
    "statusChangedAt": "2025-01-15T10:30:15Z"
  }
}

Intent Call

Triggered when a specific intent is detected during the conversation. Useful for triggering actions or integrations. Event Type: intent.triggered
Example Payload
{
  "event": "intent.triggered",
  "timestamp": "2025-01-15T10:32:00Z",
  "data": {
    "conversationId": "550e8400-e29b-41d4-a716-446655440000",
    "intent": {
      "name": "transfer_to_human",
      "confidence": 0.95,
      "parameters": {
        "department": "billing",
        "priority": "high"
      }
    },
    "context": {
      "lastUserMessage": "I need to speak with someone about my bill",
      "conversationTurn": 5
    }
  }
}

Text Evaluation

Results from text-based conversation evaluations. Event Type: evaluation.text
Example Payload
{
  "event": "evaluation.text",
  "timestamp": "2025-01-15T10:35:30Z",
  "data": {
    "conversationId": "550e8400-e29b-41d4-a716-446655440000",
    "evaluationType": "quality_assurance",
    "scores": {
      "accuracy": 0.92,
      "relevance": 0.88,
      "completeness": 0.95
    },
    "feedback": "Conversation handled professionally with accurate information.",
    "recommendations": [
      "Consider offering callback option earlier"
    ]
  }
}

Voice Evaluation

Results from voice conversation quality evaluations. Event Type: evaluation.voice
Example Payload
{
  "event": "evaluation.voice",
  "timestamp": "2025-01-15T10:35:45Z",
  "data": {
    "conversationId": "550e8400-e29b-41d4-a716-446655440000",
    "evaluationType": "voice_quality",
    "scores": {
      "clarity": 0.94,
      "naturalness": 0.89,
      "emotionalTone": 0.87,
      "audioQuality": 0.96
    },
    "metrics": {
      "averageResponseTime": 1.2,
      "silenceDuration": 5.3,
      "interruptionCount": 2
    }
  }
}

Webhook Payload Structure

All webhook payloads follow a consistent structure:
{
  "event": "event.type",
  "timestamp": "ISO 8601 timestamp",
  "data": {
    // Event-specific data
  },
  "signature": "webhook-signature-hash"
}
event
string
required
The type of event that triggered the webhook
timestamp
string
required
ISO 8601 formatted timestamp when the event occurred
data
object
required
Event-specific payload containing relevant information
signature
string
HMAC-SHA256 signature for payload verification (when secret key is configured)

Security & Verification

Verifying Webhook Signatures

When you configure a secret key, Mindhunters signs each webhook payload with an HMAC-SHA256 signature. Always verify this signature to ensure the webhook is genuine.
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  const expectedSignature = hmac.update(JSON.stringify(payload)).digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Express.js middleware example
app.post('/webhooks/mihu', express.json(), (req, res) => {
  const signature = req.headers['x-mihu-signature'];
  const secret = process.env.MIHU_WEBHOOK_SECRET;

  if (!verifyWebhookSignature(req.body, signature, secret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process webhook
  const { event, data } = req.body;
  console.log(`Received event: ${event}`);

  res.status(200).json({ received: true });
});

Best Practices

Never process webhooks without verifying the signature when a secret key is configured. This prevents unauthorized access and spoofed requests.
Only use HTTPS endpoints for webhooks. HTTP is not supported for security reasons.
Return a 200 status code within 5 seconds. Process long-running tasks asynchronously in a background job.
Webhooks may be delivered more than once. Use the event ID to track processed events and prevent duplicate processing.
If your endpoint is temporarily unavailable, Mindhunters will retry delivery. Implement proper error handling to accept retries gracefully.
Keep logs of received webhooks for debugging and audit purposes.

Webhook Endpoint Requirements

Your webhook endpoint must: ✅ Accept POST requests with JSON payloads ✅ Use HTTPS (HTTP not supported) ✅ Return HTTP 200 status code to acknowledge receipt ✅ Respond within 5 seconds ✅ Be publicly accessible on the internet
Endpoints that consistently timeout or return errors may be automatically disabled to prevent delivery issues.

Testing Webhooks

Viewing Webhook Logs

You can view webhook delivery attempts in your Mindhunters dashboard:
  1. Navigate to Developer section
  2. Click on “Logs” in the right panel
  3. Select “Webhook Logs”
Here you’ll see:
  • Delivery timestamps
  • Event types
  • HTTP status codes
  • Response times
  • Payload contents
  • Error messages (if any)

Testing Locally

For local development, use tools like ngrok to expose your local server:
# Install ngrok
npm install -g ngrok

# Expose your local server
ngrok http 3000

# Use the generated HTTPS URL in webhook configuration
# Example: https://abc123.ngrok.io/webhooks/mihu

Manual Testing

You can trigger test webhook events from the Developer section:
  1. Go to DeveloperWebhooks
  2. Click “Send Test Event”
  3. Select the event type
  4. Review the test payload
  5. Click “Send”

Example Webhook Handler

Here’s a complete example webhook handler with signature verification and event processing:
const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.MIHU_WEBHOOK_SECRET;

// Verify webhook signature
function verifySignature(payload, signature) {
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
  const expectedSignature = hmac.update(JSON.stringify(payload)).digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Webhook handler
app.post('/webhooks/mihu', async (req, res) => {
  try {
    const signature = req.headers['x-mihu-signature'];

    // Verify signature
    if (!verifySignature(req.body, signature)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }

    const { event, data, timestamp } = req.body;

    // Process different event types
    switch (event) {
      case 'conversation.end':
        await handleConversationEnd(data);
        break;

      case 'conversation.update':
        await handleConversationUpdate(data);
        break;

      case 'intent.triggered':
        await handleIntentTriggered(data);
        break;

      default:
        console.log(`Unhandled event type: ${event}`);
    }

    // Acknowledge receipt
    res.status(200).json({ received: true, event });

  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).json({ error: 'Processing failed' });
  }
});

async function handleConversationEnd(data) {
  console.log('Conversation ended:', data.id);
  console.log('Duration:', data.duration, 'seconds');
  console.log('Outcome:', data.summary.outcome);

  // Store in database, send notifications, etc.
}

async function handleConversationUpdate(data) {
  console.log('Conversation update:', data.id);
  console.log('Current status:', data.status);

  // Update real-time dashboard, etc.
}

async function handleIntentTriggered(data) {
  console.log('Intent triggered:', data.intent.name);
  console.log('Confidence:', data.intent.confidence);

  // Trigger actions based on intent
}

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

Retry Policy

If your webhook endpoint fails or times out, Mindhunters will automatically retry delivery:
  • Initial retry: After 1 minute
  • Subsequent retries: Exponential backoff (2min, 4min, 8min, 16min)
  • Maximum retries: 5 attempts
  • Timeout: 5 seconds per attempt
After exhausting all retries, the webhook event will be marked as failed and logged in the Webhook Logs.

Troubleshooting

Common Issues

Possible causes:
  • Endpoint URL is not publicly accessible
  • Firewall blocking incoming requests
  • Server is down or not responding
  • Using HTTP instead of HTTPS
Solution: Test your endpoint with tools like Postman or curl, check logs, ensure HTTPS.
Possible causes:
  • Incorrect secret key
  • Payload modified before verification
  • Character encoding issues
Solution: Double-check your secret key, verify you’re using the raw request body.
Possible causes:
  • Processing taking longer than 5 seconds
  • Database operations blocking response
Solution: Return 200 immediately, process webhook asynchronously in background.
Possible causes:
  • Retry mechanism delivering same event
  • Network issues causing retransmission
Solution: Implement idempotency using event IDs to track processed events.

Next Steps