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

# End call

`end_call` is on every assistant. Nothing to add, nothing to configure. It is how the model hangs up.

## When the model reaches for it

Two triggers:

* **The caller wants out.** They ask to stop, opt out, or say they don't want to talk. This one is written into the tool's own description.
* **The conversation is done.** The outcome is recorded, the question is answered, the caller has nothing more. This one comes from your prompt and from the procedures we ship. See [Give it your own rules](#give-it-your-own-rules).

## The order it happens in

1. The model says a short goodbye. Never in the same turn as the caller's last reply, and never before the caller has spoken at all.
2. It waits for the caller to acknowledge: "اللہ حافظ", "اچھا", "شکریہ".
3. It calls `end_call` with a `reason`, a few words of its own on why: `booking_confirmed`, `wrong_person`, `caller_busy_callback`. Empty when nothing fits, never invented.
4. We let any speech still playing finish, wait half a second, and hang up.

## What gets recorded

A real outreach call, names removed. The transcript ends the way the rules say:

```json theme={null}
[
  { "speaker": "caller", "text": "نہیں، بس ٹھیک ہے۔", "atSec": 203 },
  { "speaker": "agent", "text": "بہت شکریہ۔ اللہ حافظ۔", "atSec": 206 },
  { "speaker": "caller", "text": "اللہ حافظ۔", "atSec": 208 }
]
```

Then `endedBy` and the tool calls, in order. The outcome was recorded first and the hangup came last:

```json theme={null}
{
  "endedBy": "agent",
  "toolCalls": [
    { "name": "record_outreach_interest", "arguments": "{\"status\": \"interested\", \"notes\": \"Wants one bottle for the next corn crop in February.\"}", "result": "{'ok': True}", "atSec": 138 },
    { "name": "end_call", "arguments": "{\"reason\": \"booking_confirmed\"}", "atSec": 210 }
  ]
}
```

All of it is on the [session detail](/api-reference/sessions-%26-call-records/get-a-calls-transcript-and-outcomes). `endedBy` is also on the lighter [session status](/api-reference/sessions-%26-call-records/get-a-sessions-status) read. One thing to know when you build on it.

**`endedBy: agent` means the agent ended the call**: the model called `end_call`, the silence check went unanswered, or the agent detected a voicemail. The `end_call` entry in `toolCalls` tells you it was the model. On a few such calls the entry itself is missing. If you need the reason, take it from the entry when there is one, and from the transcript when there is not.

## Give it your own rules

The built-in rules cover manners. Your prompt decides what counts as done. Name the tool, and the model treats the line as an instruction:

```text wrap theme={null}
If the person does not confirm they are Ayesha, apologize for the mistake, say goodbye, and call end_call.
Once the order is confirmed and read back to the caller, thank them, say goodbye, and call end_call.
```

The prompts we build do the same. Their closing section names `end_call` after the goodbye, and the campaign procedures spell out the sequence: record the outcome, close politely, then end the call.
