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

# Recap: full transcripts and post-call notes

> Turn completed Hear calls into a retained speaker-labelled transcript, detailed summary, action items, call signals, and CRM-ready fields.

Recap runs after a call ends. It keeps the speaker-labelled transcript and adds
the notes people need to act on the call: a short headline, a detailed summary,
decisions, action items, next steps, important moments, call signals, and
structured fields.

Recap is an add-on to a call transcript. It does not transcribe audio itself.
Use Hear async jobs or Hear streaming to produce the transcript, then read the
Recap by `call_id`.

## Enable Recap

You need `recap:configure` to change the organization setting and `recap:read`
to list or read results. A sandbox key includes both and mints with Recap
already enabled.

```bash theme={null}
curl -X PUT https://api.pyai.com/v1/recap/config \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "default_pack_id": "sales_outbound"
  }'
```

## Create a Recap from an async transcription job

Add a stable `call_id` to the job. The other Recap fields are optional.
`language` controls the language of the notes, not speech recognition. Hear
transcription remains English-only.

```bash theme={null}
curl -X POST https://api.pyai.com/v1/transcription/jobs \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: call-481-transcription" \
  -d '{
    "audio_url": "https://recordings.example.com/call-481.wav",
    "channel": true,
    "call_id": "call_481",
    "call_direction": "outbound",
    "customer_name": "Acme",
    "pack_id": "sales_outbound"
  }'
```

When transcription completes, PyAI sends the complete segment list to Recap.
The Recap record moves through `pending`, `processing`, and `complete`, or
`failed` if processing cannot finish.

## Create a Recap from Hear streaming

For a Recap-enabled organization, the streaming adapter submits committed
utterances when the socket closes. Pass a stable `call_id` so the result can be
joined to your own call record:

```text theme={null}
wss://api.pyai.com/v1/audio/transcriptions/stream
  ?protocol=pyai-hear-v1
  &call_id=call_481
  &pack_id=sales_outbound
  &call_direction=inbound
```

Keep sending audio frames through pauses so turn detection can observe silence.
Only committed utterances become part of the retained transcript.

## Submit an existing transcript

If you already have speaker-labelled utterances, trigger Recap directly:

```bash theme={null}
curl -X POST https://api.pyai.com/v1/recap/calls/call_481 \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "call_direction": "outbound",
    "customer_name": "Acme",
    "utterances": [
      {
        "speaker_role": "agent",
        "text": "I will send the pricing sheet today.",
        "offset_s": 12.4,
        "duration_s": 3.1
      },
      {
        "speaker_role": "customer",
        "text": "Please include the annual option.",
        "offset_s": 16.1,
        "duration_s": 2.3
      }
    ]
  }'
```

## Read the call detail

```bash theme={null}
curl https://api.pyai.com/v1/recap/calls/call_481 \
  -H "Authorization: Bearer $PYAI_API_KEY"
```

A completed response includes two separate artifacts:

* `transcript`: the full retained utterance list with speaker roles and timing.
* `record`: typed intelligence (`recap.record.v1`). `tldr` is the one-line
  headline; `summary` is the detailed notes. `action_items`, `next_steps`,
  `talk_ratio`, `signals`, and `fields` are always present (null or empty when
  unknown).

```json theme={null}
{
  "object": "recap.call",
  "call_id": "call_481",
  "status": "complete",
  "headline": "Acme requested annual pricing and agreed to review it this week.",
  "transcript": {
    "format": "utterances.v1",
    "utterances": [
      {
        "speaker_role": "agent",
        "text": "I will send the pricing sheet today.",
        "offset_s": 12.4,
        "duration_s": 3.1
      }
    ]
  },
  "record": {
    "format": "recap.record.v1",
    "tldr": "Acme requested annual pricing and agreed to review it this week.",
    "summary": "The call focused on pricing structure and the next review step.",
    "action_items": [
      {
        "owner": "agent",
        "task": "Send annual pricing",
        "due": "today"
      }
    ],
    "disposition": null,
    "next_steps": "Email the annual option.",
    "talk_ratio": { "agent": 0.55, "customer": 0.45 },
    "signals": [],
    "fields": {}
  }
}
```

## Review in the console

The Recap call view keeps processing and failed calls visible. A completed call
has separate Summary, Insights, Transcript, and Ops views. The Transcript view
contains every retained utterance and can be copied with timestamps.

Recap rows, including their transcript and generated intelligence, are retained
for 90 days. Input audio follows the retention policy of the Hear surface that
created the transcript. See [Security and data handling](/security-and-data).

## Troubleshooting

| Symptom                          | What to check                                                                                 |
| -------------------------------- | --------------------------------------------------------------------------------------------- |
| Call remains `pending`           | Confirm Recap is enabled and retry the source job or transcript submission.                   |
| Only a one-line headline appears | Read `record.summary` or `record.summary_draft`; `tldr` is intentionally brief.               |
| Transcript is missing            | The record predates transcript retention or the trigger did not contain committed utterances. |
| Speaker roles are wrong          | Use `channel: true` for stereo recordings or `diarize: true` for mono recordings.             |
| Summary language is wrong        | Set the job or manual-trigger `language`; it changes Recap output only.                       |
