import { CodeTabs } from '../views/docs/CodeTabs';
import { Callout, AutoNextLink, PropTable } from '../views/docs/prose';

export const ON_SESSION_END_SIG_PY = `@agent.on_session_end
def on_session_end(call: guava.Call, event: BotSessionEnded) -> None:
    ...`;

export const ON_SESSION_END_SIG_TS = `agent.onSessionEnd(async (call: guava.Call, event: BotSessionEnded) => void);`;

export const ON_SESSION_END_EX_PY = `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
        ...`;

export const ON_SESSION_END_EX_TS = `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
  }
});`;

## on\_session\_end()

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:

| 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.

<Callout type="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.
</Callout>

### Signature

<CodeTabs
  python={{ code: ON_SESSION_END_SIG_PY, filename: "signature" }}
  typescript={{ code: ON_SESSION_END_SIG_TS, filename: "signature" }}
/>

<PropTable rows={[
  {
    name: "call",
    type: "Call",
    desc: "The call object. Note: the call is already ended — do not issue commands on it.",
  },
  {
    name: "event",
    type: "BotSessionEnded",
    desc: "Contains `termination_reason` — one of `\"user-hangup\"`, `\"bot-hangup\"`, `\"bot-failure\"`, `\"bot-transfer\"`, `\"voicemail\"`. Also contains `dnc` (`bool` / `boolean`) — `false` by default, `true` when the caller verbally opted out (outbound campaigns only).",
  },
]} />

**Return value:** `None`

### Example

<CodeTabs
  python={{ code: ON_SESSION_END_EX_PY, filename: "controller.py" }}
  typescript={{ code: ON_SESSION_END_EX_TS, filename: "controller.ts" }}
/>

<AutoNextLink currentSection="on-session-end" />
