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

# Searchable recording and transcript library (QA)

> One unified read surface that lists every recording, searches the finalised diarised transcript segments, and joins the latest QA evaluation score plus the recording QC verdict — so the hit list is the QA worklist.

# Searchable recording and transcript library (QA)

Finding the call a QA review needs used to mean scrubbing audio by hand. The recording library gives reviewers one unified surface across every recordable channel — SIP/SBC calls, AI agents, browser softphone, conferences, video rooms, chat, email, social, and messaging — and folds three facts into every row so the hit list is the QA worklist:

1. **The recording itself** — classification, duration, and link back to the call, conference, video room, or conversation.
2. **The finalised, diarised transcript** — per-turn speaker segments, searched by case-insensitive substring match on `q`. Only the finalised segments are indexed, so partial or still-processing transcription rows never produce phantom hits.
3. **The linked QA verdict** — the latest evaluation's total score and provenance (AI auto-scored or human-reviewed) plus the recording QC verdict (`passed` / `failed` / `pending` / `skipped`) and its failed-check count.

That combination lets a reviewer answer a full worklist question in one request: *"Show me recordings where the customer said 'refund', scored below 70, QC-failed."*

**Endpoint:** `GET /api/v1/quality/recording-library`

**Authentication:** API key (`X-API-Key`) or session JWT; `inbox:read` scope.

**Roles:** `owner`, `admin`, or `supervisor`. Agents are deliberately excluded: the library surfaces transcript content and QA scores across **all** agents, which makes it a reviewer surface rather than a self-service one — the same boundary the evaluation-scorecard API enforces.

## Filters

All filters are optional and AND-combined.

| Parameter                 | Values                                                                                                             | Meaning                                                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `q`                       | string, 1–500 chars                                                                                                | Case-insensitive substring across diarised transcript segments. `%` and `_` are escaped before matching, so they search literally. |
| `classification`          | `sbc` · `ai_agent` · `browser_softphone` · `conference` · `video_room` · `chat` · `email` · `social` · `messaging` | Restrict to one recording class.                                                                                                   |
| `transcription_status`    | `pending` · `processing` · `complete` · `failed` · `skipped`                                                       | Restrict to one STT pipeline state — e.g. `failed` to triage recordings that never transcribed.                                    |
| `qc_status`               | `pending` · `passed` · `failed` · `skipped`                                                                        | Restrict to one post-finalize recording QC verdict — e.g. `failed` for the QC-triage worklist.                                     |
| `auto_scored`             | `true` · `false`                                                                                                   | Only recordings whose latest linked evaluation was AI-authored (`true`) or human-authored (`false`).                               |
| `has_evaluation`          | `true` · `false`                                                                                                   | Only recordings with (`true`) or without (`false`) any linked QA evaluation — `false` builds the "nothing reviewed yet" queue.     |
| `min_score` / `max_score` | number, 0–100                                                                                                      | Bounds on the latest linked evaluation's total score — the core of score-based review queues.                                      |
| `since` / `until`         | ISO-8601 timestamp                                                                                                 | Bounds the recording's `started_at` window.                                                                                        |
| `limit`                   | integer, 1–100 (default 25)                                                                                        | Page size.                                                                                                                         |
| `cursor`                  | opaque string (max 512 chars)                                                                                      | Keyset over the previous page, detailed below.                                                                                     |

An out-of-range filter value returns `422` (API key) or `400` (session). A malformed cursor returns `400 INVALID_CURSOR`.

## Response shape

Each item is one recording: its identifiers (`call_sid`, `conference_id`, `video_room_session_id`, `conversation_id` as applicable), `classification`, `mime_type`, `duration_seconds`, `file_size_bytes`, `started_at`, `ended_at`, `transcription_status`, `qc_status`, `qc_failed_count`, the latest linked evaluation (`qa_evaluation_id`, `qa_form_id`, `qa_total_score`, `qa_status`, `qa_auto_scored`, `qa_flagged_for_review`, `qa_agent_id`, `qa_evaluated_at`), and `matches` — the top transcript fragments when `q` was passed, each with `start_ms`, `channel`, and `text`. `matches` is an empty array by contract when no `q` was supplied.

