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

# Voice call recording lifecycle: announce, egress, retention, purge

> Run the full voice call recording lifecycle — arm with consent, egress to your own bucket, set retention windows, apply legal holds, purge on request, and prove deletion in the audit log.

# Voice call recording lifecycle

A voice call recording moves through several stages from the moment the call starts until it is deleted. This walkthrough covers the full operator lifecycle: arming a recording on a call, storing the audio in your own cloud bucket (BYO), setting retention windows that delete recordings automatically after a set period, placing a legal hold to preserve specific recordings past the retention window, replaying and exporting the recording, purging recordings on a customer's request, and proving that purging in the audit log.

Each section below is a self-contained operation — you can jump straight to the one you need. The sections build on each other, but they do not require you to have completed the earlier ones.

## 1. Lifecycle primitives

Before walking the individual operations, here are the four primitives that govern a voice recording's lifetime:

| Primitive | What it does | Where it is set |
| - | - | - |
| **Recording consent** | Controls *which* calls are recorded and *how* participants are notified. | **Voice → Calls → Recording settings** in the dashboard, or `PATCH /api/v1/voice/settings/recording-consent`. |
| **Retention window** | Deletes recordings — audio and metadata — automatically after a set number of days in your configured per-domain retention. | **Settings → Retention** in the dashboard, or `PUT /api/v1/settings/retention`. |
| **Egress (BYO storage)** | Delivers the finalized audio file to a Google Cloud Storage bucket you own, while keeping the transcript and metadata in your workspace. | **Settings → Voice → Recording storage** in the dashboard, or `PATCH /api/v1/voice/settings/recording-storage`. |
| **Legal hold** | Exempts a specific recording — or every recording on a conversation — from the retention sweep, preserving it past the window. | `PUT /api/v1/recordings/:id/legal-hold` or `PUT /api/v1/recordings/conversation/:conversationId/legal-hold`. |

The consent primitive gates whether a recording is captured at all. Retention, egress, and legal hold govern what happens to it afterwards. Every change to any of these is written to the audit log.

## 2. Start a call recording

The first step is arming a recording to be captured when a call starts.

### Consent posture

Your organization's recording consent is a two-axis policy configured in **Voice → Calls → Recording settings** — it is a tenant-owned control, and changing it is audit-logged. The two axes are:

| Axis | Setting | Options |
| - | - | - |
| Eligibility (which calls auto-record) | `eligibility_mode` | `manual`, `inbound_only`, `outbound_only`, `all_calls` |
| Announcement (how participants are notified) | `consent_announcement_mode` | `none`, `announce_caller`, `announce_all`, `dual_channel` |

See [Call Recording Consent](/compliance/recording-consent) for the full per-jurisdiction guidance and the rules each mode pair enforces.

### Channel announcement

When the consent policy calls for an announcement (anything other than `none`), Orbit plays the announcement to the participant(s) **before** the call connects and before recording starts. If the caller hangs up during the announcement, no recording is captured and no per-minute recording cost is billed.

