SSE API

Regenerate Summary

Connection Information

ItemValue
Base pathhttps://vas-poc.vurbo.ai/api/v1/sse
ProtocolHTTP + Server-Sent Events (SSE)
Data formattext/event-stream
AuthenticationHeader X-API-Key: {KEY}

Note: The browser's native EventSource API does not support custom headers. Use the fetch API with a ReadableStream, or use an SSE client library that supports headers.


Endpoint Overview

Split into two endpoints, preview and persist:

MethodEndpointWrites DBSaves transcriptBilledPurpose
GET/api/v1/sse/regenerate/summary/{taskId}Preview (dry run, compare results across prompts)
POST/api/v1/sse/regenerate/summary/{taskId}✅ + bump revisionPersist (official save)

Known limitation: The GET preview is still billed — the LLM genuinely consumes tokens, so the GET endpoint cannot be used for free. Calling GET repeatedly is billed each time, but does not change the backend's stored state.


Shared: Request Parameters

GET uses the query string and POST uses a JSON body, with identical field names and types:

ParameterTypeRequiredConstraintsDescription
taskId (path)stringYesUUIDRecording ID
modestringYesenum "builtin" | "custom"Explicit path selection
templatestringRequired for builtin / forbidden for customexists prompt_templates.slugBuilt-in template slug
promptstringRequired for custom / forbidden for builtin≤2000 charactersThe customer's complete prompt (replaces the built-in layered prompt)
promptSlugstringRequired for custom / forbidden for builtin≤64 characters, Unicode, no control charactersThe customer's own identifier (pass-through)
languagestringNo-Summary output language code (e.g. zh-TW, en-US); when unspecified, the first transcription language is used
plainTextbooleanNoDefault falseRequest plain-text output (the backend performs additional markdown post-processing)

Mutual exclusion rules:

  • With mode=builtin, you may not include prompt or promptSlug
  • With mode=custom, you may not include template, but prompt and promptSlug are required

Violations → 422 validation error (summary_mode_field_mismatch)


GET /api/v1/sse/regenerate/summary/{taskId} (preview)

Description

Runs the LLM once to regenerate the summary, streaming only to the client. Does not write the DB and does not update the transcript record.

Use case: the client wants to try different prompt / plain_text settings, compare the results, and then decide whether to persist.

Request Examples

builtin mode

curl -N "https://vas-poc.vurbo.ai/api/v1/sse/regenerate/summary/550e8400-e29b-41d4-a716-446655440000?mode=builtin&template=meeting&language=zh-TW&plainText=true" \
  -H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"

custom mode

curl -N "https://vas-poc.vurbo.ai/api/v1/sse/regenerate/summary/550e8400-e29b-41d4-a716-446655440000?mode=custom&prompt=%E8%AB%8B%E5%BC%B7%E8%AA%BFKPI&promptSlug=acme-meeting-v2&plainText=true" \
  -H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"

Side Effects

  • ✅ Adds one billing record with type=summary to usage_logs (the LLM genuinely consumes tokens)
  • ❌ Does not update the three columns recordings.summary_mode / summary_template / summary_prompt_slug
  • ❌ Does not overwrite the saved summary

POST /api/v1/sse/regenerate/summary/{taskId} (persist)

Description

All actions of the GET preview, plus writing to the DB and the transcript record.

Request Examples

builtin mode

curl -N -X POST "https://vas-poc.vurbo.ai/api/v1/sse/regenerate/summary/550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: vas_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "builtin",
    "template": "meeting",
    "language": "zh-TW",
    "plainText": true
  }'

custom mode

curl -N -X POST "https://vas-poc.vurbo.ai/api/v1/sse/regenerate/summary/550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: vas_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "custom",
    "prompt": "You are a dermatology specialist assistant. Extract the Fitzpatrick skin type from the transcript...",
    "promptSlug": "skin-clinic-acme-v2",
    "language": "zh-TW",
    "plainText": true
  }'

