Conversation & Analytics
← Back to the API Reference index
Converse
POST https://genie.nvp1.ovp.kaltura.com/assistant/converse
Requires a conversation KS (see Authentication § KS types):
CONV_KS=$(curl -s -X POST "https://www.kaltura.com/api_v3/service/session/action/start" \
-d "format=1" -d "secret=$AGENTIC_ADMIN_SECRET" \
-d "partnerId=$AGENTIC_PARTNER_ID" -d "type=2" -d "expiry=86400" \
-d "privileges=geniegpcid:1389" | tr -d '"')
{
"userMessage": "How do I fix video buffering?",
"threadId": null,
"sse": false
}
| Field | Notes |
|---|---|
userMessage |
Required |
threadId |
Omit for new conversation; pass previous value for memory |
sse |
false = NDJSON (default); true = SSE |
model_type |
"fast" for cheaper/faster model |
force_experience |
Must be one of markdown, summarization, flashcards, avatar_only. Anything else 422s before any network call. A hint to the brain about which experience to render, not a guaranteed outcome. |
request_vars |
{{var}} interpolation values. Needs allow_client_variables:true on the intellect (see Authentication § The Five Services for what an intellect is). Values persist on the thread: the server merges each message's map into what's stored, so send only deltas — a new thread starts clean. They interpolate into both prompt blocks and server-side api-tool templates. Reserved sys__* keys (including sys__user_id) are server-injected and rejected if you try to set them yourself — see § Bind a session to a real end-user identity above for how sys__user_id gets populated. Semantics in depth: Dynamic Data Injection. |
capabilities |
Per-message capability override |
Enabling allow_client_variables: mgmt.intellects.setClientVariablesEnabled(configId, true, adminKs) (WRITE, admin KS; also exposed as mgmt.intellectConfig.setClientVariablesEnabled).
With it off, the rejection is silent on every path. The turn streams back empty: no HTTP error on converse, no socket error. The server's 403 fires inside its streaming pipeline after the response has already opened, so it never reaches the wire.
Both session classes (KalturaAvatarSession, KalturaChatSession) detect the pattern and emit a once-per-session warning event (code: 'empty_turn_with_request_vars', variable names only, never values). The management converse helpers keep a defensive remap to a typed client_variables_disabled error for the pre-stream case, should the server ever start rejecting before the stream opens.
Stream segments (each line is a JSON object):
type |
Meaning |
|---|---|
"think" |
Processing (show spinner) |
"text" |
Response content — concatenate content fields. On an avatar-enabled intellect, spoken content streams as "avatar" (and "avatar-filler") instead, accumulated the same way. |
"tool" / "tool_response" |
Server tool call + result; content carries client commands |
"unisphere-tool" |
GenUI widget — metadata.runtimeName names the widget |
"error" |
Brain error |
"user-interruption" |
User barged in |
Key envelope fields: threadId (save for follow-ups), messageId (save for feedback), isFinal:true (stream done).
Abort a running turn:
POST https://genie.nvp1.ovp.kaltura.com/assistant/abort
{ "threadId": "154a05c4-..." }
Reserved Template Variables (sys__*)
The server sets these on every turn. They're available to {{ ... }} interpolation in base_directive / prompts[].value / glossary (see Configure an Intellect) and in a Skill's instructions text (see § Skills), regardless of allow_client_variables.
Before any network call, the SDK's own request_vars pre-flight guard rejects a client-supplied value for any key in the table below, plus secrets (see request_vars above):
| Variable | Resolves to | Notes |
|---|---|---|
sys__thread_id |
Current conversation thread id | |
sys__message_id |
Current message id | |
sys__user_id |
The bound end-user id | Empty by default (an anonymous KS). Bind a real identity with Sessions.createConversationToken({ userId }) (or createAdminToken({ userId })) so this resolves server-side instead of always being empty — see § Bind a session to a real end-user identity above. |
sys__user_message |
The current turn's user text | |
sys__is_new_thread |
true on the first turn of a new thread, false otherwise |
|
sys__avatar_enabled |
Whether the current thread has a live avatar attached | Used in a Skill's condition to gate it to avatar-only sessions, e.g. {{ sys__avatar_enabled }} — see Configure an Intellect § skill_ids. |
sys__avatar_share_screen_enabled |
Whether the current avatar session has screen-share analysis enabled | Also usable in a Skill's condition, e.g. {{ sys__avatar_enabled and not sys__avatar_share_screen_enabled }} to gate a skill to avatar sessions that are NOT sharing a screen. |
sys__context_id |
The category/entry id the current context (and its knowledge base, if any) is scoped to | Set via KalturaAvatarSession's contextId constructor option, sent on the live socket join payload — see System Internals Reference · Connection and Handshake § The join payload. Empty when no context was set at join. |
sys__context_type |
The type of that context (e.g. an entry vs. a category) |
Set via KalturaAvatarSession's contextType constructor option, alongside contextId; empty when no context was set at join. |
sys__ks |
The raw Kaltura Session token for the current request | ⚠️ Security warning: never reference sys__ks in a prompt whose output could be echoed back to a user or logged. It is a live credential. Rendering it as plain text in a model response, chat transcript, or log can leak that credential. See Security & Compliance. |
sys__user_obj.first_name / .last_name / .title / .company / .gender / .email |
Attributes of the bound-user object | Verify these resolve with intellects.previewPrompt() before shipping a prompt — the rendered preview flags unresolved references with a reserved_user_attr_unresolved warning. |
secrets.<NAME> |
A named secret configured on the intellect | Write-only — see § Secrets. |
Check Status
GET https://genie.nvp1.ovp.kaltura.com/assistant/status
Returns {aiConsent, avatar, identifiedUser}. avatar is non-null when the agent has an avatar configured.
Threads
All thread endpoints require an admin KS (disableentitlement). Pager: {"pageIndex":1,"pageSize":30}.
| Operation | Endpoint | Body |
|---|---|---|
| List | POST .../v1/thread/list |
{"filter":{"objectType":"ListThreadFilter"},"pager":{"pageIndex":1,"pageSize":30}} |
| Get | POST .../v1/thread/get |
{"id":"UUID"} |
| Rename | POST .../v1/thread/update |
{"id":"UUID","title":"New name"} |
| Delete | POST .../v1/thread/delete |
{"thread_ids":["UUID"]} |
| Transcript | POST .../v1/thread/get_transcripts |
{"id":"UUID"} |
... = https://genie.nvp1.ovp.kaltura.com
Thread list response fields per object: id, title, created_at, updated_at, status.
Transcript response: {"status":"success","data":"human: …\nai: …"} — plain text, one turn per line.
Delete returns {totalCount, objects[]} — a soft delete, followed by a scheduled infra-level purge of the underlying data.
SDK: mgmt.threads.{list, get, rename, delete, transcript}. Two more write operations live only on the SDK (setAnalysis, clearAnalysis, push); see Management Operations § Threads for their request shapes.
Compliance note.
threads.delete()soft-deletes immediately; a scheduled infra-level purge erases the underlying data later. See Security & Compliance for what the SDK provides versus what the operator must configure.
Session-Completion Signal
Unlike the admin-KS thread endpoints above, this one is called from the browser client itself, with the same conversation KS (geniegpcid) used for every other client-facing call. It mints nothing new and needs no elevated privilege.
| Operation | Endpoint | Body | Auth |
|---|---|---|---|
| Session completed | POST {genieUrl}/thread/session_completed |
{"id":"<threadId>"} |
Authorization: KS <conversation ks> |
{genieUrl} defaults to https://genie.nvp1.ovp.kaltura.com (no /v1 prefix — a different route family from the thread CRUD above). It's idempotent: a repeat call for the same thread is a no-op server-side. There's no rate limit. It can block up to ~10s on a backend publish-ack, so a client must never await it on a page-unload path.
Call this the moment a conversation is genuinely over, instead of waiting for the server's idle timeout (about 10 minutes), so end-of-conversation lifecycle rules (summaries, insights, CRM pushes) fire in seconds.
KalturaAvatarSession/KalturaChatSession/KalturaAgentSession call this automatically on disconnect() (sessionCompleteOnEnd, default true) and on tab-close/backgrounding/bfcache. See README.md § Ending a conversation cleanly for the full config surface, and Wire Protocol · Events Catalog § Session-completion signal for the exact request shape.
Thread History and Context Size
There is no documented cap on how long a thread's history can grow. The full transcript is sent as model context on every turn, so the context each turn carries grows with thread length. Plan long-running threads accordingly: start a fresh thread per task, and delete threads you don't need.
Feedback and Follow-ups (SDK)
Feedback and follow-up suggestions route through internal Genie paths — use the SDK rather than calling them directly.
mgmt.feedback.add({message_id, is_positive, comment?}, convKs)— thumbs up/down on a message.message_idcomes from the converse stream.mgmt.feedback.list(ks, opts)— admin-scoped feedback listing, filterable bymessageIdEquals/messageIdsIn/threadIdEquals/agentIdEquals/isPositiveEquals. Sources from the rated message itself, not a separate feedback store — see the method's own doc for why. ⚠️ SENSITIVE: contains end-user ids/names + verbatim question/feedback text. Treat as PII; scope and redact before sharing.mgmt.followups.getSuggested(ks)— starter questions for the partner/agent. The returned set can vary between calls — don't assume a stable, fixed list. Per-answer follow-ups stream inline asunisphere-toolsegments whencapabilities.generate_followup_questions:"on".mgmt.followups.list(ks, opts)— raw partner-wide follow-up/starter question record listing (distinct fromgetSuggested's per-agent shortlist).
Usage Analytics
Partner-scoped read-only CSV — contains end-user IDs and verbatim questions (treat as PII).
SDK: mgmt.messages.report(ks) (raw CSV) / mgmt.messages.reportSummary(ks) (volume + feedback ratio + top questions, with a _meta provenance receipt).
mgmt.messages.get(id, ks) (POST {genieUrl}/message/get) fetches one message record directly, without paging through messages.list(). An unknown id throws a typed not_found; one belonging to another partner throws forbidden instead.
Knowledge Search (MCP)
POST https://genie.nvp1.ovp.kaltura.com/mcp/search
{ "query": "adaptive bitrate streaming" }
Returns {status, data}. A partner with no indexed content returns a "couldn't find relevant information" error response. SDK: mgmt.knowledge.search(query, ks, opts).
opts passes through five optional tuning params, all accepted as-is by the backend:
| Param | Default |
|---|---|
top_n |
5 |
with_line_numbers |
— |
margins_in_seconds |
15 |
include_sources |
— |
entry_description |
— |
include_sources:true changes the success shape's chapters from null to an array, and text to null.