The announcement can be a TTS string (read out by your organization's default voice engine) or a URL to a `.wav` or `.mp3` file in GCS or any reachable HTTPS endpoint. Configure it in the same **Recording settings** page under the **Announcement** section.

### Arming a recording on a call

**With auto-record eligibility (`inbound_only`, `outbound_only`, `all_calls`):** every qualifying call is recorded automatically. You do not call a recording endpoint — Orbit's media-egress pipeline starts capturing on call answer and finalizes the recording when the call ends.

**With manual eligibility:** your operator must arm the recording on a live call through the dashboard (click **Start recording** on the active call detail) or the API:

```bash theme={null}
curl -X POST "https://orbit.devotel.io/api/v1/voice/calls/call_01J8ZC3NPA/recordings/start" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

The recording status flips from `not-recording` to `recording`. A mid-call disclosure plays to every remaining participant when you start recording on a live call, regardless of the pre-connect announcement mode.

### Recording lifecycle webhooks

Three webhooks fire during a recording's lifetime:

| Webhook | When it fires |
| - | - |
| `recording.started` | Recording has begun capturing audio. |
| `recording.completed` | Recording has finalized — the audio file is ready and the transcript has been scheduled. |
| `recording.failed` | Recording could not finalize — the call was too short, the egress pipeline failed, or a storage error occurred. |

Subscribe to these under **Developers → Webhooks** to drive your downstream pipeline. See the [webhook consumer guide](/guides/webhook-consumer) for signature verification and retry handling.

## 3. Set retention windows

Retention deletes recordings — audio and metadata — automatically after a set number of days. It is disabled by default; recordings persist indefinitely until you enable it.

### Configure in the dashboard

Open **Settings → Retention**. The page lists independent windows for each data domain:

| Domain | What it governs |
| - | - |
| **Recordings** | Recorded audio files and their metadata (transcript, QC report, clips, highlights). |
| Messages | SMS, MMS, WhatsApp, RCS, and chat message bodies and metadata. |
| Conversations | Closed conversation threads and their history. |
| Audit log | Audit log entries. |

Each domain has its own window in days. Enable the **Recordings** domain, set the number of days, and save. The retention sweep runs periodically and deletes recordings that have aged past the window.

<Note>
  The window counts from the recording's `finalized_at` timestamp — the moment the egress pipeline finished writing the audio file. A recording that finalized 90 days ago is eligible for deletion under a 90-day window.
</Note>

### Configure through the API

```bash theme={null}
# Read the current retention policy
curl "https://orbit.devotel.io/api/v1/settings/retention" \
  -H "X-API-Key: $ORBIT_API_KEY"

# Set recording retention to 365 days
curl -X PUT "https://orbit.devotel.io/api/v1/settings/retention" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recordings": {"enabled": true, "days": 365}}'
```

Changing the window does not retroactively delete recordings — only recordings that age past the new window from their finalized date are swept.

### Retention sweep and legal hold interaction

A recording under a legal hold is skipped by the sweep. When the hold is released, the recording becomes eligible for sweep on the next run — the sweep re-checks every recording on each pass, so a just-released recording is swept immediately if it has aged past the window. See [Legal Hold](#6-replay-and-legal-hold) below.

## 4. Egress to your cloud storage (BYO)

By default, call recordings land on Orbit-managed object storage. If you need the audio in your own cloud account — for your own knowledge-ingest pipeline, eDiscovery warehouse, or quality tooling — configure **BYO storage**.

### Configure the destination

The BYO destination is a **Google Cloud Storage bucket** you own. Orbit's recording service authenticates to your bucket with workload identity — there is no service-account key to register.

Set it in **Settings → Voice → Recording storage** or through the API:

```bash theme={null}
curl -X PATCH "https://orbit.devotel.io/api/v1/voice/settings/recording-storage" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "byo",
    "bucket": "my-company-recordings",
    "prefix": "voice",
    "region": "us-east1"
  }'
```

Under `byo` mode, every call recording that finalizes is copied to `gs://<bucket>/<prefix>/<tenantId>/<callId>.mp3`. The recording metadata (duration, transcript, QC report) remains in your workspace — only the audio file is egressed.

For the full bucket and IAM setup walkthrough, see [Voice call recordings to your own bucket](/guides/voice-recording-byo-storage).

### Validate the egress stamp

Each egressed recording carries an **egress stamp** — a signed provenance record that proves the file was delivered by Orbit's egress pipeline and has not been altered in transit. The stamp is recorded on the recording row and returned by the recordings API.

