Using Webhooks

Webhooks let you receive real-time HTTP callbacks when events happen in Agni — like when a call starts, ends, or when post-call analysis is ready. Instead of polling our API, we push data to your server instantly.

How It Works

Configure your endpoint

Set up a publicly accessible HTTPS endpoint on your server that can receive POST requests.

Register in Agni

Add your webhook URL in the agent’s webhook settings or via the API.

Receive events

Agni sends a POST request to your endpoint whenever a subscribed event occurs.

Process & respond

Your server processes the payload and returns a 200 status. We’ll retry on failure.

Webhook Events

The primary webhook event is call.completed, dispatched automatically when a call session ends.
EventTriggerPayload Includes
call.completedCall session endsFull call data, transcripts, recording, sentiment, post-call analysis

Full Payload Reference

See the complete After-Call Webhook API reference for every field in the payload, with types and descriptions.

call.completed Payload

When a call ends, Agni sends the complete call record including metadata, transcripts, AI analysis, and costs:
{
  "event": "call.completed",
  "org_id": "1268c1f0-19f3-47db-aefb-c16a7c3ace6e",
  "data": {
    "campaign_id": "uuid-or-null",
    "contact_id": "uuid-or-null",
    "phone": "+14155550100",
    "status": "completed",
    "duration_sec": 125,
    "call_session_id": "019d2b3c-8e9f-7a0b-1c2d-4e5f6a7b8c9d",
    "attempt": 1,
    "summary": "AI-generated summary of the conversation...",
    "recording_url": "https://storage.agniai.com/rec/019d2b3c-8e9f.mp3",
    "caller_number": "+18881234567",
    "callee_number": "+14155550100",
    "caller_name": "John Doe",
    "caller_email": "john@example.com",
    "agent_name": "Support Agent",
    "channel": "voice",
    "disconnect_reason": "customer_hangup",
    "cost_total": 0.42,
    "call_latency_ms": 150,
    "started_at": "2026-03-24T18:30:00Z",
    "ended_at": "2026-03-24T18:32:05Z",
    "created_at": "2026-03-24T18:30:00Z",
    "post_call_analysis": {
      "sentiment": "positive",
      "disposition": "meeting_booked",
      "goals_met": true,
      "next_steps": "Send follow-up details via email"
    },
    "transcripts": [
      {
        "id": "t-001",
        "timestamp_ms": 0,
        "role": "agent",
        "message": { "content": "Hello! How can I help you today?", "format": "text" },
        "created_at": "2026-03-24T18:30:00Z"
      },
      {
        "id": "t-002",
        "timestamp_ms": 3200,
        "role": "user",
        "message": { "content": "I'd like to schedule an appointment.", "format": "text" },
        "created_at": "2026-03-24T18:30:03Z"
      }
    ]
  }
}

Key Fields

FieldTypeDescription
statusstringcompleted, failed, no_answer, busy, voicemail
channelstringvoice (phone), web (browser), sip
disconnect_reasonstringcustomer_hangup, agent_hangup, error
post_call_analysis.sentimentstringpositive, neutral, negative
post_call_analysis.dispositionstringe.g. meeting_booked, not_interested, callback_requested
post_call_analysis.goals_metbooleanWhether the agent achieved its objective
transcripts[]arrayFull conversation, ordered by timestamp_ms

Setting Up Your Webhook

Via Dashboard

  1. Navigate to Agents → Select your agent
  2. Open the Webhook Settings tab in the right sidebar
  3. Enter your endpoint URL (must be HTTPS)
  4. Select which events to subscribe to
  5. Click Save

Via API

cURL
curl -X PUT https://api.ravan.ai/api/v1/agents/{agent_id} \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-server.com/webhooks/agni",
    "webhook_events": ["call.completed"]
  }'

Example Webhook Receivers

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

app.post('/webhooks/agni', (req, res) => {
  const { event, org_id, data } = req.body;

  if (event === 'call.completed') {
    console.log(`Call ${data.call_session_id} completed`);
    console.log(`Duration: ${data.duration_sec}s, Status: ${data.status}`);
    console.log(`Summary: ${data.summary}`);
    console.log(`Sentiment: ${data.post_call_analysis?.sentiment}`);
    console.log(`Disposition: ${data.post_call_analysis?.disposition}`);
    console.log(`Transcripts: ${data.transcripts?.length} messages`);

    // Store in your database
    // Update CRM with call outcome
    // Trigger follow-up workflows based on disposition
  }

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

app.listen(3000);
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/agni', methods=['POST'])
def handle_webhook():
    payload = request.json
    event = payload.get('event')
    data = payload.get('data', {})

    if event == 'call.completed':
        print(f"Call {data['call_session_id']} completed")
        print(f"Duration: {data['duration_sec']}s, Status: {data['status']}")
        print(f"Summary: {data.get('summary')}")

        analysis = data.get('post_call_analysis', {})
        print(f"Sentiment: {analysis.get('sentiment')}")
        print(f"Disposition: {analysis.get('disposition')}")
        print(f"Goals met: {analysis.get('goals_met')}")

        transcripts = data.get('transcripts', [])
        print(f"Transcript entries: {len(transcripts)}")

        # Store in database, update CRM, trigger workflows

    return jsonify({'received': True}), 200

if __name__ == '__main__':
    app.run(port=3000)

Retry Policy

If your endpoint returns a non-2xx response or times out (30 seconds), Agni retries with exponential backoff:
AttemptDelay
1st retry30 seconds
2nd retry2 minutes
3rd retry10 minutes
4th retry1 hour
Final retry6 hours
After 5 failed attempts, the webhook is marked as failing. You’ll receive an email notification and can check failed deliveries in the dashboard.

Best Practices

Always return 200 quickly

Process the webhook asynchronously. Return 200 immediately and handle the data in a background job.

Handle duplicates

Use the call_id as an idempotency key. The same event may be delivered more than once during retries.

Validate payloads

Check that the event type and required fields are present before processing. Ignore unknown event types gracefully.

Use HTTPS

Webhook URLs must use HTTPS. We do not send payloads over unencrypted connections.

Troubleshooting

  • Verify your endpoint URL is correct and publicly accessible
  • Check that your server returns 200 status within 30 seconds
  • Ensure your firewall allows incoming POST requests from Agni’s IP ranges
  • Check the webhook logs in your agent’s settings
This is normal during retries. Implement idempotency using the call_id field — check if you’ve already processed that event before acting on it.
After 5 consecutive failures, webhooks pause. Fix your endpoint, then re-save the webhook URL in agent settings to resume delivery.