# Authentication
Source: https://docs.agenthuman.com/api-reference/authentication
How to authenticate with the Agent Human API
## Overview
Agent Human uses simple API key authentication. Include your API key in the `x-api-key` header with every request - that's it!
**Authentication Method:** API Key (Header-based)\
**Supported Endpoints:** All REST API endpoints (`/v1/*`)\
**Security:** Keys are encrypted in transit via HTTPS
**Quick Setup:** Get your API key from the [dashboard](https://app.agenthuman.com), add it to your environment variables and start building in minutes.
### Getting Your API Key
1. Sign in to your account at [app.agenthuman.com](https://app.agenthuman.com)
2. Navigate to [Settings → API Keys](https://app.agenthuman.com/settings/apikeys)
3. Click "New API Key"
4. Give your key a descriptive name
5. Copy and securely store your key
API keys are shown only once when created. Store them securely and never expose them in client-side code or public repositories.
## How to Authenticate
Include your API key in the `x-api-key` header with every request:
```bash cURL theme={null}
curl -X GET https://api.agenthuman.com/v1/sessions \
-H "x-api-key: ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```
```javascript JavaScript theme={null}
const response = await fetch('https://api.agenthuman.com/v1/sessions', {
headers: {
'x-api-key': 'ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
});
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.agenthuman.com/v1/sessions',
headers={'x-api-key': 'ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
)
```
## Environment Variables
For security, store API keys in environment variables:
```bash .env File theme={null}
AGENTHUMAN_API_KEY=ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
```javascript JavaScript theme={null}
const apiKey = process.env.AGENTHUMAN_API_KEY;
const response = await fetch('https://api.agenthuman.com/v1/sessions', {
headers: {
'x-api-key': apiKey
}
});
```
```python Python theme={null}
import os
import requests
api_key = os.environ.get('AGENTHUMAN_API_KEY')
response = requests.get(
'https://api.agenthuman.com/v1/sessions',
headers={'x-api-key': api_key}
)
```
## Security Best Practices
### Do's ✅
* Store API keys in environment variables or secure key management systems
* Use different keys for different environments (development, staging, production)
* Rotate keys regularly
* Monitor key usage for unusual activity
* Revoke compromised keys immediately
### Don'ts ❌
* Hard-code API keys in your source code
* Commit API keys to version control
* Share API keys via email or chat
* Use API keys in client-side JavaScript
* Log or display API keys in error messages
## Key Management
### Rotating Keys
Regularly rotate your API keys for enhanced security:
1. Create a new API key
2. Update your application to use the new key
3. Test thoroughly
4. Delete the old key
### Revoking Keys
If a key is compromised:
1. Sign in to your account immediately
2. Navigate to [Settings → API Keys](https://app.agenthuman.com/settings/apikeys)
3. Find the compromised key
4. Click "Delete" to revoke it instantly
5. Create a new key if needed
## Error Responses
### Invalid API Key
```json theme={null}
{
"error": "Invalid API key"
}
```
**HTTP Status:** `401 Unauthorized`
### Missing API Key
```json theme={null}
{
"error": "API key required"
}
```
**HTTP Status:** `401 Unauthorized`
### Deactivated Key
```json theme={null}
{
"error": "API key has been deactivated"
}
```
**HTTP Status:** `401 Unauthorized`
Most `/v1/*` endpoints return errors as `{ "success": false, "error": { "message": "...", "suggestion": "..." } }`, but authentication failures return an `error` string as shown above.
## Testing Authentication
### Using the API Playground
The easiest way to test your API key is using the built-in API Playground:
1. Navigate to any API endpoint documentation page
2. Look for the "API Playground" section
3. Enter your API key in the **x-api-key** field
4. Fill in any required parameters
5. Click "Send Request" to test the endpoint
The API Playground will automatically include your API key in all requests once entered.
### Manual Testing
You can also test your API key with these simple requests:
```bash cURL theme={null}
curl -X GET https://api.agenthuman.com/v1/sessions \
-H "x-api-key: your_api_key_here"
```
```javascript JavaScript theme={null}
// Test authentication
fetch('https://api.agenthuman.com/v1/sessions', {
headers: {
'x-api-key': 'your_api_key_here'
}
})
.then(response => {
if (response.ok) {
console.log('Authentication successful!');
} else {
console.error('Authentication failed:', response.status);
}
});
```
```python Python theme={null}
import requests
# Test authentication
response = requests.get(
'https://api.agenthuman.com/v1/sessions',
headers={'x-api-key': 'your_api_key_here'}
)
if response.status_code == 200:
print('Authentication successful!')
else:
print(f'Authentication failed: {response.status_code}')
```
## Need Help?
If you're having authentication issues:
1. Verify your API key is correct and hasn't been revoked
2. Check that you're using the correct header name (`x-api-key`)
3. Ensure you're using HTTPS for all requests
4. Contact support at [support@agenthuman.com](mailto:support@agenthuman.com) if issues persist
# Create Session
Source: https://docs.agenthuman.com/api-reference/endpoints/create-session
POST /v1/sessions
Create and start a new avatar video session
**What This Does:** Creates and automatically starts a session with a video room where your avatar will appear. The session is immediately set to `started` status and a server is allocated to run the avatar.
### Body
The avatar to use for this session. Accepts one of three formats:
**Recommended:** An avatar ID returned by the [Upload Face](/api-reference/endpoints/upload-face) endpoint (e.g. `avat_01H3Z8G9YR3K2N5M6P7Q8W4T`). This is the fastest and most reliable option — the avatar has already been validated, aligned and stored, so the session starts immediately.
**Accepted (paid plans only):**
* A publicly accessible image URL (e.g. `https://example.com/photo.jpg`)
* A base64-encoded image string (e.g. `data:image/jpeg;base64,...`)
When a URL or base64 string is provided, the image is automatically processed through the same pipeline as [Upload Face](/api-reference/endpoints/upload-face): face detection, alignment and upload to storage. The resulting avatar ID is then used for the session. This adds processing time to the request. **Free plan users must always provide an avatar ID.**
Video aspect ratio for the session. Must be one of: `4:3`, `3:4` or `1:1`. Defaults to `4:3` if not provided.
Video room configuration object with the following fields:
* `platform` (string, required): Room platform - must be "daily" or "livekit"
* `url` (string, required): Room URL for joining
* `token` (string, required): Authentication token for the room
Optional metadata object to attach to the session. Must be a valid JSON object. Defaults to `{}` if not provided.
### Response
Whether the session was created successfully
The created session object (See [Session schema](/api-reference/schemas/session)).
Current usage snapshot (minutes + concurrency) returned by usage enforcement.
Success message
### Important Notes
**Session Lifecycle**: Creating a session **automatically starts it**. The session is created with `started_at` set to the current timestamp and a server is immediately allocated to run the avatar. You do not need to call a separate "Start Session" endpoint.
**Video Room Integration**: You must provide your own video room (Daily or LiveKit) configuration in the `room` parameter. The API will use this configuration to connect the avatar server to your video room.
**Metadata Validation**: The `metadata` field must be a valid JSON object (e.g., `{"key": "value"}`). Arrays, strings, numbers, booleans or null values will be rejected with a 400 error. If not provided, it defaults to an empty object `{}`.
**Use Avatar IDs for best performance**: When passing a URL or base64 image, the server runs the full upload-face pipeline (face detection + alignment + upload) before starting the session, which adds latency. For production use, call [POST /v1/avatars/upload-face](/api-reference/endpoints/upload-face) once to get an `avat_...` ID and pass that ID to all subsequent session requests.
**Avatar Face Requirement (URL / base64 inputs)**: The image must contain **exactly one human face** that is clearly visible and occupies a significant portion of the frame. Group photos, full-body shots, illustrations or images with no face will be rejected. Use a portrait or head-and-shoulders photo.
### Behavior
* Creates a new session using the provided avatar
* **Avatar ID (`avat_...`):** resolved directly to its stored image — no extra processing. Avatars tagged as `user_exclusive` are restricted to the account that uploaded them
* **URL or base64 (paid plans only):** the image is automatically processed through the [Upload Face](/api-reference/endpoints/upload-face) pipeline — face detection (exactly one prominent face required), face alignment, and upload to storage — before the session is created. Any validation error from this pipeline is returned as a session creation error. For best latency, call [Upload Face](/api-reference/endpoints/upload-face) separately in advance and pass the returned avatar ID here
* **Free plan:** only avatar IDs are accepted; URL and base64 inputs are rejected with a `403` error
* **Automatically starts the session** (sets `started_at` timestamp)
* Allocates a GPU-enabled server for avatar processing
* Calculates expiration time based on your subscription plan's session duration limits
* Returns `session_token` needed for internal server authentication
* Validates room platform is "daily" or "livekit"
* Defaults aspect ratio to "4:3" if not provided, validates it's one of: "4:3", "3:4" or "1:1"
* Metadata defaults to `{}` and must be a valid JSON object
* Session IDs are prefixed with `sess_`
* Enforces subscription usage limits (minutes remaining, concurrent session limits)
### Use Cases
* Create and start a session with your own Daily or LiveKit room
* Set up sessions with custom metadata for tracking
* Initialize avatar sessions programmatically
* Start avatar conversations with specific aspect ratios for different devices
```json 201 - Success theme={null}
{
"success": true,
"session": {
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "started",
"session_token": "session_token_xxxxxxxxxxxxx",
"started_at": "2024-01-15T10:30:00Z",
"ended_at": null,
"expiration": "2024-01-15T14:30:00Z",
"metadata": {
"user_name": "John Doe",
"session_purpose": "Customer Support"
}
},
"usage": {
"concurrency": {
"current": 1,
"max": 1,
"available": 0
},
"minutes": {
"total_remaining": 120
}
},
"message": "Session created successfully"
}
```
```json 400 - Missing Avatar theme={null}
{
"success": false,
"error": {
"message": "Avatar is required",
"suggestion": "Provide an avatar ID (avat_...), a URL (https://...), or a base64-encoded image string (data:image/...;base64,...)"
}
}
```
```json 400 - Invalid Avatar Format theme={null}
{
"success": false,
"error": {
"message": "Invalid avatar format",
"suggestion": "Provide an avatar ID (avat_...), a URL (https://...), or a base64-encoded image string (data:image/...;base64,...)"
}
}
```
```json 400 - Avatar Too Small theme={null}
{
"success": false,
"error": {
"message": "Avatar image is too small",
"suggestion": "Image must be at least 200x200 pixels"
}
}
```
```json 400 - No Face Detected theme={null}
{
"success": false,
"error": {
"message": "No face detected in the avatar image",
"suggestion": "Provide a clear photo with one visible face"
}
}
```
```json 400 - Multiple Faces Detected theme={null}
{
"success": false,
"error": {
"message": "2 faces detected in the avatar image",
"suggestion": "The avatar must contain exactly one person"
}
}
```
```json 400 - Face Too Small theme={null}
{
"success": false,
"error": {
"message": "The face in the avatar image is too small or not clearly visible",
"suggestion": "Use a closer photo where the face is clearly visible and takes up a significant portion of the image"
}
}
```
```json 400 - Avatar Too Large theme={null}
{
"success": false,
"error": {
"message": "Avatar image is too large",
"suggestion": "Image must be under 10 MB"
}
}
```
```json 400 - Avatar URL Not Accessible theme={null}
{
"success": false,
"error": {
"message": "Failed to fetch avatar image from the provided URL",
"suggestion": "Ensure the URL is publicly accessible and points to a valid image"
}
}
```
```json 400 - Avatar URL Private Address theme={null}
{
"success": false,
"error": {
"message": "Avatar URL points to a private or reserved address",
"suggestion": "Provide a publicly accessible URL"
}
}
```
```json 403 - Free Plan: Avatar ID Required theme={null}
{
"success": false,
"error": {
"message": "Providing an image URL or base64 requires a paid plan",
"suggestion": "Upload your image first via POST /v1/avatars/upload-face to get an avatar ID (avat_...), or upgrade your plan"
}
}
```
```json 403 - Avatar Access Denied theme={null}
{
"success": false,
"error": {
"message": "Avatar access denied",
"suggestion": "This avatar is exclusive and does not belong to your account"
}
}
```
```json 404 - Avatar ID Not Found theme={null}
{
"success": false,
"error": {
"message": "Avatar not found",
"suggestion": "The provided avatar ID does not match any avatar"
}
}
```
```json 400 - Invalid Aspect Ratio theme={null}
{
"success": false,
"error": {
"message": "Invalid aspect ratio",
"suggestion": "Aspect ratio must be one of: 4:3, 3:4, 1:1"
}
}
```
```json 400 - Invalid Room theme={null}
{
"success": false,
"error": {
"message": "Room must be a valid JSON object",
"suggestion": "Room should be an object like {\"platform\": \"daily\", \"url\": \"https://your-domain.daily.co/your-room-name\", \"token\": \"your-room-token\"}, not an array or primitive"
}
}
```
```json 400 - Invalid Room Platform theme={null}
{
"success": false,
"error": {
"message": "Invalid room platform",
"suggestion": "Room platform must be one of: daily, livekit"
}
}
```
```json 402 - Insufficient Minutes theme={null}
{
"success": false,
"error": {
"code": "INSUFFICIENT_MINUTES",
"message": "No minutes remaining. Please purchase additional minutes or upgrade your plan at https://app.agenthuman.com/settings/billing"
}
}
```
```json 429 - Concurrency Limit Exceeded theme={null}
{
"success": false,
"error": {
"code": "CONCURRENCY_LIMIT_EXCEEDED",
"message": "Maximum concurrent sessions (1) reached. You already have 1 session running. Please end an active session before starting a new one."
}
}
```
# Delete Avatar
Source: https://docs.agenthuman.com/api-reference/endpoints/delete-avatar
DELETE /v1/avatars/{id}
Permanently delete a user-uploaded avatar
Permanently deletes an avatar. Only succeeds for avatars that you uploaded yourself — you cannot delete avatars owned by other users.
### Path Parameters
The avatar ID to delete (format: `avat_`). You can find your avatars at [app.agenthuman.com/avatars](https://app.agenthuman.com/avatars) or via [Upload Face Avatar](/api-reference/endpoints/upload-face-avatar).
### Response
Whether the avatar was deleted successfully.
```json 200 - Success theme={null}
{
"success": true
}
```
```json 404 - Not Found theme={null}
{
"success": false,
"error": {
"message": "Avatar not found"
}
}
```
```json 403 - Forbidden theme={null}
{
"success": false,
"error": {
"message": "You do not have permission to delete this avatar"
}
}
```
```json 401 - Unauthorized theme={null}
{
"success": false,
"error": {
"message": "Unauthorized"
}
}
```
# End Session
Source: https://docs.agenthuman.com/api-reference/endpoints/end-session
POST /v1/sessions/{session_id}/end
Stop the avatar server and release resources
**What This Does:** Gracefully stops the avatar server, releases GPU resources and marks the session as ended. Call this when you're done with the conversation to avoid unnecessary charges.
### Path Parameters
The session ID to end
### Response
Whether the request was successful
The updated session object (See [Session schema](/api-reference/schemas/session)).
### Important Notes
* Only active sessions (status: `started`) can be ended
* Marks the session with an `ended_at` timestamp
* Session status transitions from `started` to `ended`
* Once ended, the session cannot be restarted
* If the session is already ended, the API returns `success: true`.
### Automatic Termination Handling
**Best Practice:** Always explicitly call this endpoint when you're done with a session to ensure immediate resource release and billing accuracy.
**Sessions are automatically terminated instantly when:**
* **Client leaves the video room** - When all participants leave the Daily/LiveKit room
* **Session timeout** - When the session reaches its expiration time based on plan limits
**Safety Mechanism:** If automatic termination fails for any reason, our system has a safety check that will detect and clean up orphaned sessions within **3 minutes**. This ensures sessions are always terminated even in edge cases where the instant triggers don't fire.
```json 200 - Success theme={null}
{
"success": true,
"session": {
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "ended",
"started_at": "2024-01-15T10:30:00Z",
"ended_at": "2024-01-15T10:35:00Z"
}
}
```
```json 200 - Already Ended theme={null}
{
"success": true,
"session": {
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "ended"
}
}
```
```json 400 - Not Started theme={null}
{
"success": false,
"error": {
"message": "Cannot end a session that was never started",
"suggestion": "Only sessions with status \"started\" can be ended"
}
}
```
```json 404 - Not Found theme={null}
{
"success": false,
"error": {
"message": "Session not found",
"suggestion": "Verify the session ID is correct"
}
}
```
# Get Session
Source: https://docs.agenthuman.com/api-reference/endpoints/get-session
GET /v1/sessions/{session_id}
Retrieve a specific session by ID
### Path Parameters
The ID of the session to retrieve
### Response
Whether the request was successful
The session object (See [Session schema](/api-reference/schemas/session)).
```json 200 - Success theme={null}
{
"success": true,
"session": {
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "ended",
"started_at": "2024-01-15T12:00:00Z",
"ended_at": "2024-01-15T12:05:30Z",
"expiration": "2024-01-15T14:35:00Z",
"duration": 330,
"metadata": {
"user_name": "John Doe",
"session_purpose": "Customer Support",
"custom_field": "any value"
}
}
}
```
This endpoint does **not** return `session_token` or `room`. Save the `session_token` from the **Create Session** response if you need it for streaming. The `room` configuration is stored but not exposed in API responses.
# Get Usage Summary
Source: https://docs.agenthuman.com/api-reference/endpoints/get-usage-summary
GET /v1/usage
Get current usage summary including minutes, active sessions and billing period information
## Overview
Returns a comprehensive usage summary for the authenticated user, including:
* Current billing period details
* Included and purchased minutes (total, used, remaining)
* Currently active sessions with real-time duration
* Concurrency limits and usage
* Avatar creation usage for the current billing cycle
* Plan information
You can manage your plan, view billing details and purchase additional minutes at [app.agenthuman.com/settings/billing](https://app.agenthuman.com/settings/billing).
## Authentication
Requires authentication via API key (X-API-Key header) or JWT token (session cookie).
### Response
Whether the request was successful
The usage object (See [Usage schema](/api-reference/schemas/usage) for complete field descriptions).
```json 200 - Success (With Active Sessions) theme={null}
{
"success": true,
"usage": {
"period": {
"start": "2024-01-01T00:00:00.000Z",
"end": "2024-01-31T23:59:59.000Z"
},
"included_minutes": {
"total": 120,
"used": 75,
"remaining": 45
},
"purchased_minutes": {
"total": 100,
"remaining": 100
},
"total_remaining": 145,
"active_sessions": [
{
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "active",
"started_at": "2024-01-15T10:30:00.000Z",
"ended_at": null,
"expiration": "2024-01-15T14:30:00.000Z",
"aspect_ratio": "4:3",
"duration": 450,
"metadata": {
"user_name": "John Doe"
}
}
],
"active_minutes": 8,
"concurrency": {
"current": 1,
"max": 20,
"available": 19
},
"avatars": {
"used": 12,
"limit": 180,
"remaining": 168
},
"plan": {
"key": "pro",
"name": "Pro",
"max_session_duration": 180,
"will_renew": true
}
}
}
```
```json 200 - Success (No Active Sessions) theme={null}
{
"success": true,
"usage": {
"period": {
"start": "2024-01-01T00:00:00.000Z",
"end": "2024-01-31T23:59:59.000Z"
},
"included_minutes": {
"total": 30,
"used": 15,
"remaining": 15
},
"purchased_minutes": {
"total": 0,
"remaining": 0
},
"total_remaining": 15,
"active_sessions": [],
"active_minutes": 0,
"concurrency": {
"current": 0,
"max": 1,
"available": 1
},
"avatars": {
"used": 0,
"limit": 0,
"remaining": 0
},
"plan": {
"key": "free",
"name": "Free",
"max_session_duration": 5,
"will_renew": false
}
}
}
```
## Usage Notes
### Billing Period
* **Paid plans**: Uses Stripe subscription's `current_period_start` and `current_period_end`
* **Free plans**: Uses calendar month (1st to last day of month)
### Minutes Calculation
1. **Included minutes** are used first
2. **Purchased minutes** are consumed after included minutes are exhausted
3. **Active minutes** are calculated in real-time and rounded up per session
### Active Sessions
* Each session in `active_sessions` is a standard [Session object](/api-reference/schemas/session)
* Sessions have `status: "active"` and `ended_at: null`
* The `duration` field shows real-time elapsed seconds since session started
### Concurrency
**Concurrent sessions** are sessions running at the same time. The `concurrency` object shows how many sessions you currently have active (`current`), your plan's maximum allowed (`max`) and how many more you can start (`available`).
For example, if you have an Explorer plan with `max: 5`, you can run 5 avatar sessions simultaneously. If you currently have 1 session running (`current: 1`), you have 4 available slots (`available: 4`) to start more sessions.
Plans have different concurrent session limits:
* **Free**: 1 concurrent session
* **Explorer**: 5 concurrent sessions
* **Growth**: 10 concurrent sessions
* **Pro**: 20 concurrent sessions
### Avatar Creation
The `avatars` object tracks custom avatar generation usage for the current billing cycle. `limit: 0` means the plan does not include avatar creation. `limit: null` means unlimited. Resets each billing cycle alongside included minutes.
| Plan | Avatars per cycle |
| ---------- | ----------------- |
| Free | Not available (0) |
| Explorer | 20 |
| Growth | 60 |
| Pro | 180 |
| Enterprise | Unlimited |
# List Avatars
Source: https://docs.agenthuman.com/api-reference/endpoints/list-avatars
GET /v1/avatars
Get public avatars and your own uploaded avatars in one request
Returns the full library of pre-built public avatars alongside any avatars you have uploaded yourself.
Use `image_url` from either list as the `avatar_image_url` when creating sessions or agents or pass the `avatar_id` to [Preview Avatar](/api-reference/endpoints/preview-avatar).
### Response
Whether the request was successful.
Array of [Avatar](/api-reference/schemas/avatar) objects available to all users.
Array of [Avatar](/api-reference/schemas/avatar) objects you have uploaded or generated.
```json 200 - Success theme={null}
{
"success": true,
"public_avatars": [
{
"avatar_id": "PUBLIC/Avatars/sara-office",
"tags": ["female", "professional"],
"preview_url": "https://cdn.agenthuman.com/avatars/sara-office/preview",
"image_url": "https://cdn.agenthuman.com/avatars/sara-office/image"
}
],
"my_avatars": [
{
"avatar_id": "avat_01J8ZK4R2N5M6P7Q8W4T",
"tags": ["user_exclusive", "user_42"],
"preview_url": "https://cdn.agenthuman.com/avatars/avat_01J8ZK4R2N5M6P7Q8W4T/preview",
"image_url": "https://cdn.agenthuman.com/avatars/avat_01J8ZK4R2N5M6P7Q8W4T/image"
}
]
}
```
```json 401 - Unauthorized theme={null}
{
"success": false,
"error": {
"message": "Unauthorized"
}
}
```
# List Sessions
Source: https://docs.agenthuman.com/api-reference/endpoints/list-sessions
GET /v1/sessions
Get all sessions for the authenticated user with optional filtering
### Query Parameters
Filter sessions by status. Options:
* `all` - Return all sessions (default)
* `started` - Return only active/started sessions
* `ended` - Return only ended sessions (includes billing data)
Note: Invalid status values are treated as `all` and return all sessions.
### Response
Whether the request was successful
Array of session objects (See [Session schema](/api-reference/schemas/session)). Ended sessions include billing information.
```json 200 - Success theme={null}
{
"success": true,
"sessions": [
{
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "ended",
"started_at": "2024-01-15T12:00:00Z",
"ended_at": "2024-01-15T12:05:30Z",
"expiration": "2024-01-15T14:35:00Z",
"aspect_ratio": "4:3",
"duration": 330,
"metadata": {
"user_name": "John Doe"
},
"billing": {
"minutes_consumed": 6,
"minutes_source": "plan",
"billing_status": "free",
"minutes_billed": 0
}
},
{
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W5U",
"status": "started",
"started_at": "2024-01-15T14:00:00Z",
"ended_at": null,
"expiration": "2024-01-15T18:00:00Z",
"aspect_ratio": "4:3",
"duration": 90,
"metadata": {}
}
]
}
```
For active sessions (`status = started`), `duration` is calculated at request time (seconds since `started_at`).
Ended sessions include a `billing` object with information about minutes consumed and billing status. Active/created sessions do not include billing information.
# Initiate Room
Source: https://docs.agenthuman.com/api-reference/endpoints/openclaw-initiate
POST /v1/openclaw/initiate
Create a LiveKit room for an avatar session and receive a client join token
**What This Does:** Creates a LiveKit room and returns the WebSocket URL and a JWT token your client can use to join immediately. The avatar agent connects automatically when the client joins.
### Body
All fields are optional. When omitted, server-side defaults are used.
Avatar ID to use for the session (e.g. `avat_01KMZHXFPBVCXA5ATK85HCP8G1`). Must be an `avat_...` ID returned by the [Upload Face](/api-reference/endpoints/upload-face-avatar) endpoint.
Video aspect ratio. Must be one of: `1:1`, `4:3`, `3:4`. Defaults to `1:1`.
ElevenLabs voice ID to use for TTS. Defaults to the server-configured voice.
Whether to enable real-time transcription of user speech. Defaults to `true`.
### Response
`true` when the room was created successfully.
Room connection details.
Unique room name (e.g. `ah-01JR...`).
LiveKit WebSocket URL. Pass this to the LiveKit client SDK.
Signed JWT the client uses to join the room. Pass directly to `new Room().connect(url, token)`.
### How It Works
1. A LiveKit room is created and configured with the options you provide.
2. A client join token is generated.
3. When the client connects using the returned `url` + `token`, the avatar agent starts automatically.
**No separate session call needed.** Unlike the `/v1/sessions` endpoint (which requires you to bring your own LiveKit room), this endpoint handles room creation for you. The avatar agent starts automatically on client join.
### Client Integration
```javascript theme={null}
const res = await fetch('https://api.agenthuman.com/v1/openclaw/initiate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY',
},
body: JSON.stringify({
avatar: 'avat_01KMZHXFPBVCXA5ATK85HCP8G1',
aspect_ratio: '1:1',
voice_id: 'hpp4J3VqNfWAUOO0d1Us',
}),
});
const { room } = await res.json();
// Connect with the LiveKit client SDK
import { Room } from 'livekit-client';
const livekitRoom = new Room();
await livekitRoom.connect(room.url, room.token);
```
```json 201 - Success theme={null}
{
"success": true,
"room": {
"name": "ah-01JR4XKPQM7T2N3V5W8Y6Z9B",
"url": "wss://your-livekit-host.livekit.cloud",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
```
```json 401 - Unauthorized theme={null}
{
"success": false,
"error": {
"message": "Unauthorized"
}
}
```
```json 500 - Server Configuration Error theme={null}
{
"success": false,
"error": {
"message": "LiveKit is not configured on this server"
}
}
```
# Preview Avatar
Source: https://docs.agenthuman.com/api-reference/endpoints/preview-avatar
POST /v1/avatars/preview
Generate a short-lived preview link to see an avatar in a live session
**What This Does:** Creates a temporary agent and a one-time preview link that expires in **5 minutes**. Opening the returned URL launches a live video session using the specified avatar, so you can see exactly how it will look and behave before using it in production.
### Body
The avatar ID to preview (format: `avat_` or a pre-built avatar ID such as `PUBLIC/Avatars/sara-office`). Returned by [Upload Face Avatar](/api-reference/endpoints/upload-face-avatar) or found via [List Avatars](/api-reference/endpoints/list-avatars).
### Response
Whether the preview link was created successfully.
A one-time preview URL (expires in 5 minutes, single-use). Open this in a browser to start a live video session with the avatar.
```json 201 - Success theme={null}
{
"success": true,
"url": "https://links.agenthuman.com/lnk_01J8ZK4R2N5M6P7Q8W4T"
}
```
```json 400 - Missing Avatar ID theme={null}
{
"success": false,
"error": {
"message": "avatar_id is required"
}
}
```
```json 401 - Unauthorized theme={null}
{
"success": false,
"error": {
"message": "Unauthorized"
}
}
```
# Upload Face Avatar
Source: https://docs.agenthuman.com/api-reference/endpoints/upload-face-avatar
POST /v1/avatars/upload-face
Upload a portrait photo to create a face-aligned avatar image
**What This Does:** Validates that the image contains exactly one prominent face, automatically detects the image orientation (landscape, portrait, or square), aligns the face to the appropriate avatar profile, and stores the result. Returns a preview URL, a full-resolution image URL, and an avatar ID for use in sessions.
### Input Methods
Supply the image using **exactly one** of the following options:
| Method | Content-Type | Field |
| ------------- | ------------------------------------------- | -------------- |
| File upload | `multipart/form-data` | `file` |
| Image URL | `application/json` or `multipart/form-data` | `image_url` |
| Base64 string | `application/json` or `multipart/form-data` | `image_base64` |
### Body
Portrait photo to process. Accepted formats: `jpeg`, `jpg`, `png`, `gif`, `webp`. Maximum size: **10 MB**.
The image must contain exactly one clearly visible, prominent face.
Send as `multipart/form-data`.
Publicly accessible URL of a portrait photo. The server fetches the image directly.
Accepted formats: `jpeg`, `jpg`, `png`, `gif`, `webp`. Maximum size: **10 MB**.
Can be sent as JSON (`application/json`) or as a text field in `multipart/form-data`.
Base64-encoded portrait photo. A data URI prefix (e.g. `data:image/jpeg;base64,`) is accepted but not required.
Maximum decoded size: **10 MB**.
Can be sent as JSON (`application/json`) or as a text field in `multipart/form-data`.
### Response
Whether the upload and face alignment succeeded.
Signed URL of the aligned avatar at 500×500 px, suitable for display in the UI.
Signed URL of the full-resolution aligned avatar (capped at 1200 px wide).
The avatar ID (format: `avat_`). Use this as the `avatar_id` when calling [Preview Avatar](/api-reference/endpoints/preview-avatar) or as the `avatar_image_url` in session and agent payloads.
```json 201 - Success theme={null}
{
"success": true,
"preview_url": "https://cdn.agenthuman.com/avatars/avat_01KMZHXFPBVCXA5ATK85HCP8G1/preview",
"image_url": "https://cdn.agenthuman.com/avatars/avat_01KMZHXFPBVCXA5ATK85HCP8G1/image",
"avatar_id": "avat_01KMZHXFPBVCXA5ATK85HCP8G1"
}
```
```json 400 - No Image Provided theme={null}
{
"success": false,
"error": {
"message": "No image provided",
"suggestion": "Supply an image via: file upload (\"file\" field in multipart/form-data), URL (\"image_url\"), or base64 string (\"image_base64\")"
}
}
```
```json 400 - URL Fetch Failed theme={null}
{
"success": false,
"error": {
"message": "Failed to fetch image from URL (HTTP 403)",
"suggestion": "Check that the URL is publicly accessible"
}
}
```
```json 400 - No Face Detected theme={null}
{
"success": false,
"error": {
"message": "No face detected in the image",
"suggestion": "Upload a clear portrait photo with one visible face"
}
}
```
```json 400 - Multiple Faces theme={null}
{
"success": false,
"error": {
"message": "Multiple faces detected",
"suggestion": "Upload a photo with only one person"
}
}
```
```json 401 - Unauthorized theme={null}
{
"success": false,
"error": {
"message": "Unauthorized"
}
}
```
# Introduction
Source: https://docs.agenthuman.com/api-reference/introduction
Getting started with the Agent Human API
## Welcome to Agent Human API
Transform your applications with **AI-powered video avatars** that respond in real-time. Agent Human provides a simple REST API to generate lifelike talking head videos from audio, perfect for customer service, virtual assistants, education and more.
**Architecture:** We handle the complexity of real-time video generation using GPU-accelerated processing, while your chosen video platform (Daily or LiveKit) manages WebRTC streaming for you. Just send audio commands and receive professional avatar video.
## Base URL
All API requests should be made to:
```
https://api.agenthuman.com
```
## What You Can Build
The Agent Human API enables you to:
* **Create Interactive Sessions** - Start WebRTC video sessions with AI avatars using Daily or LiveKit infrastructure
* **Real-time Video Generation** - Send audio and receive synchronized talking head video
* **Track Analytics** - Monitor session duration, status and usage metrics
## Quick Start
Get started in minutes:
### Step 1: Get Your API Key
1. Log in to your [Agent Human Dashboard](https://app.agenthuman.com)
2. Navigate to [**Settings → API Keys**](https://app.agenthuman.com/settings/apikeys)
3. Click **"New API Key"**
4. Name your key (e.g., "Development" or "Production")
5. Copy and save the key securely - it won't be shown again!
API keys are shown only once. Store them securely and never commit to version control.
Store your API key as an environment variable:
**Linux/macOS:**
```bash theme={null}
export AGENTHUMAN_API_KEY="ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```
**Windows (PowerShell):**
```powershell theme={null}
$env:AGENTHUMAN_API_KEY="ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```
**Or in a `.env` file:**
```env theme={null}
AGENTHUMAN_API_KEY=ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
Using environment variables keeps your API keys secure and out of your source code.
### Step 2: Create a Session
```javascript JavaScript theme={null}
// Create a new video session
// Note: You must provide your own Daily.co or LiveKit room configuration
const sessionResponse = await fetch('https://api.agenthuman.com/v1/sessions', {
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
avatar: 'https://example.com/avatar.jpg', // URL or base64 image (data:image/...;base64,...)
aspect_ratio: '4:3', // Options: '4:3', '3:4', '1:1'
room: {
platform: 'daily', // or 'livekit'
url: 'https://your-domain.daily.co/your-room',
token: 'your-daily-token'
},
metadata: {
user_name: 'John Doe',
purpose: 'Demo session'
}
})
});
const { success, session, message } = await sessionResponse.json();
console.log(message); // "Session created successfully"
console.log(`Session ID: ${session.session_id}`);
console.log(`Status: ${session.status}`); // "started"
console.log(`Access Token: ${session.session_token}`);
// Session is now started and avatar server is allocated
```
```python Python theme={null}
# Create a new video session
# Note: You must provide your own Daily.co or LiveKit room configuration
response = requests.post(
'https://api.agenthuman.com/v1/sessions',
headers={
'x-api-key': API_KEY,
'Content-Type': 'application/json'
},
json={
'avatar': 'https://example.com/avatar.jpg', # URL or base64 image (data:image/...;base64,...)
'aspect_ratio': '4:3', # Options: '4:3', '3:4', '1:1'
'room': {
'platform': 'daily', # or 'livekit'
'url': 'https://your-domain.daily.co/your-room',
'token': 'your-daily-token'
},
'metadata': {
'user_name': 'John Doe',
'purpose': 'Demo session'
}
}
)
data = response.json()
session = data['session']
print(data['message']) # "Session created successfully"
print(f"Session ID: {session['session_id']}")
print(f"Status: {session['status']}") # "started"
print(f"Access Token: {session['session_token']}")
# Session is now started and avatar server is allocated
```
```bash cURL theme={null}
# Create a session with your avatar ID
curl -X POST https://api.agenthuman.com/v1/sessions \
-H "x-api-key: $AGENTHUMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"avatar": "https://example.com/avatar.jpg",
"aspect_ratio": "4:3",
"room": {
"platform": "daily",
"url": "https://your-domain.daily.co/your-room",
"token": "your-daily-token"
},
"metadata": {
"user_name": "John Doe",
"purpose": "Demo session"
}
}'
```
Sessions are **automatically started** when created. The session is created with `started` status and a GPU-enabled server is immediately allocated to run the avatar.
**Video Room Setup**: You must provide your own Daily.co or LiveKit room configuration in the `room` parameter. The API will connect the avatar server to your video room.
## Core Concepts
Video conversations with avatars. Lifecycle: **Started** (at creation) → **Ended**. You provide your own Daily or LiveKit room for WebRTC video streaming.
Real-time audio/video communication using Daily or LiveKit infrastructure for low-latency avatar video delivery.
### Response Format
Most `/v1/*` API responses include a `success` field. Authentication and some middleware responses may instead return an `error` string.
### Success Responses
```json List Response theme={null}
{
"success": true,
"sessions": [
{
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "started",
"created_at": "2024-01-15T10:30:00Z"
}
]
}
```
```json Create/Update Response theme={null}
{
"success": true,
"session": {
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "created",
"session_token": "session_token_xxxxx",
"created_at": "2024-01-15T10:30:00Z"
},
"message": "Session created successfully"
}
```
### Error Responses
All errors include helpful messages and optional suggestions:
```json theme={null}
{
"success": false,
"error": {
"message": "Invalid avatar format",
"suggestion": "Provide a URL (https://...) or a base64-encoded image string (data:image/...;base64,...)"
}
}
```
## Resource IDs
All resources use prefixed IDs for easy identification:
| Resource | Prefix | Example |
| -------- | ----------------------- | ------------------------------- |
| Sessions | `sess_` | `sess_01H3Z8G9YR3K2N5M6P7Q8W4T` |
| API Keys | `ah_live_` / `ah_test_` | `ah_live_1234567890abcdef...` |
Resource IDs are **immutable** and **globally unique**. Use them for reliable references across API calls.
## HTTP Status Codes
| Status Code | Description |
| ----------- | ------------------------------------------------- |
| `200` | Success - Request completed successfully |
| `201` | Created - Resource created successfully |
| `400` | Bad Request - Invalid request parameters |
| `401` | Unauthorized - Invalid or missing API key |
| `403` | Forbidden - Insufficient permissions |
| `404` | Not Found - Resource does not exist |
| `429` | Too Many Requests - Rate limit exceeded |
| `500` | Internal Server Error - Server error |
| `503` | Service Unavailable - Service temporarily offline |
## Rate Limiting
Agent Human implements rate limiting to ensure fair usage and system stability. Limits apply per API key.
### Rate Limits by Endpoint Type
| Endpoint Type | Limit | Window | HTTP Status |
| ------------------------------------ | ------------ | -------- | ----------- |
| Read Operations (GET) | 300 requests | 1 minute | 429 |
| Write Operations (POST, PUT, DELETE) | 60 requests | 1 minute | 429 |
| Session Start | 150 requests | 1 minute | 429 |
### Rate Limit Headers
Every API response includes rate limit information in the headers:
```http theme={null}
RateLimit-Limit: 300
RateLimit-Remaining: 285
RateLimit-Reset: 12
```
| Header | Description |
| --------------------- | ---------------------------------------------- |
| `RateLimit-Limit` | Maximum requests allowed in the current window |
| `RateLimit-Remaining` | Requests remaining in the current window |
| `RateLimit-Reset` | Seconds until the current window resets |
### When Rate Limited
**Response (429 Too Many Requests):**
You may receive a `429` response with a simple message body, for example:
```text theme={null}
Too many requests, please slow down.
```
**Headers included:**
```http theme={null}
Retry-After: 60
RateLimit-Reset: 60
```
### Best Practices
Track `RateLimit-Remaining` in your application and slow down requests when approaching the limit.
```javascript theme={null}
const response = await fetch('https://api.agenthuman.com/v1/sessions', {
headers: { 'x-api-key': API_KEY }
});
const remaining = response.headers.get('RateLimit-Remaining');
if (parseInt(remaining) < 10) {
console.warn('Approaching rate limit, consider slowing down');
}
```
When you receive a 429 response, wait before retrying with exponentially increasing delays.
```javascript theme={null}
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
// Exponential backoff: 1s, 2s, 4s, etc.
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
throw new Error('Max retries exceeded');
}
```
Instead of making multiple individual requests, batch operations where the API supports it.
* Use `GET /v1/sessions` to fetch all sessions at once instead of individual requests
* Cache session data locally to reduce API calls
Cache API responses that don't change frequently (session details).
```javascript theme={null}
const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
async function getCachedSession(sessionId) {
const cached = cache.get(sessionId);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
const response = await fetch(`https://api.agenthuman.com/v1/sessions/${sessionId}`, {
headers: { 'x-api-key': API_KEY }
});
const data = await response.json();
cache.set(sessionId, { data, timestamp: Date.now() });
return data;
}
```
### Rate Limit Scope
Rate limits are applied **per API key**, meaning:
* Different API keys have independent rate limits
* Test and production keys have separate quotas
* Team members with different keys don't share limits
**Session Limits:** In addition to API rate limits, your subscription plan has limits on:
* **Concurrent sessions**: How many avatar sessions you can run simultaneously (e.g., Free: 2, Explorer: 5, Growth: 10, Pro: 20)
* **Total session time**: Monthly minutes included in your plan
These limits are separate from API request rate limits. See the [Usage endpoint](/api-reference/endpoints/get-usage-summary) to check your current usage.
## Working with the API
### Prerequisites
Before you begin, ensure you have:
* An Agent Human account ([Sign up here](https://app.agenthuman.com))
* Your API key from the dashboard
* An HTTP client library for your language
### SDK & HTTP Clients
Agent Human works with any standard HTTP client:
**Recommended libraries:**
* `fetch` (built-in in Node.js 18+)
* `axios` - Full-featured HTTP client
* `node-fetch` - Node.js implementation of fetch
```bash theme={null}
npm install axios dotenv
```
**Recommended libraries:**
* `requests` - Simple and elegant HTTP library
* `httpx` - Modern async-capable HTTP client
* `aiohttp` - Async HTTP client/server
```bash theme={null}
pip install requests python-dotenv
```
* **PHP**: Guzzle, cURL
* **Ruby**: Net::HTTP, HTTParty, Faraday
* **Go**: net/http, resty
* **Java**: OkHttp, Apache HttpClient
* **.NET**: HttpClient, RestSharp
## Best Practices
Store API keys in environment variables, never in source code or version control
Check `success` field and handle errors gracefully with retry logic
Monitor rate limit headers and implement exponential backoff
Always use HTTPS endpoints for secure communication
## Common Workflows
A typical session flow from creation to completion:
```javascript theme={null}
const headers = { 'x-api-key': API_KEY };
// 1. Create and start a session (happens in one step)
const createRes = await fetch('https://api.agenthuman.com/v1/sessions', {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
avatar: 'https://example.com/avatar.jpg', // URL or base64 image
aspect_ratio: '4:3',
room: {
platform: 'daily',
url: 'https://your-domain.daily.co/your-room',
token: 'your-daily-token'
},
metadata: { user_id: 'user_123' }
})
});
const { session } = await createRes.json();
console.log(`Session started: ${session.session_id}`);
// 2. Connect to your video room (Daily or LiveKit) for WebRTC video
// Use session.room.url and session.room.token
// 3. End the session when done
const endRes = await fetch(
`https://api.agenthuman.com/v1/sessions/${session.session_id}/end`,
{
method: 'POST',
headers: { 'x-api-key': API_KEY }
}
);
const { session: endedSession } = await endRes.json();
console.log(`Session duration: ${endedSession.duration} seconds`);
```
Query sessions with filters and track their status:
```python theme={null}
import requests
API_KEY = os.getenv('AGENTHUMAN_API_KEY')
headers = {'x-api-key': API_KEY}
# Get all active sessions
active_res = requests.get(
'https://api.agenthuman.com/v1/sessions?status=started',
headers=headers
)
active_sessions = active_res.json()['sessions']
print(f"Active sessions: {len(active_sessions)}")
# Get detailed info for a specific session
session_res = requests.get(
f'https://api.agenthuman.com/v1/sessions/{session_id}',
headers=headers
)
session_details = session_res.json()['session']
print(f"Session status: {session_details['status']}")
```
Always check the `success` field and handle errors appropriately:
```javascript theme={null}
async function createSessionSafely(avatar) {
try {
const response = await fetch('https://api.agenthuman.com/v1/sessions', {
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ avatar })
});
const data = await response.json();
// Most /v1 endpoints return { success: boolean, ... }, but auth/middleware
// failures may return { error: string }.
if (!response.ok || data.success === false) {
const errorMessage =
typeof data.error === 'string'
? data.error
: data.error?.message || data.error || 'Request failed';
console.error('API Error:', errorMessage);
const suggestion = typeof data.error === 'object' ? data.error?.suggestion : undefined;
if (suggestion) console.log('Suggestion:', suggestion);
return null;
}
return data.session;
} catch (error) {
// Handle network error
console.error('Network error:', error.message);
return null;
}
}
```
## Next Steps
Learn how to authenticate with API keys
Create and start a session with your video room
Send audio to create talking avatars
## Need Help?
**[support@agenthuman.com](mailto:support@agenthuman.com)**
Our team typically responds within 24 hours
**Stuck?** Check our detailed [endpoint documentation](/api-reference/endpoints/create-session) or reach out to our support team. We're here to help!
# Avatar Object
Source: https://docs.agenthuman.com/api-reference/schemas/avatar
Schema definition for Avatar objects returned by the API
## Overview
An **Avatar** represents a pre-built or user-uploaded avatar image available for use in sessions and agents. Each avatar exposes both a square preview URL and a full-resolution image URL.
## Fields
Unique identifier for the avatar (e.g. `PUBLIC/Avatars/sara-office`). Pass this as `avatar_id` to [Preview Avatar](/api-reference/endpoints/preview-avatar) or as `avatar_image_url` in sessions and agents.
Array of tags associated with the avatar.
Signed URL at 500×500 px, suitable for display in a picker UI.
Signed URL of the full-resolution avatar (capped at 1200 px wide). Use this as `avatar_image_url` in sessions or agents.
## Example
```json theme={null}
{
"avatar_id": "PUBLIC/Avatars/sara-office",
"tags": ["female", "professional"],
"preview_url": "https://cdn.agenthuman.com/avatars/sara-office/preview",
"image_url": "https://cdn.agenthuman.com/avatars/sara-office/image"
}
```
# Error Response Object
Source: https://docs.agenthuman.com/api-reference/schemas/error
Schema definition for Error Response objects returned by the API
## Overview
An **Error Response** is returned when an API request fails. All endpoints return a standardized JSON error format.
**What Error Responses Include:**
* Human-readable error message
* Optional suggestions for resolving the issue
* Optional machine-readable error codes (for usage enforcement and billing errors)
* Optional structured details (field validation, usage breakdowns)
* HTTP status code indicating the error type
## Fields
Always `false` for error responses
Error details object
Human-readable error message describing what went wrong
Optional suggestion for resolving the issue (not present in all errors)
Optional machine-readable error code. Only present for usage enforcement and billing errors.
**Possible codes:**
* `MISSING_USER_ID` - User ID required for operation
* `CONCURRENCY_LIMIT_EXCEEDED` - Too many concurrent sessions
* `INSUFFICIENT_MINUTES` - No minutes remaining in account
* `USAGE_CHECK_FAILED` - Failed to verify usage limits
* `NO_SUBSCRIPTION` - No active subscription found
Optional structured details. Format varies by error type:
* **Usage/Billing errors**: Object with usage breakdown (`minutesRemaining`, `minutesUsed`, etc.)
* **Field validation errors**: Array of objects with `field` and `message` properties
## Error Response Formats
The API uses a consistent error format across all endpoints:
### 1. Standard Error
Most errors use this format:
```json theme={null}
{
"success": false,
"error": {
"message": "Something went wrong",
"suggestion": "Optional suggestion"
}
}
```
### 2. Usage Enforcement Errors
Usage and billing-related errors include a machine-readable `code`:
```json theme={null}
{
"success": false,
"error": {
"code": "INSUFFICIENT_MINUTES",
"message": "No minutes remaining. Please purchase additional minutes or upgrade your plan at https://app.agenthuman.com/settings/billing",
"details": {
"minutesRemaining": 0,
"minutesUsed": 100
}
}
}
```
**Error codes used:**
* `MISSING_USER_ID` - User ID required for operation
* `CONCURRENCY_LIMIT_EXCEEDED` - Too many concurrent sessions
* `INSUFFICIENT_MINUTES` - No minutes remaining in account
* `USAGE_CHECK_FAILED` - Failed to verify usage limits
* `NO_SUBSCRIPTION` - No active subscription found
### 3. Field Validation Errors
When request validation fails, the API returns detailed field-level errors:
```json theme={null}
{
"success": false,
"error": {
"message": "Validation failed",
"details": [
{ "field": "email", "message": "email must be a valid email" },
{ "field": "password", "message": "password must be at least 8 characters" }
]
}
}
```
### 4. Unexpected Server Errors
When an unexpected error occurs, the global error handler returns:
```json theme={null}
{
"success": false,
"error": {
"message": "Internal server error"
}
}
```
## Field Reference
All error responses follow the structure defined in the Fields section above.
## Examples
### Bad Request (400)
```json theme={null}
{
"success": false,
"error": {
"message": "Avatar is required",
"suggestion": "Provide a URL (https://...) or a base64-encoded image string (data:image/...;base64,...)"
}
}
```
### Not Found (404)
```json theme={null}
{
"success": false,
"error": {
"message": "Session not found"
}
}
```
### Access Denied (403)
```json theme={null}
{
"success": false,
"error": {
"message": "Access denied to this avatar",
"suggestion": "You do not have permission to create sessions with this avatar"
}
}
```
### Authentication Error (401)
```json theme={null}
{
"success": false,
"error": {
"message": "API key required"
}
}
```
### Validation Error (400) - Business Logic
```json theme={null}
{
"success": false,
"error": {
"message": "Metadata must be a valid JSON object",
"suggestion": "Metadata should be an object like {\"key\": \"value\"}, not an array or primitive"
}
}
```
### Validation Error (400) - Field-Level
When request validation fails (e.g., using Joi schemas), the API returns detailed field-level errors:
```json theme={null}
{
"success": false,
"error": {
"message": "Validation failed",
"details": [
{ "field": "email", "message": "email is required" },
{ "field": "display_name", "message": "display_name must be at least 1 character" }
]
}
}
```
### Usage Enforcement Error (402 / 429)
When usage limits are exceeded:
```json theme={null}
{
"success": false,
"error": {
"code": "INSUFFICIENT_MINUTES",
"message": "No minutes remaining. Please purchase additional minutes or upgrade your plan at https://app.agenthuman.com/settings/billing",
"details": {
"minutesRemaining": 0,
"minutesUsed": 120,
"monthlyAllowance": 100
}
}
}
```
Concurrency limit exceeded:
```json theme={null}
{
"success": false,
"error": {
"code": "CONCURRENCY_LIMIT_EXCEEDED",
"message": "You have reached your concurrent session limit.",
"details": {
"currentSessions": 5,
"limit": 5
}
}
}
```
## HTTP Status Codes
| Status Code | Description | When It Occurs |
| ----------- | --------------------- | -------------------------------------------------------------------------------------------------- |
| `400` | Bad Request | Missing required fields, invalid data format, field validation failures, business logic violations |
| `401` | Unauthorized | Missing or invalid API key, expired JWT token |
| `402` | Payment Required | Insufficient minutes remaining in account |
| `403` | Forbidden | User doesn't have permission to access the resource, invalid token |
| `404` | Not Found | Session, avatar or other resource not found |
| `409` | Conflict | OAuth account already linked to another user |
| `429` | Too Many Requests | Rate limit exceeded |
| `500` | Internal Server Error | Unexpected server-side error |
| `503` | Service Unavailable | Service temporarily unavailable, external service not ready |
## Rate Limiting
When rate limited (429 status code), you may receive headers indicating:
* `RateLimit-Limit`: Maximum number of requests allowed
* `RateLimit-Remaining`: Requests remaining in current window
* `RateLimit-Reset`: Seconds until the window resets
* `Retry-After`: Seconds to wait before making another request
## Error Handling Best Practices
### Always Check Response Status
Check both the HTTP status code and the `success` field:
```javascript theme={null}
const response = await fetch('https://api.agenthuman.com/v1/sessions/sess_123', {
headers: { 'x-api-key': 'your-api-key' }
});
const data = await response.json();
if (!response.ok || !data.success) {
// All errors now use standardized format
const errorMessage = data.error?.message || 'Request failed';
console.error('Error:', errorMessage);
// Check for suggestions
if (data.error?.suggestion) {
console.log('Suggestion:', data.error.suggestion);
}
// Check for error code (usage/billing errors)
if (data.error?.code) {
console.log('Error code:', data.error.code);
}
// Check for field-level validation errors
if (data.error?.details && Array.isArray(data.error.details)) {
console.log('Field errors:');
data.error.details.forEach(err => {
console.log(` - ${err.field}: ${err.message}`);
});
}
}
```
### Implement Retry Logic for Rate Limits
```javascript theme={null}
async function apiCall(url, options, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
// Exponential backoff
const delay = 1000 * Math.pow(2, i);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
```
### Don't Retry 4xx Errors
```javascript theme={null}
// Only retry on network errors or 5xx server errors
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
// Client error - don't retry, fix the request
throw new Error(data.error);
}
```
## Notes
* **All errors** use the standardized format with `success: false` and `error` as an object
* **Error codes** are only used for usage enforcement and billing errors
* **Suggestions** are optional - not all errors include them
* **Error messages** are human-readable and safe to display to users
* **HTTP status codes** follow standard REST conventions
# Session Object
Source: https://docs.agenthuman.com/api-reference/schemas/session
Schema definition for Session objects returned by the API
## Overview
A **Session** represents a single conversation with an AI avatar. Each session includes configuration for video streaming and tracks the lifecycle from creation to completion.
**Session Lifecycle:**
* **Created** → Session initialized (automatically started)
* **Started** → Session active with video room allocated
* **Ended** → Session completed, resources released
## Fields
Unique session identifier (starts with `sess_`)
Session status: `created`, `started` or `ended`
* `created` - Session initialized but not yet running
* `started` - Session is active and the avatar server is running
* `ended` - Session completed and resources released
ISO 8601 timestamp when session started (null before start)
ISO 8601 timestamp when session ended (null if still active)
Session access token for streaming (only returned in create response)
ISO 8601 timestamp when session will automatically expire based on plan limits
Video aspect ratio: `4:3`, `3:4` or `1:1`
Session duration in seconds. For active sessions, calculated in real-time. For ended sessions, total duration.
Custom metadata object (empty object `{}` if not provided). Can store any valid JSON data.
Billing information (only present for ended sessions)
Number of minutes consumed in this session (rounded up from duration)
Source of minutes used:
* `plan` - All minutes from included plan allowance
* `extra` - All minutes from purchased packages
* `mixed` - Combination of plan and purchased minutes
Billing status of the session:
* `free` - No additional charge (covered by plan)
* `billed` - Additional minutes charged from purchased packages
* `pending` - Billing not yet processed
Number of minutes charged from purchased packages (0 if all from plan)
## Status Values
| Status | Description |
| --------- | ----------------------------------------------- |
| `created` | Session initialized, server not yet allocated |
| `started` | Session is running with an active avatar server |
| `ended` | Session completed, resources released |
## Session Expiration
The `expiration` field indicates when the session will automatically end based on your subscription plan's time limits.
## Example
### Created Session
```json theme={null}
{
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "started",
"session_token": "session_token_xxxxxxxxxxxxx",
"started_at": "2024-01-15T10:30:00Z",
"ended_at": null,
"expiration": "2024-01-15T14:30:00Z",
"duration": null,
"aspect_ratio": "4:3",
"metadata": {}
}
```
### Ended Session
```json theme={null}
{
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "ended",
"started_at": "2024-01-15T10:30:00Z",
"ended_at": "2024-01-15T11:15:00Z",
"expiration": "2024-01-15T14:30:00Z",
"duration": 2700,
"aspect_ratio": "4:3",
"metadata": {},
"billing": {
"minutes_consumed": 45,
"minutes_source": "mixed",
"billing_status": "billed",
"minutes_billed": 15
}
}
```
The `billing` object is only included for ended sessions. It contains information about how many minutes were consumed and whether they came from the plan allowance or purchased packages. You can manage your plan and purchase additional minutes at [app.agenthuman.com/settings/billing](https://app.agenthuman.com/settings/billing).
# Usage Object
Source: https://docs.agenthuman.com/api-reference/schemas/usage
Schema definition for Usage objects returned by the API
## Overview
A **Usage** object represents the current state of a user's subscription usage, including minutes consumed, remaining allowances, active sessions and concurrency limits.
## Fields
Billing period information
ISO 8601 timestamp of period start
ISO 8601 timestamp of period end
Minutes included in subscription plan
Total included minutes for this period
Minutes used from included allowance
Minutes remaining from included allowance
Additional purchased minutes (separate from plan)
Total purchased minutes available
Purchased minutes remaining (after deducting any overage usage)
Total minutes remaining across all sources (included + purchased)
Array of currently active [Session objects](/api-reference/schemas/session). Each session has status `"active"` and `ended_at` set to `null`.
Minutes consumed by currently active sessions (rounded up per session). Included in total usage calculation.
Concurrent session limits and current usage
Number of currently active sessions
Maximum allowed concurrent sessions for current plan
Available concurrent session slots (max - current)
Avatar creation usage for the current billing cycle
Number of avatars created this billing cycle
Maximum avatars allowed per billing cycle (`null` = unlimited, `0` = not available on this plan)
Avatars remaining this billing cycle (`null` = unlimited)
Current subscription plan details
Plan identifier (e.g., 'free', 'explorer', 'growth', 'pro', 'enterprise')
Human-readable plan name
Maximum session duration in minutes (null = unlimited)
Whether subscription will auto-renew at period end
## Example
### Usage Summary with Active Sessions
```json theme={null}
{
"period": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-01-31T23:59:59Z"
},
"included_minutes": {
"total": 120,
"used": 75,
"remaining": 45
},
"purchased_minutes": {
"total": 100,
"remaining": 100
},
"total_remaining": 145,
"active_sessions": [
{
"session_id": "sess_01H3Z8G9YR3K2N5M6P7Q8W4T",
"status": "active",
"started_at": "2024-01-15T10:30:00Z",
"ended_at": null,
"expiration": "2024-01-15T14:30:00Z",
"aspect_ratio": "4:3",
"duration": 450,
"metadata": {
"user_name": "John Doe"
}
}
],
"active_minutes": 8,
"concurrency": {
"current": 1,
"max": 2,
"available": 1
},
"avatars": {
"used": 12,
"limit": 180,
"remaining": 168
},
"plan": {
"key": "pro",
"name": "Pro",
"max_session_duration": 240,
"will_renew": true
}
}
```
### Usage Summary with No Active Sessions
```json theme={null}
{
"period": {
"start": "2024-01-01T00:00:00Z",
"end": "2024-01-31T23:59:59Z"
},
"included_minutes": {
"total": 60,
"used": 15,
"remaining": 45
},
"purchased_minutes": {
"total": 0,
"remaining": 0
},
"total_remaining": 45,
"active_sessions": [],
"active_minutes": 0,
"concurrency": {
"current": 0,
"max": 2,
"available": 2
},
"avatars": {
"used": 0,
"limit": 0,
"remaining": 0
},
"plan": {
"key": "free",
"name": "Free",
"max_session_duration": 60,
"will_renew": false
}
}
```
## Billing Period Calculation
### Paid Plans
* Uses `current_period_start` and `current_period_end` from Stripe subscription
* Period typically aligns with billing cycle (monthly or annual)
### Free Plans
* Uses calendar month: 1st of month to last day of month
* Resets at the start of each calendar month
## Minutes Calculation
### Included Minutes
* Comes from subscription plan configuration
* Resets at the start of each billing period
* Used first before purchased minutes
### Purchased Minutes
* Additional minutes bought outside of subscription
* Do not expire by default (unless marked as bonus minutes)
* Used after included minutes are exhausted
* Deducted when sessions end (FIFO - first purchased, first consumed)
Purchase additional minutes at [app.agenthuman.com/settings/billing](https://app.agenthuman.com/settings/billing).
### Active Minutes
* Calculated in real-time from currently running sessions
* Rounds up to nearest minute for each session
* Included in total usage calculation
## Concurrency Limits
The `concurrency` object tracks concurrent session usage:
* **current**: Number of sessions currently active (running at the same time)
* **max**: Maximum allowed concurrent sessions based on your plan
* **available**: Remaining concurrent session slots you can start (calculated as `max - current`)
**What are concurrent sessions?** Concurrent sessions are sessions that are running at the same time. For example, if your plan allows 3 concurrent sessions, you can have up to 3 active avatar sessions running simultaneously. Once you end a session, that slot becomes available for a new session.
Plans have different concurrency limits:
* **Free**: 1 concurrent session
* **Explorer**: 5 concurrent sessions
* **Growth**: 10 concurrent sessions
* **Pro**: 20 concurrent sessions
* **Enterprise**: Custom limits
## Avatar Creation Limits
The `avatars` object tracks how many custom avatars have been generated this billing cycle using the avatar creator.
* **`used`** — avatars created so far in the current cycle
* **`limit`** — the plan's per-cycle cap (`0` = not available, `null` = unlimited)
* **`remaining`** — avatars left to create this cycle (`null` = unlimited)
Limits reset at the start of each billing cycle, the same as included minutes.
| Plan | Avatars per cycle |
| ---------- | ----------------- |
| Free | Not available (0) |
| Explorer | 20 |
| Growth | 60 |
| Pro | 180 |
| Enterprise | Unlimited |
Attempting to generate an avatar after reaching your limit returns a `403` error with `reason: "limit_reached"`. Upgrading your plan immediately grants the higher limit for the remainder of the current cycle.
# Introduction
Source: https://docs.agenthuman.com/documentation/introduction
Integrate real-time talking avatars into your voice AI pipeline
## Overview
Agent Human provides native integrations for the two most popular real-time voice AI frameworks — **Pipecat** and **LiveKit Agents**. Both integrations handle all the complexity of session management, audio streaming, and video delivery so you can add a talking avatar to your pipeline with just a few lines of code.
## Choose Your Integration
**`AgentHumanVideoService`** — slots into any Pipecat pipeline right after your TTS service. Sends TTS audio to the avatar and injects the avatar's video frames back into your pipeline.
**`AvatarSession`** — drops into your LiveKit Agents entrypoint and renders your agent's voice output as a talking avatar video in the LiveKit room.
## How It Works
Both integrations follow the same underlying flow:
1. **Create a session** — the integration calls the Agent Human REST API to provision an avatar server
2. **Stream audio** — your pipeline's TTS audio is forwarded to the avatar server in real time
3. **Receive video** — the avatar server generates synchronized talking-head video and publishes it to your video room
You never need to manage sessions, tokens, or audio encoding manually — the integration handles all of it.
## Prerequisites
* An Agent Human account ([sign up here](https://app.agenthuman.com))
* An API key from [Settings → API Keys](https://app.agenthuman.com/settings/apikeys)
* An avatar image (URL or base64) from your [dashboard](https://app.agenthuman.com)
## Next Steps
Add a talking avatar to a Pipecat bot in minutes
Add a talking avatar to a LiveKit agent in minutes
REST API for sessions, avatars, and usage
# Configuration
Source: https://docs.agenthuman.com/documentation/livekit/configuration
AvatarSession parameters and options
## AvatarSession
```python theme={null}
agenthuman.AvatarSession(
avatar="avat_xxxxxxxxxxxxxxxxxxxxxxxx",
aspect_ratio="4:3"
)
```
### Constructor Parameters
| Parameter | Type | Required | Description |
| ----------------------------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
| `avatar` | string | No | Avatar ID or image URL. Falls back to `AGENTHUMAN_AVATAR` env var, then a default avatar |
| `aspect_ratio` | string | No | Video aspect ratio: `'4:3'`, `'3:4'` or `'1:1'`. Defaults to `'4:3'` |
| `api_key` | string | No | Agent Human API key. Falls back to `AGENTHUMAN_API_KEY` env var |
| `avatar_participant_identity` | string | No | LiveKit identity for the avatar participant (default: `'agenthuman-avatar-agent'`) |
| `avatar_participant_name` | string | No | LiveKit display name for the avatar participant (default: `'agenthuman-avatar-agent'`) |
## `start()` Method
```python theme={null}
await avatar.start(
agent_session,
room=ctx.room,
livekit_url="wss://...", # optional — reads LIVEKIT_URL env var
livekit_api_key="...", # optional — reads LIVEKIT_API_KEY env var
livekit_api_secret="..." # optional — reads LIVEKIT_API_SECRET env var
)
```
| Parameter | Type | Required | Description |
| -------------------- | -------------- | -------- | --------------------------------- |
| `agent_session` | `AgentSession` | Yes | The active LiveKit `AgentSession` |
| `room` | `rtc.Room` | Yes | The LiveKit room from `ctx.room` |
| `livekit_url` | string | No | Override for `LIVEKIT_URL` |
| `livekit_api_key` | string | No | Override for `LIVEKIT_API_KEY` |
| `livekit_api_secret` | string | No | Override for `LIVEKIT_API_SECRET` |
`start()` generates a LiveKit token for the avatar, creates the Agent Human session, and attaches the avatar's audio output to the room. It must be called before `session.start()`.
## `session.state` Events
The Agent Human server sends status updates to the room as LiveKit data packets on the `session.state` topic. You can listen for them on `ctx.room`:
```python theme={null}
from livekit import rtc
@ctx.room.on("data_received")
def on_data_received(data_packet: rtc.DataPacket) -> None:
if data_packet.topic == "session.state":
import json
payload = json.loads(data_packet.data.decode("utf-8"))
state = payload.get("state")
reason = payload.get("reason", "")
print(f"Avatar state: {state} — {reason}")
```
### Payload Fields
| Field | Type | Description |
| -------- | ------ | ------------------------------------------------------------------------------------- |
| `state` | string | Current avatar state (e.g. `"connected"`, `"disconnected"`) |
| `reason` | string | Optional reason string, present when state changes due to an error or explicit action |
# Examples
Source: https://docs.agenthuman.com/documentation/livekit/examples
Complete LiveKit Agent examples with Agent Human avatars
## Agent with OpenAI + ElevenLabs
```python theme={null}
from dotenv import load_dotenv
from livekit.agents import Agent, AgentServer, AgentSession, JobContext
from livekit.plugins import deepgram, elevenlabs, openai, silero
import agenthuman
load_dotenv()
server = AgentServer()
@server.rtc_session()
async def entrypoint(ctx: JobContext):
session = AgentSession(
vad=silero.VAD.load(),
stt=deepgram.STT(model="nova-3", language="multi"),
llm=openai.LLM(model="gpt-4o-mini"),
tts=elevenlabs.TTS(
voice_id="cgSgspJ2msm6clMCkdW9",
model="eleven_multilingual_v2"
)
)
avatar = agenthuman.AvatarSession(
avatar="avat_xxxxxxxxxxxxxxxxxxxxxxxx",
aspect_ratio="3:4"
)
await avatar.start(session, room=ctx.room)
await session.start(
agent=Agent(instructions="You are a friendly voice assistant."),
room=ctx.room
)
await session.generate_reply(instructions="Greet the user and ask about their day.")
if __name__ == "__main__":
from livekit.agents import cli
cli.run_app(server)
```
## Agent with Cartesia TTS
```python theme={null}
from dotenv import load_dotenv
from livekit.agents import Agent, AgentServer, AgentSession, JobContext
from livekit.plugins import cartesia, openai, silero, deepgram
import agenthuman
load_dotenv()
server = AgentServer()
@server.rtc_session()
async def entrypoint(ctx: JobContext):
session = AgentSession(
vad=silero.VAD.load(),
stt=deepgram.STT(model="nova-3"),
llm=openai.LLM(model="gpt-4o-mini"),
tts=cartesia.TTS(
model="sonic-3",
voice="9626c31c-bec5-4cca-baa8-f8ba9e84c8bc"
)
)
avatar = agenthuman.AvatarSession(
avatar="avat_xxxxxxxxxxxxxxxxxxxxxxxx",
aspect_ratio="4:3"
)
await avatar.start(session, room=ctx.room)
await session.start(
agent=Agent(instructions="You are a helpful customer support agent."),
room=ctx.room
)
if __name__ == "__main__":
from livekit.agents import cli
cli.run_app(server)
```
## Using LiveKit Inference
```python theme={null}
from dotenv import load_dotenv
from livekit.agents import Agent, AgentServer, AgentSession, JobContext, inference
from livekit.plugins import silero
import agenthuman
load_dotenv()
server = AgentServer()
@server.rtc_session()
async def entrypoint(ctx: JobContext):
session = AgentSession(
vad=silero.VAD.load(),
stt=inference.STT("deepgram/nova-3"),
llm=inference.LLM("openai/gpt-4.1-mini"),
tts=inference.TTS("cartesia/sonic-3"),
)
avatar = agenthuman.AvatarSession(
avatar="avat_xxxxxxxxxxxxxxxxxxxxxxxx",
aspect_ratio="4:3"
)
await avatar.start(session, room=ctx.room)
await session.start(
agent=Agent(instructions="You are a helpful assistant."),
room=ctx.room
)
if __name__ == "__main__":
from livekit.agents import cli
cli.run_app(server)
```
# LiveKit Integration
Source: https://docs.agenthuman.com/documentation/livekit/overview
Add Agent Human talking avatars to your LiveKit Agents pipeline
## Overview
`AvatarSession` is an Agent Human plugin for [LiveKit Agents](https://docs.livekit.io/agents). Drop it into your agent entrypoint and your agent's voice output will be rendered as a talking avatar video in the LiveKit room — no manual session or token management required.
We are currently in the application process to be listed on the LiveKit framework GitHub repository.
## Installation
```bash theme={null}
pip install "livekit-agents[agenthuman]"
```
## Environment Variables
| Variable | Description |
| -------------------- | -------------------------------------------------------------------- |
| `AGENTHUMAN_API_KEY` | Your Agent Human API key |
| `AGENTHUMAN_AVATAR` | Default avatar ID (used if no `avatar` is passed to `AvatarSession`) |
| `LIVEKIT_URL` | Your LiveKit server WebSocket URL |
| `LIVEKIT_API_KEY` | LiveKit API key |
| `LIVEKIT_API_SECRET` | LiveKit API secret |
## Next Steps
Add AvatarSession to your agent in minutes
AvatarSession parameters and options
Complete agent examples
Underlying session API
# Quick Start
Source: https://docs.agenthuman.com/documentation/livekit/quick-start
Add an Agent Human avatar to your LiveKit Agent in three steps
## Step 1 — Install and configure
```bash theme={null}
pip install "livekit-agents[agenthuman]"
```
Set your environment variables:
```bash theme={null}
AGENTHUMAN_API_KEY=ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
AGENTHUMAN_AVATAR=avat_xxxxxxxxxxxxxxxxxxxxxxxx # optional default avatar
LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=your-livekit-key
LIVEKIT_API_SECRET=your-livekit-secret
```
## Step 2 — Add `AvatarSession` to your agent
Import `AvatarSession`, instantiate it with your avatar, and call `start()` before starting the agent session:
```python theme={null}
import agenthuman
from livekit.agents import Agent, AgentServer, AgentSession, JobContext
server = AgentServer()
@server.rtc_session()
async def entrypoint(ctx: JobContext):
session = AgentSession(
# ... your STT, LLM, TTS config
)
avatar = agenthuman.AvatarSession(
avatar="avat_xxxxxxxxxxxxxxxxxxxxxxxx",
aspect_ratio="4:3"
)
await avatar.start(session, room=ctx.room)
await session.start(
agent=Agent(instructions="You are a helpful assistant."),
room=ctx.room
)
```
That's it. `AvatarSession` creates the Agent Human session, generates the LiveKit token, and routes your agent's audio output through the avatar video stream automatically.
## Step 3 — Run your agent
```bash theme={null}
python agent.py start
```
`avatar.start()` reads `LIVEKIT_URL`, `LIVEKIT_API_KEY`, and `LIVEKIT_API_SECRET` from environment variables. You can also pass them directly — see [Configuration](/documentation/livekit/configuration).
# Configuration
Source: https://docs.agenthuman.com/documentation/pipecat/configuration
AgentHumanVideoService parameters, NewSessionRequest options, and transport requirements
## `AgentHumanVideoService`
```python theme={null}
from pipecat.services.agenthuman.api import NewSessionRequest
from pipecat.services.agenthuman.video import AgentHumanVideoService
AgentHumanVideoService(
api_key="ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
session_request=NewSessionRequest(avatar="avat_01KMZHXFPBVCXA5ATK85HCP8G1"),
transport=transport,
)
```
### Constructor Parameters
| Parameter | Type | Description |
| ----------------- | ------------------- | ----------------------------------------------------------------------------------------------- |
| `api_key` | `str` | **Required.** Your AgentHuman API key |
| `session_request` | `NewSessionRequest` | Avatar and aspect ratio configuration. Defaults to a built-in avatar with `aspect_ratio="auto"` |
| `transport` | `BaseTransport` | **Required.** Your Pipecat output transport. Must have `video_out_enabled=True` |
## `NewSessionRequest`
```python theme={null}
from pipecat.services.agenthuman.api import NewSessionRequest
NewSessionRequest(
avatar="avat_01KMZHXFPBVCXA5ATK85HCP8G1",
aspect_ratio="auto", # optional
)
```
### Parameters
| Parameter | Type | Description |
| -------------- | ----- | -------------------------------------------------------------------------------- |
| `avatar` | `str` | **Required.** Avatar ID from your AgentHuman dashboard |
| `aspect_ratio` | `str` | Video aspect ratio: `"4:3"`, `"3:4"`, `"1:1"`, or `"auto"`. Defaults to `"auto"` |
### Aspect Ratios
| Value | Typical resolution | Best for |
| -------- | ---------------------- | ----------------------- |
| `"4:3"` | `1280×960` | Landscape / desktop |
| `"3:4"` | `960x1280` | Portrait / mobile |
| `"1:1"` | `1280x1280` | Square layouts |
| `"auto"` | Derived from transport | Automatic — recommended |
When `aspect_ratio` is `"auto"` (the default), the service reads `video_out_width` and `video_out_height` from your transport and selects the closest supported ratio. A warning is logged if the dimensions don't closely match any standard ratio.
## Transport Requirements
`AgentHumanVideoService` requires a transport with video output enabled. Passing a transport without `video_out_enabled=True` raises a `ValueError` immediately.
```python theme={null}
# Daily.co
from pipecat.transports.daily.transport import DailyParams
DailyParams(
audio_in_enabled=True,
audio_out_enabled=True,
video_out_enabled=True, # required
video_out_is_live=True,
video_out_width=1280,
video_out_height=960,
video_out_bitrate=2_000_000,
)
```
```python theme={null}
# Generic WebRTC
from pipecat.transports.base_transport import TransportParams
TransportParams(
audio_in_enabled=True,
audio_out_enabled=True,
video_out_enabled=True, # required
video_out_is_live=True,
video_out_width=1280,
video_out_height=960,
)
```
## Session Lifecycle
| Phase | What happens |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **Setup** | `POST /sessions` API call — AgentHuman avatar session created, internal LiveKit credentials returned |
| **Start** | LiveKit room connection established; audio chunk size calculated from transport dimensions |
| **Running** | TTS frames resampled to 16 kHz mono → sent to avatar via `DataStreamAudioOutput`; avatar video/audio frames forwarded downstream |
| **Stop / Cancel** | LiveKit room disconnected; `POST /sessions/{id}/end` called to terminate the session |
## Audio Processing
The service automatically resamples all incoming TTS audio to **16 kHz mono PCM** before forwarding to the avatar. No manual audio format configuration is needed regardless of which TTS service you use.
Audio is buffered and sent in chunks. A new chunk is dispatched when:
* The buffer reaches the target chunk size **and** silence is detected, or
* A `TTSStoppedFrame` is received (end of utterance)
A 2-second trailing silence is appended to each final chunk to ensure the avatar finishes animating cleanly.
# Examples
Source: https://docs.agenthuman.com/documentation/pipecat/examples
Complete Pipecat bot examples with AgentHuman avatars
## Reference bot (Pipecat runner)
This matches the Pipecat example script `examples/video-avatar/video-avatar-agenthuman-video-service.py`: Deepgram STT, Google Gemini LLM, ElevenLabs TTS, and `AgentHumanVideoService` with the Pipecat `create_transport` runner (Daily or WebRTC).
Install the extras used here (in addition to your transport/STT/TTS/LLM keys in `.env`):
```bash theme={null}
pip install "pipecat-ai[agenthuman,daily,deepgram,elevenlabs,google]" python-dotenv
```
```python theme={null}
import os
from dotenv import load_dotenv
from loguru import logger
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.frames.frames import LLMRunFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import (
LLMContextAggregatorPair,
LLMUserAggregatorParams,
)
from pipecat.runner.types import RunnerArguments
from pipecat.runner.utils import create_transport
from pipecat.services.agenthuman.api import NewSessionRequest
from pipecat.services.agenthuman.video import AgentHumanVideoService
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
from pipecat.services.google.llm import GoogleLLMService
from pipecat.transports.base_transport import BaseTransport, TransportParams
from pipecat.transports.daily.transport import DailyParams, DailyTransport
load_dotenv(override=True)
# Lambdas defer transport params until the runner selects Daily vs WebRTC at runtime.
transport_params = {
"daily": lambda: DailyParams(
audio_in_enabled=True,
audio_out_enabled=True,
video_out_enabled=True,
video_out_is_live=True,
video_out_width=1280,
video_out_height=960,
video_out_bitrate=2_000_000,
),
"webrtc": lambda: TransportParams(
audio_in_enabled=True,
audio_out_enabled=True,
video_out_enabled=True,
video_out_is_live=True,
video_out_width=1280,
video_out_height=960,
),
}
async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
logger.info("Starting bot")
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
tts = ElevenLabsTTSService(
api_key=os.getenv("ELEVENLABS_API_KEY"),
voice_id="cgSgspJ2msm6clMCkdW9",
)
llm = GoogleLLMService(
api_key=os.getenv("GOOGLE_API_KEY"),
settings=GoogleLLMService.Settings(
system_instruction=(
"You are a helpful assistant. Your output will be spoken aloud, so avoid "
"special characters that can't easily be spoken, such as emojis or bullet points. "
"Be succinct and respond to what the user said in a creative and helpful way."
),
),
)
agentHuman = AgentHumanVideoService(
api_key=os.getenv("AGENTHUMAN_API_KEY"),
session_request=NewSessionRequest(
avatar="avat_01KMZHXFPBVCXA5ATK85HCP8G1"
),
transport=transport,
)
context = LLMContext()
user_aggregator, assistant_aggregator = LLMContextAggregatorPair(
context,
user_params=LLMUserAggregatorParams(vad_analyzer=SileroVADAnalyzer()),
)
pipeline = Pipeline(
[
transport.input(),
stt,
user_aggregator,
llm,
tts,
agentHuman,
transport.output(),
assistant_aggregator,
]
)
task = PipelineTask(
pipeline,
params=PipelineParams(
enable_metrics=True,
enable_usage_metrics=True,
),
idle_timeout_secs=runner_args.pipeline_idle_timeout_secs,
)
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
logger.info("Client connected")
if isinstance(transport, DailyTransport):
await transport.update_publishing(
publishing_settings={
"camera": {
"sendSettings": {
"allowAdaptiveLayers": True,
}
}
}
)
context.add_message(
{
"role": "developer",
"content": "Start by saying 'Hello' and then a short greeting.",
}
)
await task.queue_frames([LLMRunFrame()])
@transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, client):
logger.info("Client disconnected")
await task.cancel()
runner = PipelineRunner(handle_sigint=runner_args.handle_sigint)
await runner.run(task)
async def bot(runner_args: RunnerArguments):
"""Main bot entry point compatible with Pipecat Cloud and the local runner."""
transport = await create_transport(runner_args, transport_params)
await run_bot(transport, runner_args)
if __name__ == "__main__":
from pipecat.runner.run import main
main()
```
Run with the Pipecat development runner so `create_transport` receives `RunnerArguments` from your CLI or Pipecat Cloud deployment.
## Manual `DailyTransport` (no runner CLI)
If you construct `DailyTransport` yourself, reuse `run_bot` by passing a default `RunnerArguments()` for idle timeout and signal handling:
```python theme={null}
import asyncio
import os
from dotenv import load_dotenv
from pipecat.transports.daily.transport import DailyParams, DailyTransport
from pipecat.runner.types import RunnerArguments
load_dotenv(override=True)
async def main():
transport = DailyTransport(
room_url=os.getenv("DAILY_ROOM_URL"),
token=os.getenv("DAILY_TOKEN"),
bot_name="AI Avatar",
params=DailyParams(
audio_in_enabled=True,
audio_out_enabled=True,
video_out_enabled=True,
video_out_is_live=True,
video_out_width=1280,
video_out_height=960,
video_out_bitrate=2_000_000,
),
)
await run_bot(transport, RunnerArguments())
if __name__ == "__main__":
asyncio.run(main())
```
Define `run_bot` as in the reference example above (same file as `main`).
## Bot with Cartesia TTS
Swap ElevenLabs for Cartesia — pipeline and `AgentHumanVideoService` stay the same.
```python theme={null}
from pipecat.services.cartesia.tts import CartesiaTTSService
tts = CartesiaTTSService(
api_key=os.getenv("CARTESIA_API_KEY"),
settings=CartesiaTTSService.Settings(
voice="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
),
)
```
Install `pip install "pipecat-ai[cartesia]"` (or include `cartesia` in your combined extras).
## Portrait avatar (mobile layout)
Use a `3:4` aspect ratio for portrait video, suitable for mobile UIs.
```python theme={null}
from pipecat.services.agenthuman.api import NewSessionRequest
from pipecat.services.agenthuman.video import AgentHumanVideoService
transport = DailyTransport(
room_url=os.getenv("DAILY_ROOM_URL"),
token=os.getenv("DAILY_TOKEN"),
bot_name="AI Avatar",
params=DailyParams(
audio_in_enabled=True,
audio_out_enabled=True,
video_out_enabled=True,
video_out_is_live=True,
video_out_width=960,
video_out_height=1280,
),
)
agentHuman = AgentHumanVideoService(
api_key=os.getenv("AGENTHUMAN_API_KEY"),
session_request=NewSessionRequest(
avatar="avat_01KMZHXFPBVCXA5ATK85HCP8G1",
aspect_ratio="3:4",
),
transport=transport,
)
```
# Pipecat Integration
Source: https://docs.agenthuman.com/documentation/pipecat/overview
Add a talking avatar to any Pipecat voice AI pipeline with AgentHuman
## Overview
`AgentHumanVideoService` is a Pipecat `AIService` that slots into your pipeline right after your TTS service. It receives TTS audio frames, sends them to the AgentHuman avatar via a LiveKit data stream, and injects the avatar's video and audio frames back into the pipeline for your output transport to publish.
We are currently in the application process to be listed on the Pipecat framework GitHub repository.
**Perfect for:**
* Voice AI applications with a visual presenter
* Conversational agents with animated avatars
* Real-time speech-driven video generation
* Interactive AI assistants
## How It Works
```
User mic → Transport input → STT → LLM → TTS → AgentHumanVideoService → Transport output
↑ ↑
Sends audio to avatar Receives avatar video
```
`AgentHumanVideoService` handles everything internally:
1. Creates an AgentHuman session via the REST API on startup
2. Connects to the returned LiveKit room
3. Resamples TTS audio to 16 kHz mono and streams it to the avatar
4. Forwards the avatar's video and audio frames downstream to your transport
## Installation
```bash theme={null}
pip install pipecat-ai[agenthuman]
```
The integration lives in **`pipecat.services.agenthuman`**: import `AgentHumanVideoService` from `pipecat.services.agenthuman.video` and `NewSessionRequest` from `pipecat.services.agenthuman.api`.
The `agenthuman` extra installs the LiveKit SDK (`livekit`) required for the avatar data stream. The [Examples](/documentation/pipecat/examples) use additional Pipecat extras (`daily`, `deepgram`, `elevenlabs`, `google`, and `python-dotenv`) — install those alongside `agenthuman` when you run the full bot.
## Environment Variables
| Variable | Description |
| -------------------- | ----------------------- |
| `AGENTHUMAN_API_KEY` | Your AgentHuman API key |
## Next Steps
Add AgentHumanVideoService to your pipeline in minutes
All parameters and transport requirements
Complete working bot examples
Underlying session API
# Quick Start
Source: https://docs.agenthuman.com/documentation/pipecat/quick-start
Add an AgentHuman avatar to your Pipecat pipeline in four steps
## Step 1 — Install and configure
```bash theme={null}
pip install pipecat-ai[agenthuman]
```
Add your AgentHuman API key to your `.env` file, along with any STT, LLM, and TTS services you plan to use:
```bash theme={null}
AGENTHUMAN_API_KEY=ah_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Example services (use whichever LLM / STT / TTS you prefer)
DAILY_ROOM_URL=https://your-domain.daily.co/your-room
DAILY_TOKEN=your-daily-participant-token
DEEPGRAM_API_KEY=your-deepgram-key
GOOGLE_API_KEY=your-google-ai-key
ELEVENLABS_API_KEY=your-elevenlabs-key
```
## Step 2 — Set up your transport with video output
`AgentHumanVideoService` **requires** a transport with `video_out_enabled=True`. The transport dimensions are used to auto-select the avatar's aspect ratio.
```python theme={null}
from pipecat.transports.daily.transport import DailyParams, DailyTransport
transport = DailyTransport(
room_url=os.getenv("DAILY_ROOM_URL"),
token=os.getenv("DAILY_TOKEN"),
bot_name="AI Avatar",
params=DailyParams(
audio_in_enabled=True,
audio_out_enabled=True,
video_out_enabled=True, # required
video_out_is_live=True,
video_out_width=1280,
video_out_height=960,
video_out_bitrate=2_000_000,
),
)
```
## Step 3 — Add `AgentHumanVideoService` to your pipeline
Import `AgentHumanVideoService` and `NewSessionRequest` from `pipecat.services.agenthuman`, instantiate the service with your avatar ID and transport, and place it in the pipeline **after TTS** and **before `transport.output()`**.
```python theme={null}
from pipecat.services.agenthuman.api import NewSessionRequest
from pipecat.services.agenthuman.video import AgentHumanVideoService
agentHuman = AgentHumanVideoService(
api_key=os.getenv("AGENTHUMAN_API_KEY"),
session_request=NewSessionRequest(
avatar="avat_01KMZHXFPBVCXA5ATK85HCP8G1" # your avatar ID
),
transport=transport,
)
pipeline = Pipeline([
transport.input(),
stt,
user_aggregator,
llm,
tts,
agentHuman, # ← place after TTS
transport.output(),
assistant_aggregator,
])
```
## Step 4 — Run your bot
```python theme={null}
task = PipelineTask(pipeline, params=PipelineParams(enable_metrics=True))
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
context.add_message({"role": "user", "content": "Say hello and briefly introduce yourself."})
await task.queue_frames([LLMRunFrame()])
@transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, client):
await task.cancel()
runner = PipelineRunner()
await runner.run(task)
```
```bash theme={null}
python bot.py
```
`AgentHumanVideoService` creates the AgentHuman session and connects to the internal LiveKit room automatically on pipeline start. You don't need to manage room tokens or WebSocket connections manually.