```bash theme={null}
curl "https://orbit.devotel.io/api/v1/recordings/rec_01J8ZC3NPA" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

```json theme={null}
{
  "data": {
    "id": "rec_01J8ZC3NPA",
    "status": "completed",
    "egress": {
      "destination": "gs://my-company-recordings/voice/tnt_01H/cal_01J.mp3",
      "content_sha256": "0abc...f9",
      "stamped_at": "2026-10-09T14:20:01Z"
    }
  }
}
```

The `content_sha256` is the SHA-256 of the audio file Orbit delivered. Compute the hash of the file in your bucket and compare it to the stamp — a match confirms the file arrived intact. The recordings API also exposes an integrity seal endpoint (`POST /recordings/:id/integrity/seal`) that anchors the content into a tamper-evident hash chain; see [Integrity seals](/guides/recording-lifecycle-operations#4-integrity-seals-proving-a-recording-has-not-changed).

### Pinning a data-residency region

For tenants subject to data-residency obligations, pass a `region` in the BYO configuration. The recording is stored in the specified GCS region, and the metadata in your workspace is locked to a matching data-residency plane. Once set, the region binding is permanent — changing it for existing recordings is refused. For the full residency model, see [Voice data residency](/compliance/voice-data-residency).

<Tip>
  The egress stamp and the BYO path are recorded on the recording row. Audit log export shows every recording's egress destination — so an auditor can trace the full chain from call to bucket without accessing your GCS console.
</Tip>

## 5. Replay the recording

A finalized recording is playable from the dashboard and the recordings API. The recording library is the main surface for replay.

### In the dashboard

Open **Voice → Recordings** (or the quick-link from the call detail). Click a recording row to open the player. The player shows the waveform, the transcript side-by-side, and speaker labels. It is the same player used for agent QA and supervisor review.

### Through the API

The recordings API returns a signed, time-limited playback URL. The URL is valid for the duration specified (clamped to a maximum of 24 hours):

```bash theme={null}
curl -X POST "https://orbit.devotel.io/api/v1/recordings/rec_01J8ZC3NPA/playback-url" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ttl_seconds": 3600}'
```

```json theme={null}
{
  "data": {
    "recording_id": "rec_01J8ZC3NPA",
    "playback_url": "https://media.orbit.devotel.io/...?signature=...",
    "expires_at": "2026-10-09T15:20:01Z"
  }
}
```

Open the `playback_url` in a browser or embed it in your own review tool. The URL is public — anyone with the link can play the recording — so treat it as a bearer credential and set a short TTL.

For video room recordings, the share-link surface under `POST /recordings/:id/share` adds an expiry-gated public page with a branded player instead. See [Share tokens for external auditors](/guides/recording-lifecycle-operations#6-share-tokens-for-external-auditors).

## 6. Replay and legal hold

A legal hold exempts a specific voice recording — or every recording on a conversation — from the retention sweep. Use it when counsel or a regulator asks you to preserve a call.

### Place a hold on a single recording

```bash theme={null}
curl -X PUT "https://orbit.devotel.io/api/v1/recordings/rec_01J8ZC3NPA/legal-hold" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hold": true, "reason": "Matter 2026-118 — customer dispute"}'
```

The recording is now exempt from age-based deletion. To release:

```bash theme={null}
curl -X PUT "https://orbit.devotel.io/api/v1/recordings/rec_01J8ZC3NPA/legal-hold" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hold": false}'
```

### Place a hold on every recording in a conversation

When a dispute covers an entire call, hold all linked recordings — every call leg and every conference bridge — in one write:

```bash theme={null}
curl -X PUT "https://orbit.devotel.io/api/v1/recordings/conversation/conv_04H9T2/legal-hold" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hold": true, "reason": "Subpoena 26-4410"}'
```

Read the aggregate state to confirm:

```bash theme={null}
curl "https://orbit.devotel.io/api/v1/recordings/conversation/conv_04H9T2/legal-hold" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

```json theme={null}
{
  "data": {
    "conversation_id": "conv_04H9T2",
    "total_recordings": 3,
    "held_recordings": 3
  }
}
```

Legal hold operations require an owner or admin role and the `voice:write` scope. Every place, release, and read is recorded in the audit log. For the full legal hold model, see [Legal Holds on Messaging Conversations](/compliance/legal-hold); the recording hold documented here is the voice-facing twin of that surface.

<Warning>
  A legal hold cannot resurrect a recording that a retention sweep already deleted before the hold existed. Place the hold as soon as a matter is anticipated.
</Warning>

### Legal hold and HIPAA mode

When your organization operates under HIPAA, the legal hold reason field must not carry PHI — the reason is stored in the recording row and appears in audit log exports. Describe the matter in generic terms (e.g., "Subpoena — litigation hold — Q4 2026") rather than naming individuals or discussing clinical context. See [HIPAA posture guide](/compliance/hipaa-posture-guide).

## 7. Purge a recording

A purge deletes a recording — the audio file, the transcript, and all derived artifacts (clips, highlights, QC report, integrity seal) — immediately and irreversibly. Use it for a customer deletion request, a DSAR right-to-erasure, or a policy-mandated removal.

### Single recording purge

```bash theme={null}
curl -X DELETE "https://orbit.devotel.io/api/v1/recordings/rec_01J8ZC3NPA/purge" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Customer DSAR erasure request — case DSAR-2026-0421"}'
```

A 202 response means the purge has been accepted. The recording is immediately removed from the recording library and the recordings API. The purge is recorded in your audit log with the reason and the purging actor.

### Bulk purge by conversation

```bash theme={null}
curl -X DELETE "https://orbit.devotel.io/api/v1/recordings/conversation/conv_04H9T2/purge" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Court-ordered removal — Case No. 26-CF-0042"}'
```

Bulk purge requires owner or admin and `voice:write`. Every recording in the conversation is purged — voice call legs, conference bridges, and any linked recordings.

### Purge and BYO storage copies

