> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ravan.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time notifications for call events, transcripts, and post-call analysis using Agni webhooks.

# 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

<Steps>
  <Step title="Configure your endpoint" icon="link">
    Set up a publicly accessible HTTPS endpoint on your server that can receive POST requests.
  </Step>

  <Step title="Register in Agni" icon="gear">
    Add your webhook URL in the agent's webhook settings or via the API.
  </Step>

  <Step title="Receive events" icon="bell">
    Agni sends a POST request to your endpoint whenever a subscribed event occurs.
  </Step>

  <Step title="Process & respond" icon="check">
    Your server processes the payload and returns a 200 status. We'll retry on failure.
  </Step>
</Steps>

***

## Webhook Events

The primary webhook event is `call.completed`, dispatched automatically when a call ends.

| Event            | Trigger           | Payload Includes                                                      |
| ---------------- | ----------------- | --------------------------------------------------------------------- |
| `call.completed` | Call session ends | Full call data, transcripts, recording, sentiment, post-call analysis |

<Card title="Full Payload Reference" icon="code" href="/api-reference/webhooks/after-call">
  See the complete After-Call Webhook API reference for every field in the payload, with types and descriptions.
</Card>

***

## `call.completed` Payload

When a call ends, Agni sends the complete call record including metadata, transcripts, AI analysis, and costs:

```json theme={null}
{
  "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

| Field                            | Type    | Description                                                   |
| -------------------------------- | ------- | ------------------------------------------------------------- |
| `status`                         | string  | `completed`, `failed`, `no_answer`, `busy`, `voicemail`       |
| `channel`                        | string  | `voice` (phone), `web` (browser), `sip`                       |
| `disconnect_reason`              | string  | `customer_hangup`, `agent_hangup`, `error`                    |
| `post_call_analysis.sentiment`   | string  | `positive`, `neutral`, `negative`                             |
| `post_call_analysis.disposition` | string  | e.g. `meeting_booked`, `not_interested`, `callback_requested` |
| `post_call_analysis.goals_met`   | boolean | Whether the agent achieved its objective                      |
| `transcripts[]`                  | array   | Full 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

```bash cURL theme={null}
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

<AccordionGroup>
  <Accordion icon="js" title="Node.js (Express)">
    ```javascript theme={null}
    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);
    ```
  </Accordion>

  <Accordion icon="python" title="Python (Flask)">
    ```python theme={null}
    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)
    ```
  </Accordion>
</AccordionGroup>

***

## Retry Policy

If your endpoint returns a non-2xx response or times out (30 seconds), Agni retries with exponential backoff:

| Attempt     | Delay      |
| ----------- | ---------- |
| 1st retry   | 30 seconds |
| 2nd retry   | 2 minutes  |
| 3rd retry   | 10 minutes |
| 4th retry   | 1 hour     |
| Final retry | 6 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

<CardGroup cols={2}>
  <Card title="Always return 200 quickly" icon="clock">
    Process the webhook asynchronously. Return 200 immediately and handle the data in a background job.
  </Card>

  <Card title="Handle duplicates" icon="copy">
    Use the `call_id` as an idempotency key. The same event may be delivered more than once during retries.
  </Card>

  <Card title="Validate payloads" icon="shield-check">
    Check that the event type and required fields are present before processing. Ignore unknown event types gracefully.
  </Card>

  <Card title="Use HTTPS" icon="lock">
    Webhook URLs must use HTTPS. We do not send payloads over unencrypted connections.
  </Card>
</CardGroup>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Not receiving webhooks">
    * 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
  </Accordion>

  <Accordion title="Receiving duplicate events">
    This is normal during retries. Implement idempotency using the `call_id` field — check if you've already processed that event before acting on it.
  </Accordion>

  <Accordion title="Webhook URL keeps failing">
    After 5 consecutive failures, webhooks pause. Fix your endpoint, then re-save the webhook URL in agent settings to resume delivery.
  </Accordion>
</AccordionGroup>
