---
title: "Call Lifecycle"
method: POST
path: "/internal/voice/calls/{call_id}/lifecycle"
tags: ["Voice - Internal"]
---

# Call Lifecycle

`POST /internal/voice/calls/{call_id}/lifecycle`

## Request

- Base URL: `https://api.puppeteerai.com`
- URL: `https://api.puppeteerai.com/internal/voice/calls/{call_id}/lifecycle`
- Auth: none declared. Required headers: `X-Internal-Token`

Signal call connect / end.

`call_started` schedules at-start triggers, marks the call ongoing with its
start timestamp, and broadcasts connection to the UI (the LLM websocket
handler still owns the "Call started" memory message). `call_ended`
broadcasts the hang-up, records end timestamp / duration / disconnection
reason, marks the call ended in memory, and ends the conversation — which
fires the in-house llm_analysis_schema analysis. end_conversation is
idempotent, so this is safe even when the agent already ended the call itself.

Retell's webhook used to write these CallModel lifecycle fields; the voice
service replaces that path, so we set them here (epoch-second timestamps and
millisecond duration, matching the Retell column semantics the UI renders).

## Path parameters

- `call_id` string, required

## Headers

- `X-Internal-Token` string, required

## Request body

- LifecycleEvent
  - `event` 'call_started' | 'call_ended', required
  - `disconnection_reason` string, nullable
  - `latency` object, nullable

## Response `200`

Successful Response

- object

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/getpuppeteer/apis/puppeteer-core-api.md) - [All operations](https://skmtc.dev/getpuppeteer/apis/puppeteer-core-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getpuppeteer/apis/puppeteer-core-api/revisions/94b4e928b42f?raw)
