Skip to main content
Home›Docs›API Reference›Text analysis
API Reference

Text analysis

Run the intelligence pipeline on raw text — chat logs, emails, or transcripts from other systems — through file upload, synchronous analyze, or a real-time WebSocket stream.

Text analysis

Text analysis runs the Perceive8 intelligence pipeline (transcript segments, embeddings, enrichment, alerts, and playbook reports) on raw text instead of audio. Use it for chat logs, email threads, or transcripts produced by other systems. You can submit text three ways: upload a conversation file for asynchronous processing, post inline text for synchronous analysis, or open a WebSocket for real-time streaming with live alerts.

All endpoints require a workspace-scoped credential (X-API-Key: pk_live_xxxxxxxxxxxx or Authorization: Bearer <jwt>; see Authentication). Text processing consumes text credits — 1 credit per 1,000 tokens (rounded up) — and requests fail with 402 when the workspace has insufficient credits.

Upload a file for async analysis

POST /v1/text/upload

Uploads a conversation file and queues it for background analysis. Returns immediately with status: "pending"; poll the status endpoint or fetch the result once completed.

The request body is multipart/form-data:

Field Type Required Description
file file yes The conversation file to analyze.
format string yes File format: jsonl, txt, or csv (case-insensitive).
playbook_id string (UUID) no Playbook to evaluate. Falls back to the user's active playbook when omitted.
language string no Language code. Default "en".

Accepted file layouts:

  • jsonl — one JSON object per line. Text is read from text, content, or message; speaker from speaker, role, from, or name; optional start_time/end_time (seconds) or a single timestamp.
  • txt — one turn per block, e.g. Speaker: text, [00:00] Speaker: text, Speaker - text, Speaker> text, [Speaker] text, or Speaker:: text. Consecutive lines without a speaker marker continue the previous turn.
  • csv — header row required. Speaker column: one of speaker, role, from, name, user. Text column: one of text, content, message, body. Optional start column (start_time, start, timestamp, time) and end column (end_time, end, duration).

Speaker names are normalized: user/customer/client/human become Person, and assistant/agent/bot/system become Agent. Segments without timestamps are assigned estimated times from word count.

curl -X POST https://api.perceive8.com/v1/text/upload \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx" \
  -F "[email protected]" \
  -F "format=jsonl" \
  -F "language=en"

Response — 202 Accepted

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "pending",
  "createdAt": "2026-01-15T10:30:00.123456"
}

Errors: 400 (unsupported format, empty file, unparseable content, no valid segments, or malformed playbook_id), 402 (insufficient text credits), 404 (playbook not found or not owned by the user).

Analyze inline text synchronously

POST /v1/text/analyze

Analyzes a single block of text synchronously and returns once the pipeline has finished. The text is stored as a single transcript segment. If a playbook is resolved, a report is generated automatically.

Request body

{
  "text": "Thanks for calling. I'd like to cancel my subscription.",
  "language": "en",
  "playbook_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "speaker": "Person"
}
Field Type Required Description
text string yes The text to analyze. Must not be blank.
language string no Language code. Default "en".
playbook_id string (UUID) no Playbook to evaluate. Falls back to the user's active playbook when omitted.
speaker string no Speaker label for the segment. Default "Person".

Response — 200 OK

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": null,
  "language": "en",
  "created_at": "2026-01-15T10:30:00.123456",
  "audio_intelligence_features": null,
  "key_phrases": null,
  "metadata": null
}

The name, audio_intelligence_features, key_phrases, and metadata fields are part of the shared analysis schema and are null for text analyses. Note this endpoint uses snake_case created_at, unlike the camelCase createdAt returned by the other endpoints on this page.

Errors: 400 (missing/blank text or malformed playbook_id), 402 (insufficient text credits), 404 (playbook not found), 502 (the analysis pipeline failed; the analysis is marked failed).

Poll processing status

GET /v1/text/{analysis_id}/status

Returns the processing status of a text analysis created via upload, analyze, or stream.

Parameters

Name In Type Description
analysis_id path string (UUID) The analysis ID.

Response — 200 OK

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "completed",
  "createdAt": "2026-01-15T10:30:00.123456"
}

status is one of pending, processing, completed, or failed. Returns 404 when the ID is not a valid UUID or no analysis with that ID exists in the workspace.

Fetch the result

GET /v1/text/{analysis_id}/result

Returns the transcript segments and, when a playbook was evaluated, the generated report.

Parameters

Name In Type Description
analysis_id path string (UUID) The analysis ID.

Response — 200 OK

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "completed",
  "language": "en",
  "createdAt": "2026-01-15T10:30:00.123456",
  "segments": [
    {
      "speaker": "Person",
      "text": "Thanks for calling. I'd like to cancel my subscription.",
      "startTime": 0.0,
      "endTime": 6.5,
      "confidence": null
    }
  ],
  "report": {
    "id": "9b2f4c10-3f4a-4c6e-9f0a-1d2e3f4a5b6c",
    "status": "completed",
    "executiveSummary": "The customer requested a subscription cancellation.",
    "overallAssessment": "Churn risk identified; retention playbook steps partially covered."
  }
}

