curl --fail-with-body --request POST \
--url "$XPILOT_API_BASE_URL/v1/avatars" \
--header "Authorization: Bearer $XPILOT_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: lesson-request-001' \
--data '{"name":"<name>","avatar_type":"human_video","video_url":"https://cdn.example.com/training.mp4"}'const response = await fetch(process.env.XPILOT_API_BASE_URL + "/v1/avatars", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.XPILOT_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "lesson-request-001",
},
body: JSON.stringify({
"name": "<name>",
"avatar_type": "human_video",
"video_url": "https://cdn.example.com/training.mp4"
}),
});
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result));
console.log(result);import os, json, requests
response = requests.request(
"POST",
os.environ["XPILOT_API_BASE_URL"] + "/v1/avatars",
headers={
"Authorization": "Bearer " + os.environ["XPILOT_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": "lesson-request-001",
},
json=json.loads("{\"name\":\"<name>\",\"avatar_type\":\"human_video\",\"video_url\":\"https://cdn.example.com/training.mp4\"}"),
timeout=70,
)
print(response.status_code, response.text)
response.raise_for_status(){
"data": {
"id": "<avatar_id>",
"name": "Presenter",
"avatar_type": "human_video",
"status": "generating",
"training_status": "pending",
"preview_status": "not_requested",
"selectable": false,
"stage": "provider_processing",
"preview_image_url": null,
"preview_video_url": null,
"error": null,
"created_at": 1791849600,
"updated_at": 1791849600
}
}{
"error": {
"code": "invalid_parameter",
"message": "Check parameter types, required fields and idempotency key."
},
"request_id": "<request_id>"
}{
"error": {
"code": "invalid_token",
"message": "Use an active scoped API Key. MCP OAuth tokens cannot call REST."
},
"request_id": "<request_id>"
}{
"error": {
"code": "insufficient_scope",
"message": "Check the credential scope and resource ownership."
},
"request_id": "<request_id>"
}{
"error": {
"code": "idempotency_conflict",
"message": "The same idempotency key must use the exact same input. A conversation may already be busy."
},
"request_id": "<request_id>"
}{
"error": {
"code": "rate_limited",
"message": "Credential/workspace rate or concurrency limit reached. Respect Retry-After before retrying.",
"retry_after": 60
},
"request_id": "<request_id>"
}create avatar
Create an avatar from an owned asset_id or a public http(s) image/video URL. Third-party URLs are downloaded and archived to the caller’s owned storage before training. Requires platform entitlement. Poll get_avatar for preview_image_url and preview_video_url, or receive avatar.completed / avatar.failed through callback_url or a webhook subscription. Reuse Idempotency-Key on retry; never retrain automatically.
curl --fail-with-body --request POST \
--url "$XPILOT_API_BASE_URL/v1/avatars" \
--header "Authorization: Bearer $XPILOT_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: lesson-request-001' \
--data '{"name":"<name>","avatar_type":"human_video","video_url":"https://cdn.example.com/training.mp4"}'const response = await fetch(process.env.XPILOT_API_BASE_URL + "/v1/avatars", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.XPILOT_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "lesson-request-001",
},
body: JSON.stringify({
"name": "<name>",
"avatar_type": "human_video",
"video_url": "https://cdn.example.com/training.mp4"
}),
});
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result));
console.log(result);import os, json, requests
response = requests.request(
"POST",
os.environ["XPILOT_API_BASE_URL"] + "/v1/avatars",
headers={
"Authorization": "Bearer " + os.environ["XPILOT_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": "lesson-request-001",
},
json=json.loads("{\"name\":\"<name>\",\"avatar_type\":\"human_video\",\"video_url\":\"https://cdn.example.com/training.mp4\"}"),
timeout=70,
)
print(response.status_code, response.text)
response.raise_for_status(){
"data": {
"id": "<avatar_id>",
"name": "Presenter",
"avatar_type": "human_video",
"status": "generating",
"training_status": "pending",
"preview_status": "not_requested",
"selectable": false,
"stage": "provider_processing",
"preview_image_url": null,
"preview_video_url": null,
"error": null,
"created_at": 1791849600,
"updated_at": 1791849600
}
}{
"error": {
"code": "invalid_parameter",
"message": "Check parameter types, required fields and idempotency key."
},
"request_id": "<request_id>"
}{
"error": {
"code": "invalid_token",
"message": "Use an active scoped API Key. MCP OAuth tokens cannot call REST."
},
"request_id": "<request_id>"
}{
"error": {
"code": "insufficient_scope",
"message": "Check the credential scope and resource ownership."
},
"request_id": "<request_id>"
}{
"error": {
"code": "idempotency_conflict",
"message": "The same idempotency key must use the exact same input. A conversation may already be busy."
},
"request_id": "<request_id>"
}{
"error": {
"code": "rate_limited",
"message": "Credential/workspace rate or concurrency limit reached. Respect Retry-After before retrying.",
"retry_after": 60
},
"request_id": "<request_id>"
}Documentation by X-Pilot
Required scope:avatars:write. A stable Idempotency-Key is required for exact retries.
Manage API keys · Authentication and recovery

Authorizations
Scoped X-Pilot API key
Headers
Stable key for exact retries only. Use a different key for a different operation or input.
8 - 128Body
Avatar name.
80Training source.
human_video, virtual_character Owned asset ID returned by POST /v1/assets. human_video uses a video asset; virtual_character uses an image asset.
Public http(s) image URL. The backend archives third-party content to your owned storage before training. Use this or asset_id for virtual_character.
Public http(s) training video URL. The backend archives third-party content to your owned storage before training. Use this or asset_id for human_video.
Required for image avatars.
Male, Female Optional public HTTPS URL notified once with avatar.completed or avatar.failed. Signed with the workspace callback secret (X-XPilot-Signature). Only set it when you run a receiving service.
2048Optional reference echoed back as data.callback_id. Requires callback_url.
128Response
Success
