Skip to main content

📦 Catalog Service

The catalog is your library of faces and voices. Mix and match to create unique avatars.

Every avatar in the platform is built from two types of catalog items:

  • 👁️ Visual items — the avatar's appearance (uploaded as an image or processed visual asset)
  • 🗣️ Voice items — the avatar's voice (uploaded as a reference audio sample)

You browse, upload, and manage these building blocks through the Catalog Service. Once you have the catalog item IDs you need, head to the Avatar Service to combine them.

Base route: POST /service/catalog-item/action/{actionName}

Presets vs. custom items

The catalog contains both preset items (provided by Kaltura, with partnerId: 0) and custom items (uploaded by you, with your partnerId). List the catalog to see what presets are available before uploading your own.

Data Model

A visual catalog item represents an avatar's appearance.

FieldTypeDescription
itemIdstringUnique catalog item identifier
type'Visual'Item type
attributes.visual.background'Color' | 'Image'Background type
attributes.visual.namestringDisplay name for this visual
attributes.visual.genderPresentation'Masculine' | 'Feminine'Gender presentation
attributes.visual.skinTone'Light' | 'LightMedium' | 'MediumTan'Skin tone
attributes.visual.ageGroup'YoungAdult' | 'OlderAdult'Age group
attributes.visual.hairColor'Black' | 'Brown' | 'Blonde' | 'Red' | 'Gray'Hair color
attributes.visual.hairStylestring[]Hair style tags
attributes.visual.clothingstring[]Clothing style tags
attributes.visual.glassesbooleanWhether the avatar wears glasses
imageUrlstringThumbnail preview URL
agentScopestringRestricts item to a specific agent (optional)
adminTagsstring[]Tags for filtering and organization
createdBystringUser who created this item
createdAtDateCreation timestamp
updatedAtDateLast modification timestamp

Actions

get — Retrieve a Catalog Item

POST /service/catalog-item/action/get

Request body:

{
"itemId": "catalog_item_id_123"
}
FieldTypeRequiredDescription
itemIdstring✅ YesThe catalog item ID to retrieve

Response:

{
"itemId": "catalog_item_id_123",
"type": "Visual",
"attributes": {
"visual": {
"name": "Professional Maya",
"background": "Color",
"genderPresentation": "Feminine",
"skinTone": "LightMedium",
"ageGroup": "YoungAdult",
"hairColor": "Brown",
"hairStyle": ["straight", "shoulder-length"],
"clothing": ["business-casual"],
"glasses": false
}
},
"imageUrl": "https://cdn.kaltura.com/catalog/preview-maya.jpg",
"adminTags": ["production"],
"createdBy": "admin@company.com",
"createdAt": "2024-01-05T08:00:00.000Z",
"updatedAt": "2024-01-05T08:00:00.000Z"
}

Examples:

cURL
curl -X POST "https://YOUR_API_HOST/service/catalog-item/action/get" \
-H "Authorization: KS YOUR_KS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "itemId": "catalog_item_id_123" }'
JavaScript
const item = await fetch('https://YOUR_API_HOST/service/catalog-item/action/get', {
method: 'POST',
headers: {
Authorization: `KS ${ksToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ itemId: 'catalog_item_id_123' }),
}).then((res) => res.json());

list — List Catalog Items

POST /service/catalog-item/action/list

The most powerful action for browsing the catalog. Filter by type, language, tags, or agent scope.

Request body:

{
"filter": {
"typeEqual": "Visual",
"languageEqual": "en-US",
"adminTagsIn": ["production"]
},
"agentScope": "550e8400-e29b-41d4-a716-446655440000",
"pager": {
"offset": 0,
"limit": 30
},
"orderBy": "-createdAt"
}
FieldTypeRequiredDescription
filter.typeEqual'Visual' | 'Voice'NoFilter to only visual or only voice items
filter.languageEqualstringNoFilter voices by language code
filter.itemIdEqualstringNoFilter to a specific item ID
filter.adminTagsInstring[]NoFilter by any of these admin tags
agentScopestringNoInclude agent-scoped items alongside global items
pager.offsetnumberNoPagination offset (default: 0)
pager.limitnumberNoMax results per page (default: 30)
orderBystringNoSort order: +createdAt or -createdAt

Response:

{
"objects": [ ... ],
"totalCount": 24
}

Examples:

cURL — list all English voices
curl -X POST "https://YOUR_API_HOST/service/catalog-item/action/list" \
-H "Authorization: KS YOUR_KS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"typeEqual": "Voice",
"languageEqual": "en-US"
},
"pager": { "offset": 0, "limit": 30 }
}'
JavaScript — browse all visual items
const { objects, totalCount } = await fetch('https://YOUR_API_HOST/service/catalog-item/action/list', {
method: 'POST',
headers: {
Authorization: `KS ${ksToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
filter: { typeEqual: 'Visual' },
pager: { offset: 0, limit: 30 },
orderBy: '-createdAt',
}),
}).then((res) => res.json());

console.log(`Found ${totalCount} visual items in the catalog`);
objects.forEach((item) => {
console.log(`- ${item.itemId}: ${item.attributes.visual?.name}`);
});

