---
title: Authentication
url: https://docs.kaltura.com/docs/conversational-agents/agentic-avatars-sdk/control-api-authentication
---

## Authentication

Learn how to authenticate API requests using API keys and JWT tokens.

### Two Authentication Methods

The Control API uses two different authentication methods:

#### 1. Kaltura Session (KS) (for session creation)

#### 2. JWT Token (for session control)

### Kaltura Session (KS) Authentication

**Used for:** Creating new sessions

**Header:** `Authorization: KS <ks>`

**Example:**

```bash
curl -X POST https://api.avatar.us.kaltura.ai/v1/avatar-session/create \
  -H "Authorization: KS your-kaltura-session-here" \
  -H "Content-Type: application/json" \
  -d '{"visualConfig": {"id": "visual-123"}}'
```

#### Getting Your Kaltura Session

You can generate a Kaltura Session (KS) using the Kaltura API. See the [Kaltura Session Creation Guide](https://knowledge.kaltura.com/help/how-to-create-a-kaltura-session-key) for detailed instructions.

Alternatively, contact your Kaltura representative for assistance.

#### Storing Your Kaltura Session

:::danger Security
**NEVER** expose your Kaltura Session in:

- Client-side code
- Frontend applications
- Public repositories
- Browser DevTools

**ALWAYS** store it:

- In environment variables
- In secure secret managers
- On your backend server only
:::

**Good practices:**

```bash
# .env file
AVATAR_KS=your-kaltura-session-here
```

```javascript
// Node.js
const KS = process.env.AVATAR_KS;
```

```python
# Python
import os
KS = os.environ.get('AVATAR_KS')
```

### JWT Token Authentication

**Used for:** All session operations (except creation)

**Header:** `Authorization: Bearer {token}`

**Example:**

```bash
curl -X POST https://api.avatar.us.kaltura.ai/v1/avatar-session/:sessionId/say-text \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello!"}'
```

#### How to Get a Token

The JWT token is returned when you create a session:

```javascript
const response = await fetch('https://api.avatar.us.kaltura.ai/v1/avatar-session/create', {
  method: 'POST',
  headers: {
    Authorization: `KS ${process.env.AVATAR_KS}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    visualConfig: { id: 'visual-123' },
  }),
});

const data = await response.json();
console.log('Token:', data.token); // JWT token here
console.log('Session ID:', data.sessionId);
```

#### Token Properties

- **Scope**: Valid only for the specific session
- **Expiration**: Tied to session lifetime
- **Permissions**: Full control over the session
- **Single use**: One token per session

#### Using the Token

```javascript
// After creating session
const { sessionId, token } = await createSession();

// Use token for all operations
await fetch(`https://api.avatar.us.kaltura.ai/v1/avatar-session/${sessionId}/say-text`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ text: 'Hello!' }),
});
```

### Authentication Flow

```
┌──────────────┐
│   Backend    │
│              │
│ 1. Store     │
│    KS        │
└──────┬───────┘
       │
       │ Create session with KS
       ▼
┌──────────────┐
│ Avatar API   │
│              │
│ 2. Validate  │
│    KS        │
│              │
│ 3. Generate  │
│    JWT token │
└──────┬───────┘
       │
       │ Return sessionId + token
       ▼
┌──────────────┐
│   Backend    │
│              │
│ 4. Send to   │
│    frontend  │
└──────┬───────┘
       │
       │ sessionId + token
       ▼
┌──────────────┐
│   Frontend   │
│              │
│ 5. Use token │
│    for       │
│    display   │
└──────────────┘
```

### Complete Example

#### Backend Server

```javascript
import express from 'express';
import fetch from 'node-fetch';

const app = express();
app.use(express.json());

// Kaltura Session stored securely
const AVATAR_KS = process.env.AVATAR_KS;
const AVATAR_BASE_URL = 'https://api.avatar.us.kaltura.ai/v1/avatar-session';

// Session storage (in-memory for demo, use database in production)
const sessions = new Map();