When you purge a recording, Orbit deletes the audio file from its own managed storage. If the recording was egressed to your BYO bucket, the copy in your bucket is **not** deleted by the purge — Orbit cannot reach into your bucket after the egress handoff.

To fully comply with a deletion request, you must also remove the file from your BYO bucket. Use the egress stamp's `destination` path to locate the object:

```bash theme={null}
# Get the egress destination
curl "https://orbit.devotel.io/api/v1/recordings/rec_01J8ZC3NPA" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  | jq -r '.data.egress.destination'
# gs://my-company-recordings/voice/tnt_01H/cal_01J.mp3

# Delete from your bucket
gsutil rm "gs://my-company-recordings/voice/tnt_01H/cal_01J.mp3"
```

Orbit records the purge event in the audit log with the egress destination at the time of purge — use that record as evidence that you were told to delete the BYO copy.

### Rejections during purge

| Status | Meaning |
| - | - |
| `409 legal_hold_active` | The recording is under a legal hold. Release the hold first, then purge. |
| `404 not_found` | The recording ID is unknown or already purged. The response is idempotent — a second purge on the same ID returns 200, not 404. |

A recording that is currently finalizing (status `processing`) also accepts a purge — the finalize step is cancelled, and any partially written audio is discarded.

## 8. Audit the lifecycle end to end

Every lifecycle transition is recorded in the audit log. To prove that a recording was handled correctly — from capture through retention or purge — export the audit log and trace the recording's timeline.

### Export the audit log

In the dashboard, open **Settings → Audit log** and click **Export**. Choose CSV or JSON, pick a date range, and download.

Through the API:

```bash theme={null}
curl "https://orbit.devotel.io/api/v1/settings/audit-log/export?from=2026-01-01&to=2026-10-10&format=json" \
  -H "X-API-Key: $ORBIT_API_KEY" -o audit-export.json
```

See [Audit log export](/guides/audit-log-export) for the full export surface and format.

### Trace a recording's lifecycle

Filter the export for the recording's ID (`rec_01J8ZC3NPA` in the examples below). The audit log entries for a full lifecycle look like this:

```
recording.started       → recording rec_01J8ZC3NPA started on call cal_01J8ZC3NPA
recording.completed     → recording rec_01J8ZC3NPA finalized (duration 342s)
retention.configured    → recording retention set to 365 days (by admin@example.com)
legal_hold.placed       → legal hold placed on rec_01J8ZC3NPA, reason: "Matter 2026-118"
legal_hold.released     → legal hold released on rec_01J8ZC3NPA
recording.purged        → recording rec_01J8ZC3NPA purged, reason: "DSAR-2026-0421", egress_destination: "gs://..."
```

The entries form a complete, append-only chain. A reviewer — internal audit, an external auditor, or a regulator — can walk the chain from start to finish and confirm that every step is accounted for.

For a real-time stream into your SIEM, configure the audit log webhook under **Developers → Webhooks**. The `audit.entry` event fires on every audit write, and the payload carries the same fields as the export. See [Webhook event catalog](/webhooks/events).

## 9. Common pitfalls

### Recording stays in an active or processing state forever

A stuck recording usually means the call never ended cleanly or the egress pipeline encountered a transient error. Check the recording's status in the [recording library](/voice/recording-library). If the row shows `processing` and the call has been over for more than a few minutes, the finalize step may have failed silently — contact support with the recording ID and the call ID.

### Egress signature mismatch

When your BYO bucket's copy does not match the egress stamp's `content_sha256`, the file may have been modified after delivery, or a partial write occurred. Download the recording through the recordings API (`playback-url`) and compare — the API-hosted copy is the authoritative one. If your BYO copy differs but the API copy is intact, delete the BYO object and re-finalize through the recordings API (if supported for your recording) or contact support.

### Retention window shorter than legal hold window creates a floor

Setting a retention window of 30 days while recordings under legal hold must be kept for 7 years is fine — legal hold exempts recordings from the sweep regardless of the window. But if you release every hold at year 7 and the window is 30 days, the sweep deletes the recordings on the next pass — they are now 7 years past their `finalized_at` and 7,300 days over the 30-day window. Plan your hold release: either keep the hold until audit trail closure, or widen the retention window before releasing the hold so the recordings survive long enough for a final export.

### HIPAA mode and retention floor

When your organization is under HIPAA, retention has a floor — you cannot set the recording retention window below the HIPAA minimum required by your BAA. If the console refuses a window that is too short, check **Settings → Compliance → HIPAA** for the enforced minimum and adjust accordingly.


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