create — Upload a Catalog Item

POST /service/catalog-item/action/create
🔀 This one's different — it uses multipart/form-data!

Unlike the other actions (which use JSON), create and update use multipart/form-data to support file uploads. The attributes field is sent as a JSON string within the form data.

Content-Type: multipart/form-data

Form fields:

FieldTypeRequiredDescription
fileFile✅ YesImage file for visual items, or audio file for voice items
agentScopestringNoRestrict this item to a specific agent UUID
adminTagsstring (JSON array)NoTags for filtering, e.g. ["production","english"]
attributesstring (JSON)NoAttributes JSON string (see below)
📏 File requirements
Item TypeAccepted FormatsMax Size
Visualimage/* (JPG, PNG, WebP, etc.)10 MB
Voiceaudio/mpeg (MP3)10 MB

Files outside these types or over 10 MB will return a 400 error.

Attributes JSON for visual items:

{
"visual": {
"name": "Professional Maya",
"background": "Color",
"genderPresentation": "Feminine",
"skinTone": "LightMedium",
"ageGroup": "YoungAdult",
"hairColor": "Brown",
"hairStyle": ["straight", "shoulder-length"],
"clothing": ["business-casual"],
"glasses": false
}
}

Attributes JSON for voice items:

{
"voice": {
"name": "Emma",
"description": "Warm, professional female voice",
"language": "en-US"
}
}

Examples:

curl -X POST "https://YOUR_API_HOST/service/catalog-item/action/create" \
-H "Authorization: KS YOUR_KS_TOKEN" \
-F "file=@/path/to/avatar-image.jpg" \
-F 'adminTags=["production"]' \
-F 'attributes={
"visual": {
"name": "Professional Maya",
"background": "Color",
"genderPresentation": "Feminine",
"skinTone": "LightMedium",
"ageGroup": "YoungAdult",
"hairColor": "Brown",
"glasses": false
}
}'

Error responses:

StatusDescription
400Invalid file type, file too large, or malformed attributes JSON
401Invalid or expired KS token
403Missing required subscription feature (AVATAR_STUDIO or PersonalAvatar)

update — Update a Catalog Item

POST /service/catalog-item/action/update

Update metadata or replace the file of an existing catalog item.

Content-Type: multipart/form-data

Form fields:

FieldTypeRequiredDescription
itemIdstring✅ YesThe item to update
fileFileNoReplace the existing file
agentScopestring | nullNoUpdate or remove agent scope (null to make global)
adminTagsstring (JSON array)NoReplace the full tags list
attributesstring (JSON)NoReplace the full attributes object

Examples:

cURL — update tags only
curl -X POST "https://YOUR_API_HOST/service/catalog-item/action/update" \
-H "Authorization: KS YOUR_KS_TOKEN" \
-F "itemId=catalog_item_id_123" \
-F 'adminTags=["production","v2"]'
JavaScript — update tags only
const formData = new FormData();
formData.append('itemId', 'catalog_item_id_123');
formData.append('adminTags', JSON.stringify(['production', 'v2']));

await fetch('https://YOUR_API_HOST/service/catalog-item/action/update', {
method: 'POST',
headers: { Authorization: `KS ${ksToken}` },
body: formData,
});

delete — Delete a Catalog Item

POST /service/catalog-item/action/delete

Permanently deletes a catalog item. This cannot be undone.

Request body:

{
"itemId": "catalog_item_id_123"
}
FieldTypeRequiredDescription
itemIdstring✅ YesThe catalog item to delete

Response:

{
"itemId": "catalog_item_id_123"
}
⚠️ Check avatars before deleting

If you delete a catalog item that's referenced by an avatar (voice.id or visual.id), the avatar will still hold a reference to the deleted ID. Remove or update those avatar references first to avoid broken configurations.

Examples:

cURL
curl -X POST "https://YOUR_API_HOST/service/catalog-item/action/delete" \
-H "Authorization: KS YOUR_KS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "itemId": "catalog_item_id_123" }'
JavaScript
await fetch('https://YOUR_API_HOST/service/catalog-item/action/delete', {
method: 'POST',
headers: {
Authorization: `KS ${ksToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ itemId: 'catalog_item_id_123' }),
});

Usage Notes

Agent Scope

The agentScope field restricts a catalog item to a specific agent. When listing catalog items with an agentScope parameter, you'll get:

  • All unscoped (global) items
  • All items scoped to that agent

This is useful for giving individual agents exclusive avatar options while still having a shared pool of global items.

Admin Tags

Use adminTags to organize your catalog for filtering. Common conventions:

  • production / staging / test — environment markers
  • Language codes: en, es, fr
  • Style categories: professional, casual, animated
  • Version markers: v1, v2

Visual Attributes as Metadata

The visual attributes (background, skinTone, hairColor, etc.) are metadata — they describe the visual for search/filtering purposes, but they don't affect the rendered output. The actual visual is determined by the uploaded file. Tag your catalog items accurately so they're discoverable when filtering by genderPresentation, skinTone, etc.

Next Steps

  • 🎭 Avatar Service — Use your itemId values to create avatars
  • 🛠️ Workflows — See the complete end-to-end setup with catalog items