// Create session endpoint
app.post('/api/avatar/create-session', async (req, res) => {
  try {
    // Call Kaltura API with KS
    const response = await fetch(`$\{AVATAR_BASE_URL\}/create`, {
      method: 'POST',
      headers: {
        Authorization: `KS $\{AVATAR_KS\}`, // Secure on server
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        visualConfig: { id: req.body.visualId },
        voiceConfig: req.body.voiceId ? { id: req.body.voiceId } : undefined,
      }),
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }

    const data = await response.json();

    // Store session info
    sessions.set(data.sessionId, {
      token: data.token,
      createdAt: new Date(),
    });

    // Return to frontend (token is safe to send)
    res.json({
      sessionId: data.sessionId,
      token: data.token,
    });
  } catch (error) {
    console.error('Failed to create session:', error);
    res.status(500).json({ error: 'Failed to create session' });
  }
});

// Control endpoint (uses token)
app.post('/api/avatar/say-text', async (req, res) => {
  try {
    const { sessionId, token, text } = req.body;

    // Verify session exists
    if (!sessions.has(sessionId)) {
      return res.status(404).json({ error: 'Session not found' });
    }

    // Call Kaltura API with token
    const response = await fetch(`$\{AVATAR_BASE_URL\}/${sessionId}/say-text`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ text }),
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }

    const data = await response.json();
    res.json(data);
  } catch (error) {
    console.error('Failed to say text:', error);
    res.status(500).json({ error: 'Failed to say text' });
  }
});
```

#### Frontend Client

```javascript
// Create session (token from backend)
const { sessionId, token } = await fetch('/api/avatar/create-session', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    visualId: 'visual-123',
    voiceId: 'voice-456',
  }),
}).then((r) => r.json());

console.log('Session ID:', sessionId);
console.log('Token:', token);

// Use token for display
import { KalturaAvatarSession } from '@unisphere/models-sdk-js';

const session = new KalturaAvatarSession(token, {
  baseUrl: 'https://api.avatar.us.kaltura.ai/',
});
session.attachAvatar('avatar-container');

// Control from backend
await fetch('/api/avatar/say-text', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    sessionId,
    token,
    text: 'Hello from the backend!',
  }),
});
```

### Error Responses

#### 401 Unauthorized

**Kaltura Session:**

```json
{
  "success": false,
  "error": "Invalid Kaltura Session"
}
```

**Causes:**

- Kaltura Session is missing
- Kaltura Session is invalid
- Kaltura Session is expired

**Solutions:**

- Verify Kaltura Session is correct
- Check `Authorization: KS <ks>` header is set
- Contact Kaltura for new session

**Token:**

```json
{
  "success": false,
  "error": "Invalid or expired token"
}
```

**Causes:**

- Token is missing
- Token is invalid
- Session has ended
- Token has expired

**Solutions:**

- Verify token is correct
- Check `Authorization: Bearer {token}` header
- Create a new session if needed

#### 403 Forbidden

```json
{
  "success": false,
  "error": "Insufficient permissions"
}
```

**Causes:**

- Kaltura Session doesn't have required permissions
- Token doesn't match session

### Security Best Practices

#### 1. Kaltura Session Security

**DO:**

- Store in environment variables
- Use secret managers (AWS Secrets Manager, Azure Key Vault)
- Rotate sessions regularly
- Limit session scope if possible

**DON'T:**

- Commit to version control
- Include in client code
- Share via email or chat
- Log in plain text

#### 2. Token Security

**DO:**

- Send tokens over HTTPS only
- Store tokens temporarily
- Clear tokens when session ends
- Validate tokens server-side

**DON'T:**

- Store tokens long-term
- Share tokens between users
- Log tokens in plain text

#### 3. HTTPS Only

Always use HTTPS in production:

```javascript
// Bad - HTTP
const baseUrl = 'http://api.avatar.example.com';

// Good - HTTPS
const baseUrl = 'https://api.avatar.example.com';
```

#### 4. Request Validation

Validate all requests before calling the API:

```javascript
app.post('/api/avatar/say-text', async (req, res) => {
  // Validate input
  if (!req.body.text || req.body.text.length > 1000) {
    return res.status(400).json({ error: 'Invalid text' });
  }

  // Validate session exists
  if (!sessions.has(req.body.sessionId)) {
    return res.status(404).json({ error: 'Session not found' });
  }

  // Call API
  // ...
});
```

### Next Steps

- **[Create Session](/docs/conversational-agents/agentic-avatars-sdk/control-api-create-session)** - Create your first session
- **[Say Text](/docs/conversational-agents/agentic-avatars-sdk/control-api-say-text)** - Make the avatar speak
- **[API Examples](/docs/conversational-agents/agentic-avatars-sdk/control-api-examples)** - Complete working examples
