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

# Event Callbacks

> Receive real-time webhook events from active conversations to drive your UI, analytics, and business logic.

TruGen sends an HTTP `POST` request to your configured `callback_url` whenever something important happens in a conversation — a user starts speaking, the agent is interrupted, a call ends, and more.

## Enabling Webhooks

Webhooks are configured at the agent level using two fields:

* `callback_url` — your publicly reachable HTTPS endpoint
* `callback_events` — the list of event names you want to receive

```bash theme={null}
curl --request POST \
  --url https://api.trugen.ai/v1/agent/api \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '{
    "name": "My Agent",
    "system_prompt": "You are a helpful assistant",
    "avatar_ids": ["ava_ae61c9e28b"],
    "callback_url": "https://yourdomain.com/webhooks/trugen",
    "callback_events": [
      "agent.interrupted",
      "agent.started_speaking",
      "agent.stopped_speaking",
      "call_ended",
      "max_call_duration_timeout",
      "max_call_duration_warning",
      "participant_left",
      "tool_call",
      "utterance_committed",
      "user.started_speaking",
      "user.stopped_speaking"
    ]
  }'
```

<Note>Replace `<api-key>` with your API key and `callback_url` with your HTTPS endpoint.</Note>

## Webhook Request Format

Every webhook uses the same base structure:

```json theme={null}
{
  "timestamp": 1764760139.6834729,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": {
    "name": "agent.started_speaking",
    "payload": {}
  }
}
```

| Field             | Description                                                  |
| ----------------- | ------------------------------------------------------------ |
| `timestamp`       | Unix timestamp (float) when the event was generated          |
| `conversation_id` | Unique identifier for the conversation session               |
| `type`            | Event source category — always `"pipeline"` for these events |
| `event.name`      | The specific event type                                      |
| `event.payload`   | Event-specific data                                          |

## Handling Webhooks

<CodeGroup>
  ```ts Node.js theme={null}
  import express from "express";

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

  app.post("/webhooks/trugen", (req, res) => {
    const { timestamp, conversation_id, type, event } = req.body;

    switch (event.name) {
      case "agent.interrupted":
        // Cut off TTS visual indicators
        break;
      case "agent.started_speaking":
        // Update UI to show agent is speaking
        break;
      case "agent.stopped_speaking":
        // Hide speaking indicators, re-enable user input
        break;
      case "call_ended":
        // Trigger post-call workflows
        break;
      case "max_call_duration_timeout":
        // Show "session ended" message
        break;
      case "max_call_duration_warning":
        // Show "session ending soon" warning
        break;
      case "participant_left":
        // End session or clean up resources
        break;
      case "tool_call":
        // Log tool usage or trigger side effects
        break;
      case "utterance_committed":
        // Store transcript or trigger NLP analysis
        break;
      case "user.started_speaking":
        // Mark user as active
        break;
      case "user.stopped_speaking":
        // Mark turn boundary
        break;
    }

    res.status(200).send("ok");
  });

  app.listen(3000);
  ```

  ```python Python theme={null}
  from fastapi import FastAPI, Request
  from fastapi.responses import JSONResponse

  app = FastAPI()

  @app.post("/webhooks/trugen")
  async def trugen_webhook(request: Request):
      body = await request.json()
      event = body.get("event", {})
      event_name = event.get("name")
      payload = event.get("payload", {})

      if event_name == "agent.interrupted":
          print("Agent was interrupted")
      elif event_name == "agent.started_speaking":
          print(f"Agent speaking: {payload.get('text')}")
      elif event_name == "agent.stopped_speaking":
          print(f"Agent done: {payload.get('text')}")
      elif event_name == "call_ended":
          print("Call ended")
      elif event_name == "max_call_duration_timeout":
          print(f"Timeout: {payload.get('call_duration')}s")
      elif event_name == "max_call_duration_warning":
          print(f"Warning: {payload.get('call_duration')}s of {payload.get('max_call_duration')}s")
      elif event_name == "participant_left":
          print(f"Participant left: {payload.get('id')}")
      elif event_name == "tool_call":
          print(f"Tool called: {payload.get('tool_name')}")
      elif event_name == "utterance_committed":
          print(f"User said: {payload.get('text')}")
      elif event_name == "user.started_speaking":
          print("User started speaking")
      elif event_name == "user.stopped_speaking":
          print("User stopped speaking")

      return JSONResponse(content={"status": "ok"}, status_code=200)
  ```
</CodeGroup>

<Note>Keep your webhook handler fast. Acknowledge immediately and offload heavy work to background jobs.</Note>

## Event Reference

### `agent.interrupted`

Triggered when the user starts speaking before the agent finishes.

**Use cases:** cut off TTS visual indicators, log interruptions for conversational analytics.

```json theme={null}
{
  "timestamp": 1764760162.6777198,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": { "name": "agent.interrupted", "payload": {} }
}
```

***

### `agent.started_speaking`

Triggered when the agent starts speaking (TTS + avatar rendering begins).

**Use cases:** show a speaking indicator, animate visual elements, log when the agent begins its response.