```json theme={null}
{
  "data": {
    "items": [
      {
        "id": "rec_9c1a2b",
        "call_sid": "CA4f2b91",
        "classification": "sbc",
        "duration_seconds": 327,
        "started_at": "2026-08-20T14:02:11Z",
        "transcription_status": "complete",
        "qc_status": "failed",
        "qc_failed_count": "2",
        "qa_evaluation_id": "ev_81cd",
        "qa_total_score": 58.5,
        "qa_auto_scored": true,
        "qa_flagged_for_review": true,
        "matches": [
          {
            "start_ms": 12400,
            "channel": "customer",
            "text": "...asked for a refund for the duplicate charge..."
          }
        ]
      }
    ],
    "next_cursor": "eyJz...opaque"
  },
  "meta": { "request_id": "req_...", "timestamp": "..." }
}
```

## Keyset pagination

List responses order newest-first and use an opaque cursor: read `next_cursor` from the response and pass it back as `cursor` until the response returns `null` (no more rows). Keyset pagination keeps page depth constant — equivalent pages take the same time at row 10,000 as at row 10, unlike offset pagination, and it never duplicates rows when a new recording lands between requests. The cursor encodes the sort key only (no filter state), so it's safe to persist; it will not survive being edited by hand.

## Sibling surfaces — which search to use

| Surface                            | Endpoint                                                   | Use it when                                                                                                                                                                                                                                                |                                                                                                                                                                                  |
| ---------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Recording library (this page)**  | `GET /api/v1/quality/recording-library`                    | You need every recording class (conferences, video rooms, chat, email, social, messaging included), diarised per-turn match fragments, or QA score / QC-verdict filtering in one query.                                                                    |                                                                                                                                                                                  |
| **Call transcript search**         | `GET /api/v1/voice/calls/search`                           | Voice-call-only lookup with full-text relevance ranking (`billing OR refund`, `"exact phrase"`, `term -exclusion`) and highlighted snippets, no QA linkage. Conference / video-room / softphone recordings carry no searchable whole-call transcript here. |                                                                                                                                                                                  |
| **Caption export (per recording)** | \`GET /api/v1/recordings/{id}/transcript/export?format=vtt | srt\`                                                                                                                                                                                                                                                      | You already know the recording id and want a downloadable WebVTT or SubRip file — plus an optional pseudonymized variant for eDiscovery. One recording at a time; not a library. |

## End-to-end examples

Search transcripts for "refund", QC-failed, scored below 70:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/quality/recording-library?q=refund&qc_status=failed&max_score=70" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

List AI-auto-scored evaluations under 70 — the flag-and-review triage queue, without a transcript search:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/quality/recording-library?auto_scored=true&max_score=70&classification=sbc" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Paginate with the keyset cursor:

```bash theme={null}
# Page 1
curl "https://api.orbit.devotel.io/api/v1/quality/recording-library?q=escalation&limit=25" \
  -H "X-API-Key: dv_live_sk_your_key_here"

# Page 2 — pass back next_cursor until it returns null
curl "https://api.orbit.devotel.io/api/v1/quality/recording-library?q=escalation&limit=25&cursor=eyJzIjoiMjAyNi0wOC0..." \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

## Empty and edge behaviour

A tenant with no recordings gets a `200` with `items: []` and `next_cursor: null` — an empty page, never a `404`. A recording whose transcript pipeline failed or is still queued simply carries its `transcription_status`; it stays listable (with `matches: []`) and filterable by status. QC verdicts ride the tenant's own post-finalize recording QC pipeline — tenant-configured, always scoped to the calling tenant.
