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
- React
- 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 asyncio
from uthana import Uthana
client = Uthana("{{apiKey}}")
async def main():
characters = await client.characters.list()
for c in characters[:10]: # Limit to 10 characters
print(c.get("id"), c.get("name"))
asyncio.run(main())
import { UthanaClient } from "@uthana/client";
const client = new UthanaClient(process.env.UTHANA_API_KEY!);
const characters = await client.characters.list();
const limitedCharacters = characters.slice(0, 10);
limitedCharacters.forEach((c) => console.log(c.id, c.name));
import { useUthanaCharacters } from "@uthana/react";
function CharacterList() {
const { characters } = useUthanaCharacters();
const limitedCharacters = characters?.slice(0, 10);
return <ul>{limitedCharacters?.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;
}
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.Take(10))
{
Console.WriteLine($"{character.Name} ({character.Id})");
}
See the Auto-rig / add a character guide for uploading characters.
Rename a character
- Shell
- Python
- TypeScript
- React
- 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!, $name: String) { update_character(character_id: $character_id, name: $name) { character { id name } } }",
"variables": { "character_id": "'"$CHARACTER_ID"'", "name": "New name" }
}'
async def main():
await client.characters.rename("your-character-id", "New name")
asyncio.run(main())
await client.characters.rename("your-character-id", "New name");
import { useUthanaRenameCharacter } from "@uthana/react";
function RenameButton({ characterId }: { characterId: string }) {
const rename = useUthanaRenameCharacter();
return <button onClick={() => rename.mutate({ character_id: characterId, name: "New name" })}>Rename</button>;
}
var query = @"
mutation UpdateCharacter($character_id: String!, $name: String) {
update_character(character_id: $character_id, name: $name) {
character { id name }
}
}";
var request = new { query = query, variables = new { character_id = "your-character-id", name = "New name" } };
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
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
- React
- 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 }
}'
async def main():
await client.characters.delete("your-character-id")
asyncio.run(main())
await client.characters.delete("your-character-id");
import { useUthanaDeleteCharacter } from "@uthana/react";
function DeleteButton({ characterId }: { characterId: string }) {
const remove = useUthanaDeleteCharacter();
return <button onClick={() => remove.mutate({ character_id: characterId })}>Delete</button>;
}
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
- React
- 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 } } }"
}'
async def main():
motions = await client.motions.list()
for m in motions[:10]: # Limit to 10 motions
print(m.get("id"), m.get("name"))
asyncio.run(main())
const motions = await client.motions.list();
const limitedMotions = motions.slice(0, 10);
limitedMotions.forEach((m) => console.log(m.id, m.name));
import { useUthanaMotions } from "@uthana/react";
function MotionList() {
const { motions } = useUthanaMotions();
const limitedMotions = motions?.slice(0, 10);
return <ul>{limitedMotions?.map((m) => <li key={m.id}>{m.name}</li>)}</ul>;
}
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
- React
- 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"'" }
}'
async def main():
motion = await client.motions.get("your-motion-id")
print(motion.get("name"), motion.get("created"))
asyncio.run(main())
const motion = await client.motions.get("your-motion-id");
console.log(motion.name, motion.created);
import { useUthanaMotion } from "@uthana/react";
function MotionDetail({ motionId }: { motionId: string }) {
const { motion } = useUthanaMotion(motionId);
return <div>{motion?.name}</div>;
}
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;
}
Rename a motion
- Shell
- Python
- TypeScript
- React
- 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"'" }
}'
async def main():
await client.motions.rename("your-motion-id", "Updated motion name")
asyncio.run(main())
await client.motions.rename("your-motion-id", "Updated motion name");
const client = useUthanaClient();
await client.motions.rename(motionId, "Updated 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
- React
- 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 }
}'
async def main():
await client.motions.delete("your-motion-id")
asyncio.run(main())
await client.motions.delete("your-motion-id");
const client = useUthanaClient();
await client.motions.delete(motionId);
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;
}
Favorite a motion
- Shell
- Python
- TypeScript
- React
- 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!, $favorite: Boolean) { update_motion(motion_id: $motion_id, favorite: $favorite) { motion { id name } } }",
"variables": { "motion_id": "'"$MOTION_ID"'", "favorite": true }
}'
async def main():
await client.motions.favorite("your-motion-id", True)
# Unfavorite:
await client.motions.favorite("your-motion-id", False)
asyncio.run(main())
await client.motions.favorite("your-motion-id", true);
// Unfavorite:
await client.motions.favorite("your-motion-id", false);
const client = useUthanaClient();
await client.motions.favorite(motionId, true);
var query = @"
mutation UpdateMotion($motion_id: String!, $favorite: Boolean) {
update_motion(motion_id: $motion_id, favorite: $favorite) {
motion { id name }
}
}";
var request = new { query = query, variables = new { motion_id = "your-motion-id", favorite = true } };
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
Rate a motion
Rate a motion with a thumbs up or thumbs down.
- Shell
- Python
- TypeScript
- React
- C#
MOTION_ID="your-motion-id"
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{
"query": "mutation RateMotion($motion_id: String!, $score: Int!) { rate_motion(motion_id: $motion_id, score: $score) { motion { id } } }",
"variables": { "motion_id": "'"$MOTION_ID"'", "score": 1 }
}'
async def main():
# 1 = thumbs up, 0 = thumbs down
await client.motions.rate("your-motion-id", 1)
asyncio.run(main())
// 1 = thumbs up, 0 = thumbs down
await client.motions.rate("your-motion-id", 1);
import { useUthanaRateMotion } from "@uthana/react";
function RateButtons({ motionId }: { motionId: string }) {
const rate = useUthanaRateMotion();
return (
<div>
<button onClick={() => rate.mutate({ motion_id: motionId, score: 1 })}>👍</button>
<button onClick={() => rate.mutate({ motion_id: motionId, score: 0 })}>👎</button>
</div>
);
}
var query = @"
mutation RateMotion($motion_id: String!, $score: Int!) {
rate_motion(motion_id: $motion_id, score: $score) {
motion { id }
}
}";
var request = new { query = query, variables = new { motion_id = "your-motion-id", score = 1 } };
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
Next steps
- Learn about Text to motion for generating animations
- Learn about Locomotion for controllable directional movement
- 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