Kaltura Multi-Account Management API¶
Kaltura's multi-account model enables organizations to create and manage hierarchies of accounts — a parent account that controls one or more child accounts. Each child account is fully independent (its own content, users, settings, and partner ID) while the parent retains administrative oversight and cross-account analytics.
Base URL: https://www.kaltura.com/api_v3
Auth: Admin KS on the parent account for management operations
Format: Form-encoded POST, format=1 for JSON responses
1. When to Use Multi-Account Management¶
Multi-account management solves organizational challenges where a single Kaltura account is insufficient:
| Scenario | Why Multi-Account |
|---|---|
| Enterprise with divisions | Each division manages its own content library independently, while corporate HQ monitors usage and analytics across all divisions |
| Reseller / channel partner | A Kaltura reseller provisions separate accounts for each customer while maintaining centralized billing and usage reporting |
| SaaS platform embedding Kaltura | Each tenant gets an isolated Kaltura account with its own content, users, and permissions — the platform manages them all from one parent |
| Educational institution | Separate accounts per department or campus with centralized administration |
A multi-account setup consists of:
- Parent account — The administrative account that creates and manages child accounts
- Child accounts — Sub-accounts, each with their own partner ID, content library, users, and configuration
The parent account can:
- Create new child accounts via partner.register
- Authenticate as any child account via session.impersonate
- Aggregate analytics across all child accounts using cross-account report variants
- Manage child account settings and permissions
Multi-account analytics require the FEATURE_MULTI_ACCOUNT_ANALYTICS permission (ID 1130) on the parent account. Contact your Kaltura account manager to enable this.
2. Prerequisites¶
- An ADMIN KS (type=2) on the parent account with sub-account management permissions (see Session Guide)
- The multi-account feature enabled on your parent account — contact your Kaltura account manager to provision child account creation
- For cross-account analytics: the
FEATURE_MULTI_ACCOUNT_ANALYTICSpermission (ID 1130) enabled on the parent account
3. Sub-Account Creation¶
Create a child account linked to the parent using partner.register:
curl -X POST "$KALTURA_SERVICE_URL/service/partner/action/register" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "partner[objectType]=KalturaPartner" \
-d "partner[name]=Child Account - Division A" \
-d "partner[adminName]=Admin User" \
-d "partner[adminEmail]=admin@division-a.example.com" \
-d "partner[description]=Division A sub-account" \
-d "partner[country]=US"
| Parameter | Type | Required | Description |
|---|---|---|---|
partner[objectType] |
string | Yes | Always KalturaPartner |
partner[name] |
string | Yes | Display name for the child account |
partner[adminName] |
string | Yes | Name of the account admin user |
partner[adminEmail] |
string | Yes | Admin email — receives account credentials |
partner[description] |
string | No | Description of the child account |
partner[country] |
string | No | Two-letter country code (e.g., US, GB, DE) |
partner[website] |
string | No | Organization website URL |
Response:
{
"id": 5678901,
"name": "Child Account - Division A",
"adminEmail": "admin@division-a.example.com",
"status": 1,
"partnerParentId": 1234567,
"objectType": "KalturaPartner"
}
| Response Field | Type | Description |
|---|---|---|
id |
integer | Partner ID of the newly created child account |
name |
string | Account display name |
adminEmail |
string | Admin email address |
status |
integer | Account status: 1 = ACTIVE, 2 = BLOCKED |
partnerParentId |
integer | Parent account's partner ID — set automatically |
adminSecret |
string | Admin secret for the child account (returned only on creation) |
The partnerParentId field links the child to the parent account. This relationship is set automatically when a parent account creates the child.
Testing note:
partner.registercreates a real child account that cannot be deleted via the API. It requires the calling account to have sub-account creation permissions (SERVICE_FORBIDDENis returned otherwise). Test this action only when you intend to create a permanent child account. Verify your account has the necessary permissions before calling this endpoint.
4. Sub-Account Management¶
4.1 List Child Accounts¶
curl -X POST "$KALTURA_SERVICE_URL/service/partner/action/list" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "filter[objectType]=KalturaPartnerFilter" \
-d "filter[statusIn]=1,2" \
-d "pager[pageSize]=50" \
-d "pager[pageIndex]=1"
| Filter Parameter | Type | Description |
|---|---|---|
filter[objectType] |
string | Always KalturaPartnerFilter |
filter[statusIn] |
string | Comma-separated status values: 1 = ACTIVE, 2 = BLOCKED |
filter[idIn] |
string | Comma-separated partner IDs to filter |
filter[idGreaterThanOrEqual] |
integer | Filter by minimum partner ID |
filter[partnerParentIdEqual] |
integer | Filter children of a specific parent |
pager[pageSize] |
integer | Results per page (max 500) |
pager[pageIndex] |
integer | Page number (1-based) |
Response: { "objects": [...], "totalCount": N } — each object is a KalturaPartner with id, name, status, partnerParentId, adminEmail, and configuration fields.
4.2 Get Child Account Details¶
curl -X POST "$KALTURA_SERVICE_URL/service/partner/action/get" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "id=$CHILD_PARTNER_ID"
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Partner ID of the child account to retrieve |
Returns the full KalturaPartner object with account configuration, status, features, and parent relationship.
4.3 Update Child Account¶
curl -X POST "$KALTURA_SERVICE_URL/service/partner/action/update" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "partner[objectType]=KalturaPartner" \
-d "partner[name]=Updated Division Name" \
-d "partner[description]=Updated description" \
-d "id=$CHILD_PARTNER_ID"
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Partner ID of the child account to update |
partner[objectType] |
string | Yes | Always KalturaPartner |
partner[name] |
string | No | Updated display name |
partner[description] |
string | No | Updated description |
partner[adminEmail] |
string | No | Updated admin email |
partner[country] |
string | No | Updated country code |
5. Cross-Account Authentication¶
5.1 session.impersonate¶
Generate a KS scoped to a child account while authenticated as the parent. This lets the parent operate as if it were the child account — managing content, users, and running reports.
curl -X POST "$KALTURA_SERVICE_URL/service/session/action/impersonate" \
-d "format=1" \
-d "secret=$KALTURA_ADMIN_SECRET" \
-d "impersonatedPartnerId=$CHILD_PARTNER_ID" \
-d "userId=admin" \
-d "type=2" \
-d "partnerId=$KALTURA_PARTNER_ID" \
-d "expiry=1800"
| Parameter | Type | Required | Description |
|---|---|---|---|
secret |
string | Yes | Admin secret of the parent account |
impersonatedPartnerId |
integer | Yes | Partner ID of the child account to impersonate |
userId |
string | No | User ID for the impersonated session (default: calling user) |
type |
integer | Yes | KS type: 0 = USER, 2 = ADMIN |
partnerId |
integer | Yes | Partner ID of the parent account |
expiry |
integer | No | Session TTL in seconds (default: 86400) |
privileges |
string | No | KS privileges (e.g., disableentitlement, edit:*) |
Response: Returns a KS string scoped to the child account. Use this KS for any API calls that should execute in the child account context.
Use cases:
- Admin tools that manage content across all child accounts
- Running standard reports scoped to a specific child account
- Provisioning users or content in a child account from a centralized system
The impersonated KS operates with the same permissions as a native admin KS on the child account. Use short TTLs and scope privileges minimally.
session.impersonateonly works between parent and child accounts — you cannot impersonate accounts that are not your children.
6. Cross-Account Analytics¶
Cross-account analytics let the parent account aggregate data across all child accounts without impersonating each one individually.
6.1 Multi-Account Report Types¶
Every standard VOD report has a multi-account counterpart in the 20000 range. These reports automatically aggregate across all child accounts linked to the parent.
| Multi-Account Report ID | Standard Counterpart | Report Name |
|---|---|---|
| 20001 | 1 | Content Dropoff |
| 20002 | 2 | Content Interactions |
| 20003 | 3 | Content Contributions |
| 20005 | 5 | Platforms |
| 20006 | 6 | Operating Systems |
| 20007 | 7 | Browsers |
| 20010 | 10 | Top Content |
| 20011 | 11 | Unique Users (Play) |
| 20012 | 12 | Map Overlay (Country) |
| 20013 | 13 | Map Overlay (Region) |
| 20014 | 14 | Map Overlay (City) |
| 20018 | 18 | Top Syndication |
| 20019 | 19 | Partner Usage |
| 20020 | 20 | Platforms (Extended) |
| 20021 | 21 | Operating Systems (Extended) |
| 20022 | 22 | Browsers (Extended) |
| 20023 | 23 | Top Playback Context |
6.2 Aggregated Reports¶
Query a multi-account report from the parent account to get data aggregated across all child accounts:
curl -X POST "$KALTURA_SERVICE_URL/service/report/action/getTable" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "reportType=20010" \
-d "reportInputFilter[objectType]=KalturaEndUserReportInputFilter" \
-d "reportInputFilter[fromDate]=$FROM_TIMESTAMP" \
-d "reportInputFilter[toDate]=$TO_TIMESTAMP" \
-d "pager[pageSize]=100" \
-d "pager[pageIndex]=1" \
-d "responseOptions[objectType]=KalturaReportResponseOptions" \
-d "responseOptions[delimiter]=|"
| Parameter | Type | Required | Description |
|---|---|---|---|
reportType |
integer | Yes | Multi-account report ID (20001-20023) or cross-partner report ID |
reportInputFilter[objectType] |
string | Yes | Always KalturaEndUserReportInputFilter |
reportInputFilter[fromDate] |
integer | Yes | Start of date range as Unix timestamp |
reportInputFilter[toDate] |
integer | Yes | End of date range as Unix timestamp |
pager[pageSize] |
integer | No | Results per page (default: 25, max: 500) |
pager[pageIndex] |
integer | No | Page number, 1-based (default: 1) |
objectIds |
string | No | Comma-separated partner IDs to filter to specific child accounts |
responseOptions[objectType] |
string | No | Always KalturaReportResponseOptions |
responseOptions[delimiter] |
string | No | Column delimiter in response (default: ,, recommended: \|) |
The partnerParentId dimension in the analytics backend links child account data to the parent, enabling aggregation without explicit child account enumeration.
6.3 Per-Account Usage Reports¶
Use cross-partner report types to break down usage by child account:
# Partner usage breakdown (report type 201)
curl -X POST "$KALTURA_SERVICE_URL/service/report/action/getTotal" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "reportType=201" \
-d "reportInputFilter[objectType]=KalturaEndUserReportInputFilter" \
-d "reportInputFilter[fromDate]=$FROM_TIMESTAMP" \
-d "reportInputFilter[toDate]=$TO_TIMESTAMP" \
-d "responseOptions[objectType]=KalturaReportResponseOptions" \
-d "responseOptions[delimiter]=|"
Filter to specific child accounts using objectIds:
# Usage for specific child accounts only
curl -X POST "$KALTURA_SERVICE_URL/service/report/action/getTable" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "reportType=60" \
-d "reportInputFilter[objectType]=KalturaEndUserReportInputFilter" \
-d "reportInputFilter[fromDate]=$FROM_TIMESTAMP" \
-d "reportInputFilter[toDate]=$TO_TIMESTAMP" \
-d "objectIds=CHILD_PARTNER_ID_1,CHILD_PARTNER_ID_2" \
-d "pager[pageSize]=100" \
-d "pager[pageIndex]=1" \
-d "responseOptions[objectType]=KalturaReportResponseOptions" \
-d "responseOptions[delimiter]=|"
Cross-partner report types:
| Report Type | Description | Key Columns |
|---|---|---|
| 60 | Per-account engagement breakdown | partner_id, count_plays, sum_time_viewed, count_loads |
| 201 | Bandwidth and storage totals per account | partner_id, bandwidth_consumption, average_storage, combined_bandwidth_storage |
| 19 | Partner usage summary | partner_id, count_plays, count_loads, count_contributions |
| 42 | Top content across accounts | entry_id, partner_id, count_plays, sum_time_viewed |
All report calls use the same parameters: reportType, reportInputFilter (with fromDate/toDate as Unix timestamps), pager, and responseOptions. See Analytics Reports Guide for the full report.getTable/report.getTotal parameter reference.
6.5 Report Response Format¶
Report responses use pipe-delimited strings for both header and data fields. Rows within data are separated by semicolons.
report.getTable Response Structure¶
{
"header": "partner_id|count_plays|sum_time_viewed|count_loads",
"data": "5678901|150|3600|400;5678902|75|1800|200",
"totalCount": 2,
"objectType": "KalturaReportTable"
}
| Field | Format | Description |
|---|---|---|
header |
Pipe-delimited string | Column names separated by \| |
data |
Semicolon-separated rows, pipe-delimited columns | Each row is separated by ;, and columns within each row are separated by \| |
totalCount |
Integer | Total rows available (for pagination) |
report.getTotal Response Structure¶
{
"header": "count_plays|unique_known_users|sum_time_viewed",
"data": "1250|340|45600",
"objectType": "KalturaReportTotal"
}
report.getTotal returns a single row (no semicolon separators in data).
Parsing Pipe-Delimited Report Data¶
Parse the header and data fields by splitting on the delimiter characters:
# Example: Parse a multi-account report response
RESPONSE=$(curl -s -X POST "$KALTURA_SERVICE_URL/service/report/action/getTable" \
-d "ks=$KALTURA_KS" \
-d "format=1" \
-d "reportType=20010" \
-d "reportInputFilter[objectType]=KalturaEndUserReportInputFilter" \
-d "reportInputFilter[fromDate]=$FROM_TIMESTAMP" \
-d "reportInputFilter[toDate]=$TO_TIMESTAMP" \
-d "pager[pageSize]=100" \
-d "pager[pageIndex]=1" \
-d "responseOptions[objectType]=KalturaReportResponseOptions" \
-d "responseOptions[delimiter]=|")
# Extract header and data using jq
HEADER=$(echo "$RESPONSE" | jq -r '.header')
DATA=$(echo "$RESPONSE" | jq -r '.data')
TOTAL=$(echo "$RESPONSE" | jq -r '.totalCount')
# Parse header into column names
IFS='|' read -ra COLUMNS <<< "$HEADER"
echo "Columns: ${COLUMNS[*]}"
# Parse each row
IFS=';' read -ra ROWS <<< "$DATA"
for ROW in "${ROWS[@]}"; do
IFS='|' read -ra FIELDS <<< "$ROW"
# Map fields to column names
for i in "${!COLUMNS[@]}"; do
echo " ${COLUMNS[$i]}: ${FIELDS[$i]}"
done
echo "---"
done
Example output for a multi-account Top Content report (20010):
Columns: object_id entry_name count_plays sum_time_viewed
object_id: 1_abc123
entry_name: Company Overview
count_plays: 150
sum_time_viewed: 3600
---
object_id: 1_def456
entry_name: Product Demo
count_plays: 75
sum_time_viewed: 1800
---
The
responseOptions[delimiter]parameter controls the column delimiter in the response. Use|(pipe) for consistency. If you set a different delimiter (e.g.,,), adjust your parsing logic accordingly. Semicolons always separate rows regardless of the column delimiter setting.
6.4 Drill-Down into a Child Account¶
To run standard reports for a specific child account, use session.impersonate to get a child-scoped KS, then run any standard report:
# Step 1: Impersonate the child account
CHILD_KS=$(curl -s -X POST "$KALTURA_SERVICE_URL/service/session/action/impersonate" \
-d "format=1" \
-d "secret=$KALTURA_ADMIN_SECRET" \
-d "impersonatedPartnerId=$CHILD_PARTNER_ID" \
-d "userId=admin" \
-d "type=2" \
-d "partnerId=$KALTURA_PARTNER_ID" \
-d "expiry=900" | jq -r '.result // .')
# Step 2: Run a standard report scoped to the child
curl -X POST "$KALTURA_SERVICE_URL/service/report/action/getTable" \
-d "ks=$CHILD_KS" \
-d "format=1" \
-d "reportType=10" \
-d "reportInputFilter[objectType]=KalturaEndUserReportInputFilter" \
-d "reportInputFilter[fromDate]=$FROM_TIMESTAMP" \
-d "reportInputFilter[toDate]=$TO_TIMESTAMP" \
-d "pager[pageSize]=50" \
-d "pager[pageIndex]=1" \
-d "responseOptions[objectType]=KalturaReportResponseOptions" \
-d "responseOptions[delimiter]=|"
7. Error Handling¶
| Error Code | Context | Meaning |
|---|---|---|
START_SESSION_ERROR |
session.impersonate |
The target impersonatedPartnerId is not a child of the calling account |
INVALID_KS |
Any API call | The impersonated KS has expired — generate a new one with session.impersonate |
SERVICE_FORBIDDEN |
partner.register |
The calling account lacks permission to create child accounts |
Empty objects array |
Multi-account reports (20001-20023) | The parent account does not have FEATURE_MULTI_ACCOUNT_ANALYTICS (ID 1130) enabled |
8. Best Practices¶
- Use
session.impersonatewith short TTLs. Impersonated sessions should be short-lived (5-15 minutes) and scoped to the specific task. - Prefer multi-account reports for aggregation. Use multi-account report variants (20001-20023) to get cross-account data in a single call rather than impersonating each child account individually.
- Track child account IDs. Maintain a mapping of child partner IDs to your internal organization structure for report filtering with
objectIds. - Ensure
FEATURE_MULTI_ACCOUNT_ANALYTICSis enabled. This permission (ID 1130) must be active on the parent account. Without it, multi-account reports return empty results. - Use
objectIdsfor targeted reports. Filter cross-partner reports to specific child accounts rather than pulling data for all accounts when you only need a subset. - Use AppTokens per child account. Create separate AppTokens for each child account integration. This enables independent revocation and audit trails per tenant.
9. Related Guides¶
- Session Guide — KS generation, including
session.impersonateauthentication - Analytics Reports — Full analytics API reference, report types, and cross-account report variants
- User Management — User provisioning for child accounts
- AppTokens — Scoped authentication tokens for child account integrations