Asset management
Manage your characters, motions, and other assets through the Uthana API. Upload, list, download, and organize your 3D assets programmatically.
Overview
Asset management covers the full lifecycle of your 3D assets:
- Characters: Upload and manage 3D character models
- Motions: List, query, and download animation sequences
- Assets: Access metadata and file information
Character management
List your characters
Retrieve all characters available to your organization.
- Shell
- Python
- TypeScript
- C#
API_KEY="{{apiKey}}"
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{
"query": "{ characters { id name created updated assets { id filename } } }"
}'
import requests
API_URL = 'https://uthana.com/graphql'
API_KEY = '{{apiKey}}'
query = '''
query {
characters {
id
name
created
updated
assets {
id
filename
}
}
}
'''
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': query}
)
result = response.json()
characters = result["data"]["characters"]
for char in characters:
print(f"{char['name']} ({char['id']})")
const API_URL = "https://uthana.com/graphql";
const API_KEY = "{{apiKey}}";
const authString = btoa(`${API_KEY}:`);
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: `
query {
characters {
id
name
created
updated
assets {
id
filename
}
}
}
`,
}),
});
const json = await response.json();
const characters = json.data.characters;
characters.forEach((char: any) => {
console.log(`${char.name} (${char.id})`);
});
private const string ApiUrl = "https://uthana.com/graphql";
private readonly string _apiKey = "{{apiKey}}";
public async Task<List<Character>> GetCharactersAsync()
{
var query = @"
query {
characters {
id
name
created
updated
assets {
id
filename
}
}
}";
var request = new { query = query };
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var authValue = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_apiKey}:"));
_httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authValue);
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<CharactersData>>(responseJson);
return result.Data.Characters;
}
// Usage
var characters = await GetCharactersAsync();
foreach (var character in characters)
{
Console.WriteLine($"{character.Name} ({character.Id})");
}
See the Auto-rig / add a character guide for uploading characters.
Delete a character
Delete a character by setting its deleted flag to true. Built-in characters (like the default character "Tar") cannot be deleted.
- Shell
- Python
- TypeScript
- C#
CHARACTER_ID="your-character-id"
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{
"query": "mutation UpdateCharacter($character_id: String!, $deleted: Boolean) { update_character(character_id: $character_id, deleted: $deleted) { character { id name deleted } } }",
"variables": { "character_id": "'"$CHARACTER_ID"'", "deleted": true }
}'
CHARACTER_ID = "your-character-id"
query = '''
mutation UpdateCharacter($character_id: String!, $deleted: Boolean) {
update_character(character_id: $character_id, deleted: $deleted) {
character {
id
name
deleted
}
}
}
'''
variables = {
"character_id": CHARACTER_ID,
"deleted": True
}
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': query, 'variables': variables}
)
result = response.json()
character = result["data"]["update_character"]["character"]
print(f"Character deleted: {character['name']}")
const CHARACTER_ID = "your-character-id";
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: `
mutation UpdateCharacter($character_id: String!, $deleted: Boolean) {
update_character(character_id: $character_id, deleted: $deleted) {
character {
id
name
deleted
}
}
}
`,
variables: {
character_id: CHARACTER_ID,
deleted: true,
},
}),
});
const json = await response.json();
const character = json.data.update_character.character;
console.log(`Character deleted: ${character.name}`);
public async Task<Character> DeleteCharacterAsync(string characterId)
{
var query = @"
mutation UpdateCharacter($character_id: String!, $deleted: Boolean) {
update_character(character_id: $character_id, deleted: $deleted) {
character {
id
name
deleted
}
}
}";
var request = new
{
query = query,
variables = new
{
character_id = characterId,
deleted = true
}
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<UpdateCharacterData>>(responseJson);
return result.Data.UpdateCharacter.Character;
}
See the Download a character guide for downloading characters.
Motion management
List your motions
Retrieve all motions available to your organization.
- Shell
- Python
- TypeScript
- C#
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{
"query": "{ motions { id name created updated assets { id filename } } }"
}'
query = '''
query {
motions {
id
name
created
updated
assets {
id
filename
}
}
}
'''
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': query}
)
result = response.json()
motions = result["data"]["motions"]
for motion in motions:
print(f"{motion['name']} ({motion['id']})")
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: `
query {
motions {
id
name
created
updated
assets {
id
filename
}
}
}
`,
}),
});
const json = await response.json();
const motions = json.data.motions;
motions.forEach((motion: any) => {
console.log(`${motion.name} (${motion.id})`);
});
public async Task<List<Motion>> GetMotionsAsync()
{
var query = @"
query {
motions {
id
name
created
updated
assets {
id
filename
}
}
}";
var request = new { query = query };
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<MotionsData>>(responseJson);
return result.Data.Motions;
}
Get a specific motion
Retrieve detailed information about a specific motion by ID.
- Shell
- Python
- TypeScript
- C#
MOTION_ID="your-motion-id"
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{
"query": "query GetMotion($id: String!) { motion(id: $id) { id name created updated tags assets { id filename } } }",
"variables": { "id": "'"$MOTION_ID"'" }
}'
MOTION_ID = "your-motion-id"
query = '''
query GetMotion($id: String!) {
motion(id: $id) {
id
name
created
updated
tags
assets {
id
filename
}
}
}
'''
variables = {"id": MOTION_ID}
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': query, 'variables': variables}
)
result = response.json()
motion = result["data"]["motion"]
print(f"Motion: {motion['name']}")
print(f"Created: {motion['created']}")
const MOTION_ID = "your-motion-id";
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: `
query GetMotion($id: String!) {
motion(id: $id) {
id
name
created
updated
tags
assets {
id
filename
}
}
}
`,
variables: { id: MOTION_ID },
}),
});
const json = await response.json();
const motion = json.data.motion;
console.log(`Motion: ${motion.name}`);
console.log(`Created: ${motion.created}`);
public async Task<Motion> GetMotionAsync(string motionId)
{
var query = @"
query GetMotion($id: String!) {
motion(id: $id) {
id
name
created
updated
tags
assets {
id
filename
}
}
}";
var request = new
{
query = query,
variables = new { id = motionId }
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<MotionData>>(responseJson);
return result.Data.Motion;
}
Update motion metadata
Update a motion's name.
- Shell
- Python
- TypeScript
- C#
MOTION_ID="your-motion-id"
NEW_NAME="Updated Motion Name"
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{
"query": "mutation UpdateMotion($motion_id: String!, $name: String) { update_motion(motion_id: $motion_id, name: $name) { motion { id name } } }",
"variables": { "motion_id": "'"$MOTION_ID"'", "name": "'"$NEW_NAME"'" }
}'
MOTION_ID = "your-motion-id"
NEW_NAME = "Updated Motion Name"
query = '''
mutation UpdateMotion($motion_id: String!, $name: String) {
update_motion(motion_id: $motion_id, name: $name) {
motion {
id
name
}
}
}
'''
variables = {
"motion_id": MOTION_ID,
"name": NEW_NAME
}
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': query, 'variables': variables}
)
result = response.json()
motion = result["data"]["update_motion"]["motion"]
print(f"Updated motion: {motion['name']}")
const MOTION_ID = "your-motion-id";
const NEW_NAME = "Updated Motion Name";
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: `
mutation UpdateMotion($motion_id: String!, $name: String) {
update_motion(motion_id: $motion_id, name: $name) {
motion {
id
name
}
}
}
`,
variables: {
motion_id: MOTION_ID,
name: NEW_NAME,
},
}),
});
const json = await response.json();
const motion = json.data.update_motion.motion;
console.log(`Updated motion: ${motion.name}`);
public async Task<Motion> UpdateMotionAsync(string motionId, string newName)
{
var query = @"
mutation UpdateMotion($motion_id: String!, $name: String) {
update_motion(motion_id: $motion_id, name: $name) {
motion {
id
name
}
}
}";
var request = new
{
query = query,
variables = new
{
motion_id = motionId,
name = newName
}
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<UpdateMotionData>>(responseJson);
return result.Data.UpdateMotion.Motion;
}
Delete a motion
Delete a motion by setting its deleted flag to true. The motion will be marked as deleted and will not appear in API responses or the UI.
- Shell
- Python
- TypeScript
- C#
MOTION_ID="your-motion-id"
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{
"query": "mutation UpdateMotion($motion_id: String!, $deleted: Boolean) { update_motion(motion_id: $motion_id, deleted: $deleted) { motion { id name deleted } } }",
"variables": { "motion_id": "'"$MOTION_ID"'", "deleted": true }
}'
MOTION_ID = "your-motion-id"
query = '''
mutation UpdateMotion($motion_id: String!, $deleted: Boolean) {
update_motion(motion_id: $motion_id, deleted: $deleted) {
motion {
id
name
deleted
}
}
}
'''
variables = {
"motion_id": MOTION_ID,
"deleted": True
}
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': query, 'variables': variables}
)
result = response.json()
motion = result["data"]["update_motion"]["motion"]
print(f"Motion deleted: {motion['name']}")
const MOTION_ID = "your-motion-id";
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: `
mutation UpdateMotion($motion_id: String!, $deleted: Boolean) {
update_motion(motion_id: $motion_id, deleted: $deleted) {
motion {
id
name
deleted
}
}
}
`,
variables: {
motion_id: MOTION_ID,
deleted: true,
},
}),
});
const json = await response.json();
const motion = json.data.update_motion.motion;
console.log(`Motion deleted: ${motion.name}`);
public async Task<Motion> DeleteMotionAsync(string motionId)
{
var query = @"
mutation UpdateMotion($motion_id: String!, $deleted: Boolean) {
update_motion(motion_id: $motion_id, deleted: $deleted) {
motion {
id
name
deleted
}
}
}";
var request = new
{
query = query,
variables = new
{
motion_id = motionId,
deleted = true
}
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<UpdateMotionData>>(responseJson);
return result.Data.UpdateMotion.Motion;
}
Next steps
- Learn about Text to motion for generating animations
- Explore Video to motion for converting video files
- Check Account and organization for user and organization information
- Check the API reference for complete schema documentation