Side Effects

  • ✅ Adds one billing record with type=summary to usage_logs
  • Mutually exclusive writes to the three recordings columns, depending on mode:
    • builtin → summary_mode='builtin', summary_template=<slug>, summary_prompt_slug=NULL
    • custom → summary_mode='custom', summary_template=NULL, summary_prompt_slug=<customer slug>
  • ✅ Updates the top-level fields of the transcript record and bumps revision += 1:
    • summary (plain string), summary_language, summary_mode, summary_template (effective slug), summary_plain_text
    • Required in custom mode: summary_prompt_snapshot (a verbatim snapshot of the customer's prompt, the only basis for reconstruction)

Event Sequence (same for both endpoints)

1. connected              → connection confirmation
2. summary_regeneration   → sends summary chunks (repeats N times, cumulative)
3. done                   → generation complete

connected

{
  "message": "Summary regeneration stream connected (recordingId: 550e8400-..., mode: custom, endpoint: preview)"
}

⚠️ Avoid confusion: The mode in the message is the business mode (builtin / custom, the mode passed in the request); endpoint is the endpoint mode (preview for GET / persist for POST). The two have different meanings; do not conflate them.

summary_regeneration

{ "text": "This meeting discussed the following topics:\n1. Product development progress", "is_final": false }
FieldTypeDescription
textstringCumulative summary content (when plainText=true, the text of the is_final=true event is the cleaned plain text)
is_finalbooleanWhether this is the final result

done

{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "tokens_used": 123,
  "final_content": "This meeting... (complete cleaned content)",
  "mode": "custom",
  "template": "skin-clinic-acme-v2",
  "plain_text": true,
  "persisted": true,
  "prompt_snapshot": "You are a dermatology specialist assistant..."
}
FieldTypeDescription
task_idstringRecording UUID
tokens_usednumberTotal token usage
final_contentstringComplete summary content (cleaned plain text when plainText=true)
modestringBusiness mode: "builtin" or "custom"
templatestringeffective slug — builtin → built-in template slug; custom → customer slug
plain_textbooleanWhether plain-text mode is enabled
persistedbooleanWhether this summary has been officially saved (false for GET, true for POST)
prompt_snapshotstringAppears only in custom mode; the verbatim prompt content passed in by the customer (a required snapshot, the only basis for reconstruction)

Specific Error Codes

Error CodeHTTPDescriptionRecommended Action
recording_not_found404The specified recording was not foundConfirm taskId is correct
sse_template_not_found404The summary template was not found (builtin mode)Confirm template is correct
sse_transcript_not_found404The transcript was not foundThe recording may not have finished processing yet
summary_text_empty400The transcript has no content to summarizeThe recording content is too short or is entirely silence
llm_content_filtered400Blocked by content filtering (the custom prompt or transcript contains sensitive terms)As of v1.5.5, the SSE endpoint has not yet integrated the fallback chain; we recommend using the POST /api/v1/summary REST endpoint to trigger automatic downgrade, or revise the prompt and retry

Content filtering fallback chain — current status of the SSE endpoint (v1.5.5):

WebSocket realtime summaries and file-import summaries integrated the L1→L2→L3 fallback chain in v1.5.5; when blocked, they automatically downgrade to produce a simplified summary. However, this endpoint (SSE regenerate/summary) has not yet integrated it, and will still return llm_content_filtered directly.

A follow-up PR will integrate the fallback chain and extend the done event with fallback_level / dropped_segments fields. At that point the client can use done.fallback_level to determine whether a fallback occurred. Until then, if your integration requires the automatic downgrade behavior for content filtering, we recommend triggering it through the POST /api/v1/summary REST endpoint. | summary_text_too_long | 400 | The transcript exceeds the length limit (100,000 characters) | Shorten the recording or split the file | | summary_invalid_mode | 422 | mode is not builtin / custom | Change to a valid mode | | summary_mode_field_mismatch | 422 | The mode and field combination do not match (a required field is missing or a forbidden field was included) | Adjust the fields according to the mode rules | | summary_prompt_too_long | 422 | prompt exceeds 2000 characters | Shorten it | | summary_prompt_slug_too_long | 422 | promptSlug exceeds 64 characters | Shorten it | | summary_prompt_slug_invalid | 422 | promptSlug contains control characters (\n / \r / \t / \0, etc.) | Remove the control characters | | sse_summary_regeneration_failed | 500 | Summary regeneration failed (internal errors are sanitized and will not leak the LLM raw error) | Retry later |


Version: V1.5.7 Last Updated: 2026-05-20

Copyright © 2026