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

# on_session_end()

> React when a call session ends

Register a handler that fires when a call session ends. Use this to save call data, trigger post-call workflows, or log outcomes.

The `BotSessionEnded` event carries a `termination_reason` field that tells you why the session ended:

<Tabs>
  <Tab title="Python">
    | Value | Meaning |
    | - | - |
    | `"user-hangup"` | The caller hung up. |
    | `"bot-hangup"` | The agent ended the call (e.g. via `call.hangup()`). |
    | `"bot-failure"` | The session ended due to an internal error. |
    | `"bot-transfer"` | The call was transferred to another destination. |
    | `"voicemail"` | The outbound call reached voicemail. |

    The event payload contains a `dnc` boolean field (defaulting to `False`). If the voice agent detects a verbal opt-out during the call, this field is set to `True` and the caller's phone number is automatically added to your organization's Do Not Call list.
  </Tab>

  <Tab title="TypeScript">
    | Value | Meaning |
    | - | - |
    | `"user-hangup"` | The caller hung up. |
    | `"bot-hangup"` | The agent ended the call (e.g. via `call.hangup()`). |
    | `"bot-failure"` | The session ended due to an internal error. |
    | `"bot-transfer"` | The call was transferred to another destination. |
    | `"voicemail"` | The outbound call reached voicemail. |

    The event payload contains a `dnc` boolean field (defaulting to `false`). If the voice agent detects a verbal opt-out during the call, this field is set to `true` and the caller's phone number is automatically added to your organization's Do Not Call list.
  </Tab>
</Tabs>

<Info>
  DNC detection is exclusive to outbound campaigns (inbound calls are not supported). Opted-out numbers are added directly to your organization-wide DNC list rather than a campaign-specific list.
</Info>

## Signature

<Tabs>
  <Tab title="Python">
    ```python signature theme={null}
    @agent.on_session_end
    def on_session_end(call: guava.Call, event: BotSessionEnded) -> None:
        ...
    ```

    | Parameter | Type | Description |
    | - | - | - |
    | `call` | `Call` | The call object. Note: the call is already ended — do not issue commands on it. |
    | `event` | `BotSessionEnded` | Contains `termination_reason` — one of `"user-hangup"`, `"bot-hangup"`, `"bot-failure"`, `"bot-transfer"`, `"voicemail"`. Also contains `dnc` (`bool`) — `False` by default, `True` when the caller verbally opted out (outbound campaigns only). |

    **Return value:** `None`
  </Tab>

  <Tab title="TypeScript">
    ```typescript signature theme={null}
    agent.onSessionEnd(async (call: guava.Call, event: BotSessionEnded) => void);
    ```

    | Parameter | Type | Description |
    | - | - | - |
    | `call` | `Call` | The call object. Note: the call is already ended — do not issue commands on it. |
    | `event` | `BotSessionEnded` | Contains `termination_reason` — one of `"user-hangup"`, `"bot-hangup"`, `"bot-failure"`, `"bot-transfer"`, `"voicemail"`. Also contains `dnc` (`boolean`) — `false` by default, `true` when the caller verbally opted out (outbound campaigns only). |

    **Return value:** `Promise<void>`
  </Tab>
</Tabs>

## Example

<Tabs>
  <Tab title="Python">
    ```python controller.py theme={null}
    import logging
    from guava.events import BotSessionEnded

    logger = logging.getLogger(__name__)


    @agent.on_session_end
    def on_session_end(call: guava.Call, event: BotSessionEnded):
        logger.info("session ended: reason=%s", event.termination_reason)

        if event.dnc:
            # caller verbally opted out — number added to org DNC list
            logger.info("Contact opted out, added to DNC list.")

        if event.termination_reason == "user-hangup":
            # caller hung up — save any collected data
            ...
        elif event.termination_reason == "bot-transfer":
            # call was transferred to a human agent
            ...
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript controller.ts theme={null}
    import * as guava from "@guava-ai/guava-sdk";
    import type { BotSessionEnded } from "@guava-ai/guava-sdk";

    agent.onSessionEnd(async (_call: guava.Call, event: BotSessionEnded) => {
      console.log("session ended:", event.termination_reason);

      if (event.dnc) {
        // caller verbally opted out — number added to org DNC list
        console.log("Contact opted out, added to DNC list.");
      }

      if (event.termination_reason === "user-hangup") {
        // caller hung up — save any collected data
      } else if (event.termination_reason === "bot-transfer") {
        // call was transferred to a human agent
      }
    });
    ```
  </Tab>
</Tabs>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.