GraphQL API | Uthana API Docs
On this page
The Uthana GraphQL API provides a flexible way to query and mutate animation data.
Endpoint
https://uthana.com/graphql
Authentication
Include your API key in the basic Authorization header, base64-encoded with a colon:
AUTH_STRING=$(echo -n "your-api-key:" | base64)
curl -H "Authorization: Basic $AUTH_STRING" \
https://uthana.com/graphql
or in the query string (plaintext):
https://uthana.com/graphql?apikey=your-api-key
Schema
Queries
type Query {
org: Org
user: User
locomotion_styles: [String!]!
motion_download_allowed(character_id: String, motion_id: String): MotionDownloadAllowed
characters: [Character!]
motion(id: String): Motion
motions(character_id: String, app_ids: [String!], methods: [String]): [Motion!]
label_search(query: String, limit: Int = 20): [LabelSearchResult!]
job(job_id: String): Job
jobs(method: String): [Job!]
}
Org
id:StringYour organization's IDname:StringYour organization's namemotion_download_secs_per_month:FloatThe number of seconds of motion downloads per month allocated to the organizationmotion_download_secs_per_month_remaining:FloatThe number of seconds of motion downloads per month remaining for the organizationcharacters_allowed:IntThe maximum number of characters your organization can create per monthcharacters_allowed_remaining:IntThe number of character creation (upload, generate, or rig) slots remaining for the current monthusers:[User]The users in your organization
query Org {
org {
id
name
motion_download_secs_per_month
motion_download_secs_per_month_remaining
characters_allowed
characters_allowed_remaining
users {
id
name
email
email_verified
tz
roles
}
}
}
User
id:StringYour user IDname:StringYour nameemail:StringYour email addressemail_verified:BooleanWhether your email address is verifiedtz:StringYour timezone at last loginroles:[String]Your rolesorg:OrgYour organizationcreated:StringYour account creation timestampupdated:StringYour account last update timestamp
query User {
user {
id
name
email
email_verified
tz
roles
created
updated
org {
id
name
}
}
}
MotionDownloadAllowed
Return value for motion_download_allowed (input ids are the query arguments character_id and motion_id, not fields on this type).
allowed:BooleanWhether the motion download is allowedreason:StringThe reason the motion download is allowed or not
query MotionDownloadAllowed($character_id: String, $motion_id: String) {
motion_download_allowed(character_id: $character_id, motion_id: $motion_id) {
allowed
reason
}
}
Character
id:Stringname:StringCharacter nameorg_id:StringCharacter organization IDassets:[Asset]Character assetscreated:StringCharacter creation timestampupdated:StringCharacter last update timestampdeleted:String|nullCharacter deletion timestamp, ornullif the character has not been deleted
query Character($id: String!) {
character(id: $id) {
id
name
org_id
created
updated
deleted
assets {
id
filename
}
}
}
Characters
[Character]List ofCharacterobjects in your organization
query Characters {
characters {
id
name
# ...etc. (see Character object)
}
}
Motion
id:StringMotion IDname:StringMotion nameorg_id:StringMotion organization IDtags:ObjectMotion tagsassets:[Asset]Motion assetslabels:[Label]Motion labelsrating:MotionRating|nullMotion rating, ornullif the motion has not been ratedfavorite:MotionFavorite|nullMotion favorite, ornullif the motion has not been favoritedcreated:StringMotion creation timestampupdated:StringMotion last update timestampdeleted:String|nullMotion deletion timestamp, ornullif the motion has not been deleted
{ MotionRating }
user_id:StringUser ID who rated the motionscore:IntThe rating score, must be 0 (downvote) or 1 (upvote)created:StringRating creation timestampupdated:StringRating last update timestamp
{ MotionFavorite }
user_id:StringUser ID who favorited the motionmotion_id:StringMotion IDcreated:StringFavorite creation timestampupdated:StringFavorite last update timestamp
query Motion($id: String!) {
motion(id: $id) {
id
name
tags
assets {
id
filename
}
rating {
user_id
score
created
}
favorite {
user_id
created
}
created
updated
deleted
}
}
Motions
[Motion]The motions in your organization
query Motions {
motions {
id
name
# ...etc. (see Motion object)
}
}
Asset
id:Stringuid:StringAsset internal UUIDfilename:StringAsset original filenamemimetype:StringAsset mimetypetype:StringAsset type, typically:bundlesha256:StringAsset SHA-256 hashmetadata:ObjectAsset metadatasize:IntAsset size in bytescreated:StringAsset creation timestampupdated:StringAsset last update timestampdeleted:String|nullAsset deletion timestamp, ornullif the asset has not been deleted
Job
id:Stringstatus:StringJob status, one of:RESERVED,FINISHED,FAILED,READYcreated_at:StringJob creation timestampstarted_at:StringJob start timestampended_at:StringJob end timestampmethod:StringJob motion methodargs:ObjectJob argumentsresult:ObjectJob result
query Job($id: String!) {
job(id: $id) {
id
status
started_at
ended_at
result
}
}
Jobs
[Job]List ofJobobjects
query Jobs {
jobs {
id
# ...etc. (see Job object)
}
}
Mutations
The following mutations are available for creating and modifying data:
create_text_to_motion
Generate a motion from a text prompt.
prompt: The text prompt to generate a motion from.foot_ik: Whether to enable foot IK.
mutation {
create_text_to_motion(
prompt: String!
model: String # "text-to-motion" (default) or "text-to-motion-bucmd"
foot_ik: Boolean = false
) {
motion {
id
name
}
}
}
create_text_to_motion_job
Create an async text-to-motion job using text-to-motion-3.0.
prompt: Text description of the motion to generate.model: Required. Must be "text-to-motion-3.0".character_id: (optional) Character ID to retarget the motion to.length: (optional) Target video length in seconds (integer 4–10). Default:8.rewrite_prompt: (optional) Rewrite the prompt into physical motion direction. Default:true.
mutation {
create_text_to_motion_job(
prompt: String!
model: String!
character_id: String
length: Float
rewrite_prompt: Boolean
) {
job {
id
status
est_processing_time
}
}
}
create_video_to_motion
Generate a motion from a 2D video file.
file: File or URL of the video file to create a motion from.motion_name: The name of the resulting motion.character_id: (optional) Character ID to retarget the motion to.model: (optional) Model to use. Valid values:video-to-motion-2.0(default) — returns a single base motionvideo-to-motion-2.1— includes post-processing refinements; returns refined motion IDsvideo-to-motion-v2— alias forvideo-to-motion-2.0, accepted for backward compatibility
mutation {
create_video_to_motion(
file: Upload!
motion_name: String!
character_id: String
model: String
) {
job {
id
status
}
}
}
create_character
Upload a new character. Auto-rigging is handled automatically if the character doesn't have a rig.
file: File or URL of the character file to create.name: The name of the resulting character.auto_rig: Whether to auto-rig the character. (default:true)
mutation {
create_character(
file: Upload!
name: String!
auto_rig: Boolean = true # optional, defaults to true
root: String # internal use only
) {
character {
id
name
}
}
}
create_enhanced_stitched_motion
Stitch two motions together with detailed pose and spatial data for high-quality transitions. This is the primary stitching API.
stitch_input:MotionStitchInput!— Required input containingcharacter_id,prefix, andsuffix.
MotionStitchInput:
character_id(String!): Character ID both motions are retargeted to.prefix(StitchParamsInput!): Parameters for the leading motion.suffix(StitchParamsInput!): Parameters for the trailing motion.
StitchParamsInput (each of prefix and suffix):
| Field | Type | Required | Description |
|---|---|---|---|
prompt |
String |
Yes | Text hint for the transition. Use "" for deterministic baseline. |
motion_id |
String |
Yes | Motion ID. |
stitch_loop |
Boolean |
Yes | Use false for standard stitching. |
stitch_duration |
Float |
Yes | Transition duration in seconds (0.1–3.0). Recommended: 2.0. |
motion_duration |
Float |
Yes | Total motion duration in seconds. |
motion_lower_trim_time |
Float |
Yes | Lower trim time in seconds. |
motion_upper_trim_time |
Float |
Yes | Upper trim time in seconds. For prefix, use motion_duration - 0.051 to avoid exact-end sampling. |
motion_lower_trim_fraction |
Float |
Yes | Lower trim as fraction [0.0–1.0]. |
motion_upper_trim_fraction |
Float |
Yes | Upper trim as fraction [0.0–1.0]. |
root_node_world_pos |
Vector3Input |
Yes | Root node world position {x, y, z}. |
root_node_world_rot |
QuaternionInput |
Yes | Root node world rotation {x, y, z, w}. |
at_zero_time |
PelvisStateInput |
Yes | Pelvis state at time 0. |
at_lower_trim_time |
PelvisStateInput |
Yes | Pelvis state at lower trim time. |
at_upper_trim_time |
PelvisStateInput |
Yes | Pelvis state at upper trim time. |
PelvisStateInput:pelvis_world_pos (Vector3), pelvis_world_rot (Quaternion), hips_forward_facing_world_yaw (Float, radians).
mutation CreateEnhancedStitchedMotion($stitch_input: MotionStitchInput!) {
create_enhanced_stitched_motion(stitch_input: $stitch_input) {
motion {
id
name
}
}
}
}
create_stitched_motion
Legacy stitch endpoint. Prefer create_enhanced_stitched_motion for better quality and control.
leading_motion_id: The ID of the leading motion.trailing_motion_id: The ID of the trailing motion.duration: The duration in seconds of the transition between the two motions. (default:0.5)
mutation {
create_stitched_motion(
leading_motion_id: String!
trailing_motion_id: String!
duration: Float = 0.5
) {
motion {
id
name
}
}
}
}
trim_and_loop_motion
Trim and optionally loop a motion.
motion_id: The ID of the motion to trim and loop.motion_name: The name of the resulting motion.start/end: The start and end times of the trimmed motion, as a decimal value between 0 (start of motion) and 1 (end of motion).loop: Create a looped motion. Valid values:- (empty): No loop
- "cyclical": Moves the character back to its starting position before looping again (in-place)
- "progressive": Loops continuously starting from wherever the previous loop ended (locomotion)
mutation {
trim_and_loop_motion(
motion_id: String!
motion_name: String
start: Float!
end: Float!
loop: "progressive" | "cyclical"
) {
motion {
id
name
}
}
}
rate_motion
Rate a motion or label.
motion_id: The ID of the motion to rate.label_id: The ID of the label to rate.score: The score to rate the motion or label with. Must be one of:0: Downvote1: Upvote
mutation {
rate_motion(
label_id: String
motion_id: String!
score: Int! # must be 0 or 1
) {
rating {
score
user_id
}
}
}
update_motion
Update a motion's name and/or deletion status.
motion_id: The ID of the motion to update.name: The new name of the motion.deleted: Marks the motion as deleted. While not permanently removed, it will not appear in API responses or the UI.
mutation {
update_motion(
deleted: Boolean
motion_id: String!
name: String
) {
motion {
id
name
deleted
}
}
}
}
## Example queries
### Get motion details
```graphql
query GetMotion($id: String!) {
motion(id: $id) {
id
name
created
updated
org_id
priority
tags
motion_viewer
training
assets {
id
filename
}
labels {
id
description
start
end
}
rating {
score
user_id
}
}
}
List motions
query ListMotions {
motions(methods: ["TextToMotion"]) {
id
name
org_id
created
assets {
filename
}
}
}
List locomotion styles
Returns every style_id accepted by create_locomotion. See the Locomotion capability for end-to-end usage.
query ListLocomotionStyles {
locomotion_styles
}
List locomotion motions
Filter the motions list to clips created with the locomotion generator:
query ListLocomotionMotions {
motions(methods: ["Locomotion"]) {
id
name
}
}
List jobs
query ListJobs {
jobs(method: "VideoToMotion") {
id
status
est_processing_time
}
}
Generate locomotion
mutation GenerateLocomotion {
create_locomotion(
character_id: "your-character-id"
strides: 2
move_speed: 1.3
style_id: "neutral_male_a"
travel_angle: 0
) {
motion {
id
name
}
}
}
}
Generate motion from text (text-to-motion)
mutation GenerateTextMotion {
create_text_to_motion(prompt: "walk casually", model: "text-to-motion", foot_ik: true) {
motion {
id
name
}
}
}
}