Back to site
ProsodyAI Docs
API Reference

Webhooks

Receive real-time notifications for async events

Webhooks

Webhooks allow you to receive real-time notifications when events occur in ProsodyAI.

Overview

Instead of polling the API, configure webhook endpoints to receive HTTP POST requests when:

  • Analysis completes (async mode)
  • Sessions end with predictions
  • Fine-tuning jobs complete
  • Alerts trigger (e.g., high escalation risk)

Creating a Webhook

POST /v1/webhooks
{
  "url": "https://your-app.com/webhooks/prosody",
  "events": ["analysis.complete", "session.end", "alert.triggered"],
  "secret": "whsec_your_webhook_secret"
}

Response:

{
  "webhook": {
    "id": "wh_abc123",
    "url": "https://your-app.com/webhooks/prosody",
    "events": ["analysis.complete", "session.end", "alert.triggered"],
    "active": true,
    "created_at": "2025-02-22T12:00:00Z"
  }
}

Event Types

EventDescription
analysis.completeAsync analysis finished
session.endConversation session ended
session.predictionPrediction threshold crossed
alert.triggeredCustom alert triggered
fine_tune.startedFine-tuning job started
fine_tune.completedFine-tuning job completed
fine_tune.failedFine-tuning job failed

Webhook Payload

All webhooks include a standard envelope:

{
  "id": "evt_xyz789",
  "type": "analysis.complete",
  "created_at": "2025-02-22T14:30:00Z",
  "data": {
    // Event-specific data
  }
}

analysis.complete

{
  "id": "evt_xyz789",
  "type": "analysis.complete",
  "created_at": "2025-02-22T14:30:00Z",
  "data": {
    "request_id": "req_abc123",
    "emotion": "frustrated",
    "confidence": 0.87,
    "valence": -0.6,
    "arousal": 0.7,
    "dominance": 0.4,
    "state": "frustrated",
    "metrics": {
      "escalation_risk": "high",
      "csat_predicted": 2.3
    }
  }
}

session.end

{
  "id": "evt_xyz789",
  "type": "session.end",
  "created_at": "2025-02-22T14:30:00Z",
  "data": {
    "session_id": "call-12345",
    "duration": 342.5,
    "utterance_count": 28,
    "final_emotion": "satisfied",
    "emotion_trajectory": ["neutral", "confused", "frustrated", "satisfied"],
    "predictions": {
      "escalation_risk": 0.12,
      "churn_risk": 0.08,
      "resolution_probability": 0.95,
      "predicted_csat": 4.2
    },
    "summary": {
      "dominant_emotion": "confused",
      "peak_escalation_risk": 0.65,
      "sentiment_change": 0.8
    }
  }
}

alert.triggered

{
  "id": "evt_xyz789",
  "type": "alert.triggered",
  "created_at": "2025-02-22T14:30:00Z",
  "data": {
    "alert_type": "escalation_risk_high",
    "session_id": "call-12345",
    "threshold": 0.7,
    "value": 0.82,
    "emotion": "angry",
    "recommended_action": "supervisor_intervention"
  }
}

Verifying Webhooks

Always verify webhook signatures to ensure requests are from ProsodyAI.

ProsodyAI signs all webhook requests using HMAC-SHA256. The signature is in the X-Prosody-Signature header.

Verification Example

import crypto from 'crypto';

function verifyWebhook(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}

// In your webhook handler
app.post('/webhooks/prosody', (req, res) => {
  const signature = req.headers['x-prosody-signature'];
  const payload = JSON.stringify(req.body);
  
  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process the webhook
  const event = req.body;
  console.log(`Received ${event.type} event`);
  
  res.status(200).send('OK');
});
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, f'sha256={expected}')

# In your webhook handler
@app.post('/webhooks/prosody')
async def handle_webhook(request: Request):
    signature = request.headers.get('X-Prosody-Signature')
    payload = await request.body()
    
    if not verify_webhook(payload, signature, os.environ['WEBHOOK_SECRET']):
        raise HTTPException(status_code=401, detail='Invalid signature')
    
    event = await request.json()
    print(f"Received {event['type']} event")
    
    return {'status': 'ok'}
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "io"
    "net/http"
    "os"
)

func verifyWebhook(payload []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signature), []byte(expected))
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    signature := r.Header.Get("X-Prosody-Signature")
    payload, _ := io.ReadAll(r.Body)
    
    if !verifyWebhook(payload, signature, os.Getenv("WEBHOOK_SECRET")) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }
    
    fmt.Println("Webhook verified")
    w.WriteHeader(http.StatusOK)
}

Managing Webhooks

List Webhooks

curl https://api.prosody.ai/v1/webhooks \
  -H "Authorization: Bearer psk_your_api_key"

Update Webhook

curl -X PATCH https://api.prosody.ai/v1/webhooks/wh_abc123 \
  -H "Authorization: Bearer psk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["analysis.complete", "session.end"],
    "active": true
  }'

Delete Webhook

curl -X DELETE https://api.prosody.ai/v1/webhooks/wh_abc123 \
  -H "Authorization: Bearer psk_your_api_key"

Retry Policy

Failed webhook deliveries are retried with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours

After 5 failed attempts, the webhook is marked as failing. You'll receive an email notification, and can view failed deliveries in the dashboard.

Best Practices

  1. Respond quickly - Return 2xx within 5 seconds; process async
  2. Handle duplicates - Use event id for idempotency
  3. Verify signatures - Always validate webhook authenticity
  4. Use HTTPS - Webhook URLs must be HTTPS in production
  5. Monitor failures - Set up alerts for webhook delivery issues