```json theme={null}
{
  "timestamp": 1764760139.6834729,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": {
    "name": "agent.started_speaking",
    "payload": { "text": "Hi, how are you?" }
  }
}
```

* `payload.text` — the text the agent is about to speak.

***

### `agent.stopped_speaking`

Triggered when the agent finishes its current utterance.

**Use cases:** hide speaking indicators, re-enable user input, measure speaking duration.

```json theme={null}
{
  "timestamp": 1764760141.1043482,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": {
    "name": "agent.stopped_speaking",
    "payload": { "text": "Hi, how are you?" }
  }
}
```

* `payload.text` — the text that was just spoken.

***

### `call_ended`

Triggered when the call with the agent ends cleanly.

**Use cases:** trigger post-call workflows (summaries, surveys, CRM updates), log session completion.

```json theme={null}
{
  "timestamp": 1764760201.940625,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": { "name": "call_ended", "payload": {} }
}
```

***

### `max_call_duration_timeout`

Triggered when a conversation reaches its configured maximum duration.

**Use cases:** automatically end calls, show a "session ended" UI, enforce usage limits.

```json theme={null}
{
  "timestamp": 1764760631.38699,
  "conversation_id": "fe2b4550-a87f-440f-a9cd-e5f03208821c",
  "type": "pipeline",
  "event": {
    "name": "max_call_duration_timeout",
    "payload": {
      "call_duration": 60.027401447994635,
      "max_call_duration": 60
    }
  }
}
```

* `payload.call_duration` — actual call duration in seconds.
* `payload.max_call_duration` — configured maximum duration in seconds.

***

### `max_call_duration_warning`

Triggered shortly before a conversation reaches its configured maximum duration — a heads-up before the session is cut off.

**Use cases:** show a "session ending soon" warning in your UI, prompt the user to wrap up.

```json theme={null}
{
  "timestamp": 1764760601.123456,
  "conversation_id": "fe2b4550-a87f-440f-a9cd-e5f03208821c",
  "type": "pipeline",
  "event": {
    "name": "max_call_duration_warning",
    "payload": {
      "call_duration": 50.5,
      "max_call_duration": 60
    }
  }
}
```

* `payload.call_duration` — current call duration in seconds.
* `payload.max_call_duration` — configured maximum duration in seconds.

***

### `participant_left`

Triggered when a participant disconnects from the conversation.

**Use cases:** clean up rooms, timers, and state; mark conversation as ended in your backend.

```json theme={null}
{
  "timestamp": 1764760201.940625,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": {
    "name": "participant_left",
    "payload": { "id": "PA_6UYWCLeRkc9t" }
  }
}
```

* `payload.id` — identifier of the participant who left.

***

### `tool_call`

Triggered when the agent invokes a tool during a conversation.

**Use cases:** log tool usage for analytics, trigger side effects in your backend, audit agent actions.

```json theme={null}
{
  "timestamp": 1764760200.123456,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": {
    "name": "tool_call",
    "payload": {
      "tool_name": "getOrderStatus",
      "parameters": { "orderId": "ORD-9821" },
      "result": { "status": "shipped", "estimated_delivery": "2026-07-30" }
    }
  }
}
```

* `payload.tool_name` — the name of the tool that was called.
* `payload.parameters` — the inputs passed to the tool by the agent.
* `payload.result` — the response returned by the tool.

***

### `utterance_committed`

Triggered when the user's utterance has been fully captured and finalised by speech-to-text.

**Use cases:** store transcripts, trigger NLP analysis, display final transcript in your UI.

```json theme={null}
{
  "timestamp": 1764760169.0820587,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": {
    "name": "utterance_committed",
    "payload": { "text": "Hello. How are you doing?" }
  }
}
```

* `payload.text` — the final committed text of the utterance.

***

### `user.started_speaking`

Triggered when the system detects the user has started speaking.

**Use cases:** update UI to show a listening/recording state, trigger analytics on user turn count.

```json theme={null}
{
  "timestamp": 1764760162.4117968,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": { "name": "user.started_speaking", "payload": {} }
}
```

***

### `user.stopped_speaking`

Triggered when the user stops speaking (end of an utterance).

**Use cases:** mark turn boundaries for transcription, measure speech duration and turn-taking patterns.

```json theme={null}
{
  "timestamp": 1764760168.8806674,
  "conversation_id": "471f0520-cea1-487a-9bcc-37ba37717d81",
  "type": "pipeline",
  "event": { "name": "user.stopped_speaking", "payload": {} }
}
```

***

## Best Practices

* **Always return 2xx quickly** — acknowledge webhooks immediately and offload heavy work to background jobs.
* **Design for idempotency** — handlers should safely process the same event more than once.
* **Log by `conversation_id`** — makes debugging and analytics far easier.
* **Use HTTPS** — restrict your endpoint by IP or signing secret where your infrastructure supports it.

## What's Next?

<CardGroup cols={2}>
  <Card title="Templates" icon="shapes" href="/docs/agents/templates">
    Configure `callback_url` and `callback_events` as part of a reusable template.
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/overview">
    Full agent creation endpoint with all webhook options.
  </Card>
</CardGroup>