Segment fields:

Field Type Description
speaker string Speaker label; "UNKNOWN" when none was set.
text string Segment text.
startTime / endTime number Segment bounds in seconds (estimated from word count for text input).
confidence number | null Transcription confidence; null for text input.

report is null when no report was generated (for example, when no playbook was active). Returns 404 for unknown analyses.

List text uploads

GET /v1/text/uploads

Lists text-only analyses for the authenticated workspace (analyses backed by an audio file are excluded), most recent first.

Parameters

Name In Type Description
limit query integer Max items to return. Default 100.
offset query integer Number of items to skip. Default 0.

Response — 200 OK

{
  "analyses": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "language": "en",
      "status": "completed",
      "createdAt": "2026-01-15T10:30:00.123456"
    }
  ],
  "total": 1
}

Stream text in real time

WS /v1/text/stream

Opens a real-time text streaming session. Each message you send is analyzed immediately (keyword fast-path alerts, LLM sentiment/toxicity/emotion/topics, playbook question evaluation) and the results are pushed back as events. When the stream ends, the full intelligence pipeline runs over the accumulated text and a report is generated if a playbook was set.

Connection

wss://api.perceive8.com/v1/text/stream?token=pk_live_xxxxxxxxxxxx
Query parameter Required Description
token yes API key (pk_live_...) or user JWT.
x_workspace_id no Workspace UUID. Defaults to the user's personal workspace; you must be a member of the workspace given.

Authentication failures close the socket with code 4001 and a reason of Missing token, Invalid token, Invalid workspace id, Workspace access denied, or Invalid user identifier. The server sends {"type": "ping", "timestamp": <unix-ms>} every 30 seconds as a heartbeat.

Client messages

Message When to send Fields
start First message; initializes the session, creates the analysis, and loads playbook questions. action: "start", language (optional, default "en"), playbook_id (optional UUID), metadata (optional object, stored on the analysis)
text One message per conversation turn, after start succeeded. action: "text", text (string), speaker (optional, default "Person")
stop Ends the stream; triggers post-session processing. action: "stop"

Server events

Event When sent Payload fields
session_started After start is processed. session_id, analysis_id
transcript_partial For each text message. data: speaker, text, start_time, end_time (seconds, estimated)
analysis_chunk For each text message, after transcript_partial. segment_index, transcript, speaker, sentiment (positive | neutral | negative), sentiment_score (-1.0–1.0), toxicity_score (0.0–1.0), emotion, topics (array of strings)
alert For each triggered alert (playbook or fast-path), after analysis_chunk. data — see alert payloads below
analysis_started After stop, when post-session pipeline processing has completed. analysis_id, message ("Text analysis completed")
session_ended Last event before the socket closes. session_id, analysis_id (null if start never succeeded)
error Session setup or post-session failure. message

Playbook alerts (from playbook question evaluation) carry these data fields:

Field Description
id Alert UUID.
analysis_id, user_id Owning analysis and user.
playbook_id, question_id Triggering playbook and question.
severity_level CRITICAL, HIGH, WARNING, or INFO (derived from confidence).
message The playbook question text (truncated to 200 chars).
evidence_text The segment text that triggered the alert (truncated to 500 chars).
rationale Explanation string.
recommendation Recommended action, or null.
speaker_name Speaker of the triggering segment.
start_time, end_time Segment bounds in seconds.
confidence LLM confidence, 0.0–1.0.
acknowledged Always false on emission.
created_at ISO 8601 timestamp.
segment_id Related segment ID, or null.

Fast-path alerts (regex keyword matching) have a smaller data payload: id, alert_type (toxic_language, violence, or self_harm), severity (HIGH for toxic_language, CRITICAL otherwise), message, and evidence_text (the matched excerpt).

Example session

→ {"action": "start", "language": "en", "playbook_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"}
← {"event": "session_started", "session_id": "1c8f…", "analysis_id": "3fa8…"}
→ {"action": "text", "text": "I'm extremely unhappy with this service.", "speaker": "Customer"}
← {"event": "transcript_partial", "data": {"speaker": "Customer", "text": "I'm extremely unhappy with this service.", "start_time": 0.0, "end_time": 3.5}}
← {"event": "analysis_chunk", "segment_index": 0, "transcript": "I'm extremely unhappy with this service.", "speaker": "Customer", "sentiment": "negative", "sentiment_score": -0.8, "toxicity_score": 0.05, "emotion": "anger", "topics": ["complaint", "service quality"]}
→ {"action": "stop"}
← {"event": "analysis_started", "analysis_id": "3fa8…", "message": "Text analysis completed"}
← {"event": "session_ended", "session_id": "1c8f…", "analysis_id": "3fa8…"}

After session_ended, use GET /v1/text/{analysis_id}/result with the returned analysis_id to fetch the final segments and report.