Management Operations
← Back to the API Reference index
Endpoint and request reference for every Management operation: agents, avatars, intellects, tools, skills, threads, messages, knowledge, lifecycle and insight settings.
All operations use the admin KS (createAdminToken), except where a section says otherwise. Agents, Avatars, Lifecycle and Insight Settings call the management host (https://api.avatar.us.kaltura.ai). Everything else calls the conversation host (https://genie.nvp1.ovp.kaltura.com). Hosts for other regions: Regions and base URLs.
Agents
| Operation | Endpoint | Body |
|---|---|---|
| Create | POST /v1/agent/create |
{"displayName":"...", "intellect":{...}, "avatarIds":["24-char-hex"], "adminTags"?:["..."]} |
| List | POST /v1/agent/list |
{"filter":{},"pager":{"offset":0,"limit":30}} |
| Get | POST /v1/agent/get |
{"agentId":"UUID"} |
| Update | POST /v1/agent/update |
{"agentId":"UUID", ...fields} |
| Delete | POST /v1/agent/delete |
{"agentId":"UUID"} |
Filter keys: agentId, adminTagsIn, adminTagsNotIn, searchValue (case-insensitive substring match on displayName/agentId). An unrecognized key 400s.
The request also takes a top-level orderBy (+createdAt, -createdAt, +updatedAt, -updatedAt; anything else 400s). It sits next to filter, not inside it. Threads and Messages below take orderBy inside filter. SDK: mgmt.agents.list(ks, opts) passes opts.filter through as-is and does not take orderBy.
mgmt.agents.delete refuses to delete an agent whose adminTags match a production marker (prod, production, keep, do-not-delete, live; the exported PROTECTED_TAGS). Pass {confirmPermanent:true, allowProtected:true} to delete it anyway. This stops a cleanup-by-tag sweep from deleting an agent that is in use. The SDK reads the agent first. If that read fails (other than the agent not existing), the delete is refused with that error. {skipProtectedCheck:true} skips the read and the guard.
Avatars
| Operation | Endpoint | Body |
|---|---|---|
| Create | POST /v1/avatar/create |
{"voice":{"id":"...","speed"?:1.0}, "visual"?:{"id":"...","motionControl"?:{...}}, "face"?:{"id":"..."}, "background"?:{"type":"color"|"visual","value"?:"..."}, "templateId"?:"...", "name"?:"...", "openingPhrase"?:"..."} (leave openingPhrase unset. The intellect's opening_phrase sets the opening line, see Start the Conversation.) Visual resolution rules: Agent Components · Create an Avatar and an Agent § Three ways to get a visual. |
| List | POST /v1/avatar/list |
{"pager":{"offset":0,"limit":30}} |
| Get | POST /v1/avatar/get |
{"id":"24-char-hex"}. A missing or unknown id throws code:'api_exception' with title:'AVATAR_NOT_FOUND'. Branch on title, not code. A malformed id throws code:'bad_request' |
| Update | POST /v1/avatar/update |
{"id":"24-char-hex", ...fields}. Omitted fields are preserved. templateId is create-only (400 on update) |
| Delete | POST /v1/avatar/delete |
{"id":"24-char-hex"} |
| List templates | POST /v1/avatar-template/list |
{"pager":{"offset":0,"limit":30}}. Curated presets, each pairing a voice with either a ready visual or a face/background pair (Agent Components · Create an Avatar and an Agent § Three ways to get a visual). SDK: mgmt.avatars.listTemplates(ks, opts); opts.idsIn filters to specific template ids. |
Changing the look on update. On create, face and background travel together. On update:
- To change the background, send
backgroundalone. It applies to the avatar's current face. - To change the face, send
faceandbackgroundtogether.
Avatars carry no adminTags. avatars.create and avatars.update throw bad_request before any network call if you pass it. Tag the parent agent instead.
Intellects
| Operation | Endpoint | Body |
|---|---|---|
| List | POST /v1/intellect/list |
{"filter":{},"pager":{"pageIndex":1,"pageSize":30}} |
| Get | POST /v1/intellect/get |
{"id":1389} |
| Update | POST /v1/intellect/update |
See § Configure an Intellect |
| Delete | POST /v1/intellect/delete |
{"id":1389} |
Create (POST /v1/intellect/add) isn't listed here. See Agent Components · Create and Configure an Intellect for creating an intellect with the SDK's default-applying mgmt.intellects.create().
Deleting an agent does not delete its avatar or intellect.
mgmt.setForcedLanguage({ configId, agentId, language, languageName?, asrProvider? }, ks) forces the reply language and the speech-recognition language in one idempotent call. Usage and options: Languages.
Typed setters on mgmt.intellectConfig for the other single-purpose fields, all (configId, value, ks) and idempotent:
| Setter | Field | Clear with |
|---|---|---|
setModelConfiguration |
model_configuration (model_id in MODEL_IDS, max_output_tokens, thinking_level in THINKING_LEVELS, temperature) |
null |
setOpeningPhrase |
opening_phrase (the opening line of every avatar session; a template over requestVars and sys__*; SILENT_OPENING for no spoken opening, see Start the Conversation) |
null |
setThreadStartTools |
thread_start_tools: unique tool ids run in order when a thread starts, before the first user turn[^1] |
[] |
setAvatarSummaryConfig |
avatar_summary_config (prompt, analysis, template, content_type in SUMMARY_CONTENT_TYPES)[^2] |
null |
setSkillIds |
skill_ids ({ id, mode, condition? }, mode in SKILL_MODES) |
[] |
Each validates client-side and throws bad_request before any network call. See Agent Components · Create and Configure an Intellect for field semantics.
[^1]: Use api and code tools here. An api tool with wait_for_response is awaited. Every other tool runs without waiting. A failing tool does not fail the conversation. A thread-start tool never appears as a tool segment in the stream.
[^2]: null restores the defaults (analysis: { summary }, template: '{{ summary }}', content_type: 'text'). Every {{ key }} in template must be an analysis key (or summary when analysis is unset). The SDK rejects a mismatch client-side. The summary is skipped entirely when the thread has no human messages.
Tools
A standalone, partner-level entity, not embedded in an intellect. A name that is already in use returns 409. Skills below follow the same rule.
| Operation | Endpoint | Body |
|---|---|---|
| List | POST /v1/tool/list |
{"filter":{"objectType":"ToolListFilter"},"pager":{"pageIndex":1,"pageSize":30}} |
| Get | POST /v1/tool/get |
{"id":"TOOL_UUID"} |
| Add | POST /v1/tool/add |
{"name":"...", "config":{...}} |
| Update | POST /v1/tool/update |
{"id":"TOOL_UUID", "name"?, "config"?} |
| Delete | POST /v1/tool/delete |
{"id":"TOOL_UUID"} |
mgmt.tools.delete refuses by default with a typed tool_in_use error naming every intellect still listing the id in tool_ids. Pass {confirmPermanent:true, force:true} to delete anyway. Drop the id from each intellect's tool_ids first via mgmt.intellectConfig.setToolIds.
Skills
A standalone, partner-level reusable-instruction entity: {id (uuid), name, description, instructions}. SDK: mgmt.skills. A name that is already in use returns 409. Tools above follow the same rule.
| Operation | Endpoint | Body |
|---|---|---|
| List | POST /v1/skill/list |
{"filter":{"objectType":"SkillListFilter"},"pager":{"pageIndex":1,"pageSize":30}} |
| Get | POST /v1/skill/get |
{"id":"SKILL_UUID"} |
| Add | POST /v1/skill/add |
{"name":"...", "description":"...", "instructions"?} |
| Update | POST /v1/skill/update |
{"id":"SKILL_UUID", "name"?, "description"?, "instructions"?}. Idempotent. A rename to a name already in use returns 409 |
| Delete | POST /v1/skill/delete |
{"id":"SKILL_UUID"}. Replies {id}. A follow-up get returns 404 |
Shared skills. A shared skill (partner_id: 0) appears in skills.list and can be attached with setSkillIds. skills.get, skills.update and skills.delete answer 404 not_found for it, so you cannot read, edit or delete it. setSkillIds rejects a skill id that does not exist or that you cannot use with 403 forbidden. intellectConfig.audit and doctor accept an attached shared skill.
Before deleting a Skill, mgmt.skills.delete lists every intellect and refuses with a typed skill_in_use error naming each one still referencing the id in skill_ids. Pass {confirmPermanent:true, force:true} to delete anyway. mgmt.tools.delete has the same guard with a tool_in_use error.
Threads
Token rules per method: Conversation & Analytics § Threads. threads.push needs an admin token. Which threads a token can reach: Security & Compliance § Session type. SDK: mgmt.threads.{list, get, rename, setAnalysis, clearAnalysis, push, delete, transcript}.
| 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"} |
| Set analysis | POST /v1/thread/update |
{"id":"UUID","thread_metadata":{"analysis":{...}}}. Shallow merge one level under analysis. A key whose value changes fires analysis_updated. A patch that changes nothing fires no event. SDK: mgmt.threads.setAnalysis(id, patch, ks). |
| Clear analysis | POST /v1/thread/update |
{"id":"UUID","thread_metadata":{}}. Clears analysis. SDK: mgmt.threads.clearAnalysis(id, ks). |
| Push | POST /thread/push (no v1/ prefix on this route) |
{"id":"UUID","content":"...","request_vars"?:{...},"system_message"?:"..."}. delivered:false in the reply means no live socket is attached. The message is still saved and appears in the Messages list. SDK: mgmt.threads.push({id,content,request_vars?,system_message?}, ks). |
| Delete | POST /v1/thread/delete |
{"thread_ids":["UUID"]}. See Conversation & Analytics § Threads for delete behavior |
| Transcript | POST /v1/thread/get_transcripts |
{"id":"UUID"} |
Filter fields (list): agentIdEquals, contextIdEqual, createdAtGreaterThanOrEqual, createdAtLessThanOrEqual, idEquals, idsIn, isEverywhere, orderBy, partnerIdEquals, statusEquals, statusIn, updatedAtGreaterThanOrEqual, updatedAtLessThanOrEqual, userIdEquals.
Rules to know:
- Put
orderByinsidefilter(one of+createdAt,-createdAt,+updatedAt,-updatedAt). - Send
statusEquals/statusInas numbers,0or1. - Use only the filter fields listed above.
partnerIdInis not supported. - Pager is
{pageIndex, pageSize}(1-based). UsepageIndex/pageSize, not{offset, limit}. pageSizeis at most 500. This applies to every list and report pager in this section (Threads, Messages, Feedback, Followups).
SDK: mgmt.threads.list(ks, opts) merges opts.filter under the fixed objectType, so it can't be overridden. agentIdEquals filters threads by agent. There is no "in" form: agentIdIn throws a validation_error before any network call.
request_vars on push goes through the same reserved-name check as converse, before any network call. See Conversation & Analytics § Reserved Template Variables.
See Conversation & Analytics § Threads for response shapes and delete behavior.
Messages, Feedback & Followups
SDK: mgmt.messages, mgmt.feedback, mgmt.followups. messages.list, get and share accept an admin, conversation or agent token (see Conversation & Analytics § Threads). messages.report, messages.reportSummary, feedback.list and followups.list need an admin token.
| Operation | Endpoint | Body |
|---|---|---|
| List messages | POST /message/list |
{"filter":{"objectType":"GenieListMessageFilter"},"pager":{"pageIndex":1,"pageSize":50}}. opts.threadId is sugar for filter.threadIdEquals and wins if both are given. |
| Share a message | POST /message/share |
{"id":"MSG_ID","newTitle":"..."} → {"status":"success","data":{"newMessageId":"..."}}. SDK: mgmt.messages.share(id, newTitle, ks) unwraps this and returns {newMessageId}. It throws server_error if no id comes back. The clone is readable with messages.get and cannot be deleted. |
| Message report (CSV) | POST /message/report |
{"filter":{"objectType":"GenieListMessageFilter"}}. ⚠️ SENSITIVE: contains end-user ids and names plus verbatim question and feedback text. SDK: mgmt.messages.report(ks, opts) (raw CSV) or reportSummary(ks, opts) (parsed, with a _meta provenance receipt). reportSummary uses pageSize 500 (the maximum) when you omit it. |
| Add feedback | POST /feedback/add |
{"schemaVersion":1,"data":{"message_id":"...","is_positive":true,"comment"?:"..."}}. Idempotent for a given (message_id, is_positive) pair. Any KS (conversation or admin). Read ratings back with mgmt.feedback.list (below). |
| List feedback | (built on /message/list, no dedicated endpoint) |
mgmt.feedback.list(ks, opts). Details below. |
| Suggested followups | POST /followup/get-suggested-questions?new_response=true |
{}. Starter questions for the partner or agent, not scoped to a thread. The set can differ between calls. Returns [] when none are configured. Distinct from per-answer followups (capabilities.generate_followup_questions:on on converse). SDK: mgmt.followups.getSuggested(ks). |
| List followup records | POST /followup/list |
{"filter":{"objectType":"GenieListQuestionFilter"},"pager":{"pageIndex":1,"pageSize":30}}. The raw partner-wide record listing, not the shortlist above. |
List feedback in depth. mgmt.feedback.list(ks, opts) reads feedback. It queries POST /message/list (and, for filter.agentIdEquals, POST /v1/thread/list first) and returns only the messages that carry a rating.
Filter: messageIdEquals, messageIdsIn, threadIdEquals, agentIdEquals, isPositiveEquals. agentIdEquals matches only threads opened with a token minted with that agentId (createAgentToken, or createConversationToken with agentId). It costs one thread query per matching thread. Set at least one of messageIdEquals, messageIdsIn, threadIdEquals or agentIdEquals. With none set, it reads every message for the partner.
Unlike every other list() in this SDK, awaiting feedback.list(...) collects every matching row across all underlying pages, not just the first page. Use a narrow filter.
Row shape: {message_id, thread_id, genie_id, user_id, is_positive, comment, created_at, updated_at}. ⚠️ SENSITIVE: comments are end-user-entered text.
Messages filter fields: createdAtGreaterThanOrEqual, createdAtLessThanOrEqual, genieIdEquals, idEquals, idsIn, isPositiveEquals, isPositiveIn, orderBy, threadIdEquals, updatedAtGreaterThanOrEqual, updatedAtLessThanOrEqual, userIdEquals. Send only the keys listed. Sort with filter.orderBy.
threads.list, messages.list and followups.list merge opts.filter under a fixed objectType that you cannot override. feedback.list has its own filter shape (above).
Knowledge records
Full record lifecycle. SDK: mgmt.knowledge. An intellect links to a record through knowledge_ids. See Agent Components · Ground the Agent in Your Content (RAG) § Ground the Agent.
| Operation | Endpoint | Body |
|---|---|---|
| List | POST /v1/knowledge/list |
{"filter":{},"pager":{"pageIndex":1,"pageSize":30}}. Finds records without knowing ids up front. SDK: mgmt.knowledge.list(ks, opts). Distinct from mgmt.knowledge.listCategoryEntries(categoryId, ks), which lists KMS entries inside one category, not Knowledge record containers. |
| Add | POST /v1/knowledge/add |
{"name":"..."} |
| Get | POST /v1/knowledge/get |
{"id":2049} |
| Update | POST /v1/knowledge/update |
{"id":2049, ...fields}. When you send config, send the full config. To change sources, use addSource/removeSource below. |
| Delete | POST /v1/knowledge/delete |
{"id":2049}. Returns HTTP 200 with body null. A follow-up get returns 404. |
| Per-entry status | POST /v1/knowledge/entry_status |
{"knowledge_id":2049, "entry_ids":["0_abc123"]}. SDK: mgmt.knowledge.entryStatus(id, entryIds, ks). |
mgmt.knowledge.isIndexed(id, ks) wraps Get and reads status/config.sources[].indexers[].index_position. status does not tell you that indexing is complete. For the completion check, see Agent Components · Ground the Agent in Your Content (RAG) § Ground the Agent.
mgmt.knowledge.addSource(id, source, ks) / removeSource(id, source, ks) read-merge-write one source into/out of config.sources without disturbing the others. Both skip the write (applied:false) when an identical source object is already present / already absent. Call them one at a time for the same record id. Two overlapping calls can lose one of the changes.
Before deleting a record, mgmt.knowledge.deleteRecord lists every intellect and refuses with a typed knowledge_in_use error naming each one still carrying the id in knowledge_ids. Pass {confirmPermanent:true, force:true} to delete anyway. mgmt.tools.delete and mgmt.skills.delete use the same guard.
Lifecycle
An event-driven rule engine, not embedded in an intellect. SDK: mgmt.lifecycle. Full reference (rule shape, action types, CRUD + discovery methods) and a worked recipe: Lifecycle Rules. To check existing rules, and a partner's agents and intellects, for mistakes, use mgmt.lifecycle.audit, mgmt.intellectConfig.audit and mgmt.doctor. They only read. See Audit your rules.
Insight Settings
Reusable custom-insight definitions (key/title/prompt/valueType), referenced by id from a lifecycle rule's triggerInsightSettingsKai action. Not embedded in an intellect. SDK: mgmt.insightSettings. See Lifecycle Rules.
| Operation | Endpoint | Body |
|---|---|---|
| Create | POST /v1/insight-settings/create |
{"key":"NEXT_STEP","title":"Next step","prompt":"...","valueType":"string"}. All 4 fields required |
| Get | POST /v1/insight-settings/get |
{"id":"<id>"} |
| List | POST /v1/insight-settings/list |
{"filter":{"statusEqual":"active"},"pager":{"offset":0,"limit":30}} |
| Update | POST /v1/insight-settings/update |
{"id":"<id>", ...fields}. Any of key/title/prompt/valueType/status |
| Delete | POST /v1/insight-settings/delete |
{"id":"<id>"}. Remove the id from every lifecycle rule that references it before you delete it |
Full reference and a worked recipe: Lifecycle Rules § InsightSettings.