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

> ## Agent Instructions
> Use the public API base URL: https://api.teamfollowup.ai/api.
> Authenticate public API requests with Authorization: Bearer YOUR_API_KEY.
> Use product-owned terms: agents, campaigns, projects (GoHighLevel sub-accounts), contacts, calls, outcomes, skills, cadences, phone numbers.
> Scope requests by locationId rather than projectName.
> Read the guide linked from each API reference group before recommending an endpoint.

# Transcript search

> Find literal words and phrases across your saved call transcripts and return short evidence excerpts.

Search saved call text without opening every call. The API and MCP return call
record IDs and short verbatim excerpts. They include older Retell calls when
their transcripts are stored locally. Search does not fetch recordings or
missing provider transcripts.

## Matching rules

A `query` node contains exactly one of `word`, `phrase`, `all` or `any`.
`all` requires every child, across the call's turns. `any` requires one child.
A `phrase` requires adjacent words in one turn. It cannot cross speakers or
primary and transfer text.

Matching ignores case and preserves accents. Punctuation, apostrophes and
hyphens separate words. `AI` matches `ai`; `robot` does not match `robotic`.
`cafe` does not match `café`. This is literal evidence search. It does not
classify intent, expand synonyms or accept regular expressions.

Use `speaker: "lead"` when every matched word must come from a lead turn.
The default `any` includes `lead`, `agent`, `team` and `unknown` turns.
Unlabelled or ambiguous text stays `unknown` and cannot prove a lead-only hit.

```json theme={"dark"}
{
  "query": {
    "any": [{"phrase": "are you AI"}, {"phrase": "are you a robot"}]
  },
  "speaker": "lead",
  "durationMin": 121,
  "limit": 25,
  "countMode": "lower_bound",
  "includeCoverage": true
}
```

A phrase hit needs review. It does not establish that someone questioned
whether the caller was AI. Search counts matching calls, not unique leads.
For a percentage, use the same tenant scope, date window, duration filter and
observation basis for both numerator and denominator.

## Request fields

Send `POST /api/call-history/transcript-search` with `calls:read`. In MCP, use
`search_call_transcripts` with this JSON inside `body`. In search mode, run it
through `call_read_tool`.

| Field | Meaning and limits |
| - | - |
| `query` | Required. At most four levels, 16 leaves and 31 nodes. Groups cannot be empty. |
| `word` | One normalised word, at most 64 Unicode code points. |
| `phrase` | At most 256 code points and 32 words, each at most 64 code points. |
| `all`, `any` | Arrays of query nodes. AND across all children, or OR across any child. |
| `speaker` | `any` by default, or `lead`. Applies to every leaf. |
| `locationId`, `campaignId` | Optional exact IDs, at most 256 code points. They narrow your authorised scope. |
| `dateFromUTC`, `dateToUTC` | Valid inclusive `YYYY-MM-DD` UTC days. From cannot exceed to. These filter record time derived from a timestamp-bearing record ID, not the business's local day or a guaranteed call start. |
| `durationMin`, `durationMax` | Inclusive nonnegative integer seconds. Missing or invalid durations stay unknown and do not match a duration filter. Use 120 for at least two minutes, 121 for strictly over two minutes. |
| `limit` | Default 25, maximum 50. A budget limit can return fewer. |
| `cursor` | Opaque continuation, at most 8,192 characters. Keep the query and filters. Expires after 30 minutes. |
| `countMode` | `none` by default, `lower_bound` or `exact`. Independent of page size. |
| `includeCoverage` | Default false. Includes indexed coverage with your filters but without the text predicate. |

JSON is limited to 32 KiB UTF-8, including after decompression. Unknown fields,
blank leaves and excessive query complexity are rejected.

## Response fields

The usual `success` and `data` envelope contains:

