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

# Translation call history and recordings

> Save a translated call and retrieve its original speech, translated captions, transfer events and per-party audio from your product.

Your application owns its calls and connects their separate media legs to
[Live Translation](https://docs.pyai.com/guides/live-translation-telephony.md). The same customer APIs
work with ServiceAgent, JustCall or your own product. Configure Twilio, Plivo
or another calling platform in your server code; history has no provider selector.

## Access from your server

Create a **Live** project and a server API key in [Console](https://console.pyai.com/keys).
Select the permissions your server needs:

| Scope | Access |
| - | - |
| `interpret:session` | Open translation streams and mint origin-locked browser tokens |
| `interpret:write` | Create a transcript/recording archive and its short-lived upload capability |
| `interpret:read` | Read saved calls, original/translated transcripts, events and recordings |

History is restricted to the key's project and organization. A key from another
project cannot retrieve a record, even within the same organization. Browser
session tokens and upload capabilities cannot read history. Reads of existing
artifacts do not open inference sessions or consume translation minutes; they
remain available when the key's speech spending allowance is exhausted.

Opening the WebSocket does not automatically archive or record a call. Obtain
the participants' agreement and opt in explicitly. For transcript-only storage,
choose `capture_format: "none"`.

## Create the archive

```bash theme={null}
curl https://api.pyai.com/v1/audio/interpretations/recordings \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_call_id":"your-conversation-42","languages":["fr","ja"],"capture_format":"party-v1"}'
```

Save the returned `id` alongside your product's conversation ID. The response
includes `call_url`, `upload_url`, `complete_url`, `upload_capability`,
`upload_until` and capture limits. Keep the write capability on your server;
it is bound to this one archive and expires after 20 minutes. Creation is not
automatically retried: retain the successful response before proceeding.

The initial hosted archive supports **10 minutes, 2,000 events and 38,400,044
bytes per party file**, with a default allowance of 50 archives/project/day.
These are archive limits, separate from live speech concurrency and your
calling platform's limits. Use your own transcript/audio storage when your
capture exceeds them; this API does not stitch multiple archives into a long
call or establish ten-call recording capacity.

## Feed the original and translated ledgers

Your bridge's durable `record` callback receives source and translated events.
Retain `source_context_id`, source/target participants and epochs, character
offsets, playback/cancellation events and transfer revisions. Source contexts
associate the two text ledgers; they do not establish word alignment.

Save a transcript checkpoint before uploading audio:

```bash theme={null}
curl "$COMPLETE_URL" \
  -H "Authorization: Bearer $UPLOAD_CAPABILITY" \
  -H "Content-Type: application/json" \
  -d '{"room_id":"your-conversation-42","duration_ms":1000,"audio_pending":true,"events":[{"event":"transcript.committed","source_leg":"customer","source_context_id":"ctx-1","source_epoch":1,"source_participant_id":"customer-42","text":"Bonjour"},{"event":"translation.committed","source_leg":"customer","source_context_id":"ctx-1","source_epoch":1,"source_participant_id":"customer-42","target_leg":"agent","text":"こんにちは"}]}'
```

Supply the actual complete event ledger and measured duration from your bridge.
If capture fails, checkpoint with `audio_failed: true` so the archive remains
incomplete. A saved transcript can be read before audio is ready.

## Upload what each party said and was sent

For `party-v1`, upload two stereo PCM16 WAV files at 16 kHz:

| File perspective | Left channel | Right channel |
| - | - | - |
| `customer` | Customer's original speech accepted by the bridge | Translated speech sent toward the customer |
| `agent` | Agent's original speech accepted by the bridge | Translated speech sent toward the agent, including replacement-agent periods |

Use the post-echo-validation source audio and the transport's paced output tap.
Audio must never be fed back into recognition for recording. Sent audio is not
proof that a listener heard it; use the event ledger's playback receipts and
cancellation boundaries when displaying delivery status.

```bash theme={null}
curl "$UPLOAD_URL?perspective=customer" \
  -H "Authorization: Bearer $UPLOAD_CAPABILITY" \
  -H "Content-Type: audio/wav" --data-binary @customer.wav

curl "$UPLOAD_URL?perspective=agent" \
  -H "Authorization: Bearer $UPLOAD_CAPABILITY" \
  -H "Content-Type: audio/wav" --data-binary @agent.wav
```

Both files must have the same duration and be present before finalization.
Retry the same immutable bytes after an upload `429` using `Retry-After`.
Then send the complete event ledger to `complete_url` without `audio_pending`.
For `capture_format: "none"`, skip the audio uploads and finalize the ledger.
A repeated finalization preserves an already-ready archive.

Use `@pyai/sdk` 0.9.3 or later for the `translation` helpers.
The `InterpretationRecording` helper in `@pyai/twilio` 0.4.10 or later performs transcript
checkpointing, bounded per-party capture and sequential uploads. Wire
`recording.event(event)` into the room's durable event callback,
`recording.capture(kind, leg, pcm)` into `observeAudio`, and await
`recording.finish()` after the room closes. Keep your application's call
termination and transfer controls in the calling-platform integration.

## Retrieve artifacts in your product

| Method and path | Result |
| - | - |
| `GET /v1/audio/interpretations/calls` | Project history, 50 records/page, newest first; follow `next_cursor` |
| `GET /v1/audio/interpretations/calls/{id}` | Status, languages, external call ID, captions and recording availability |
| `GET /v1/audio/interpretations/calls/{id}/transcript` | Display captions, raw original text and source/translated event ledgers |
| `GET /v1/audio/interpretations/calls/{id}/events` | Uploaded events, including participant replacements, cancellations and playback receipts |
| `GET /v1/audio/interpretations/calls/{id}/recording?perspective=customer` | Customer's stereo recording |
| `GET /v1/audio/interpretations/calls/{id}/recording?perspective=agent&download=1` | Agent's stereo recording as a download |

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

curl "https://api.pyai.com/v1/audio/interpretations/calls/$CALL_ID/recording?perspective=customer&download=1" \
  -H "Authorization: Bearer $PYAI_API_KEY" -o customer.wav
```

For browser playback, proxy the audio through your application's authenticated
backend and preserve `Range`, `Content-Range`, content type and `206` responses.
Never put a live API key in an audio element, query string or public download
link. `409` means audio is not ready or was not recorded; `416` means an invalid
byte range. Incomplete/expired uploads remain visible in history instead of
appearing as complete recordings.

These archives contain artifacts submitted by your bridge. They are not an
authoritative carrier record, usage ledger or proof of acoustic echo quality.
Retain the interpretation request/session ID in the events and reconcile billing
through [Usage and metering](https://docs.pyai.com/pricing-and-metering.md). There is no call-history
webhook in this interface; poll history or notify your product after archive
finalization succeeds.


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