| Field | Meaning |
| - | - |
| `results` | Authorised matching call records. No full transcript or recording URL. |
| `recordId`, `recordIdType` | Typed record identity. Type is `string` or `objectId`. Deduplicate with both fields. |
| `callId`, `contactId`, `referenceNotes` | Provider references, nullable when absent, invalid or too long. Notes are `call_id_unavailable` or `contact_id_unavailable`. |
| `recordedAtUTC`, `durationSeconds` | Record timestamp and integer duration, nullable when unknown. |
| `excerpts` | At most three original-text excerpts, each at most 320 code points. Each names `speaker`, `sourcePart` (`primary` or `transfer`), `text` and `notes`. Spoken text can contain personal information. |
| `notes` | Source context, including generated speech whose playback was unverified, a caller on hold or speech directed at the team. These notes are separate from searchable speech. |
| `evidenceTruncated` | Some contributing query evidence did not fit the excerpts. |
| `page.nextCursor` | Continuation, or null when exhausted. An empty page can still have a continuation after stale hits were omitted. |
| `page.budgetLimited` | The candidate, copied-text byte or response-size budget stopped the page. |
| `page.staleCandidatesOmitted` | Indexed candidates omitted after current-source or content-version checks. |
| `count` | Null for `none`. Otherwise `unit: matching_calls`, `basis: indexed_stored_transcripts` and `observedAt`. |
| `corpus` | `generation`, `consistency: eventual`, `historicalBackfillComplete`, `observedAt` and optional `coverage`. Observation time is not a freshness watermark. |

A count's `status` is `available` with `value` and `kind`, or `unavailable`
with null `value` and `kind` and `reason: stale_index_candidates` when the page
finds stale source access or filter data. An available `kind` is `exact` or
`lower_bound`. The lower-bound threshold is 1,000; below it the count is exact.
Exact refers to this indexed query. Aggregate rows are not all individually
rechecked against the current source.

Coverage uses `unit: call_records`, `basis: indexed_projection`, `observedAt`
and `total`. `transcriptStates`, `primaryStates` and `transferStates` count
`missing`, `empty`, `present`, `invalid` and `oversized` text. `speakerCoverage`
counts `known`, `partial`, `unknown` and `not_applicable` attribution. Missing
transfer text does not prove a transfer happened. Coverage is not a live source
census. Oversized text is omitted whole, rather than reported as fully searched.

## Boundaries and errors

* Search is eventually consistent. Inserts, updates and deletes can change
  later pages. Pagination is not a snapshot.
* A scope change, different user or credential, or index generation change
  invalidates the cursor. Restart the search.
* Each page examines at most 200 candidates and reads at most 8 MiB of copied
  text in batches of ten. The total deadline is eight seconds; count and
  coverage each have a five-second cap inside it. Serialized `data` stays
  within 80,000 JavaScript string characters.
* The feature stays unavailable until its operator enables and prepares it.
  A failure never becomes a successful zero result.

| Status | Meaning |
| - | - |
| 400 | Malformed JSON (`TRANSCRIPT_SEARCH_INVALID_QUERY`) or invalid, expired or changed cursor (`TRANSCRIPT_SEARCH_INVALID_CURSOR`). |
| 401, 403 | Existing authentication, `calls:read` or session CSRF checks refused the request. |
| 413 | `TRANSCRIPT_SEARCH_REQUEST_TOO_LARGE`. Reduce the JSON body. |
| 422 | `TRANSCRIPT_SEARCH_INVALID_QUERY`, or `TRANSCRIPT_SEARCH_QUERY_TOO_BROAD`. Narrow the query or select one location. |
| 429 | Existing shared API rate limit. |
| 503 | `TRANSCRIPT_SEARCH_UNAVAILABLE`, including disabled or unhealthy search. A detected index/matcher defect is `TRANSCRIPT_SEARCH_BACKEND_MISMATCH`. |
| 504 | `TRANSCRIPT_SEARCH_TIMEOUT`. Narrow the query and retry. |

## In the API

* [Search call transcripts](/api-reference/calls/search-call-transcripts)
* [Calls glossary](/glossary#calls)

## Related

* [Call records](/calls/overview) explains list and detail responses.
* [API action MCP](/mcp-actions) explains credentials and search mode.


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