cURL examples
Uthana provides a powerful GraphQL API for all programmatic access to animation, character, and motion data. There is no REST API—all features are available via GraphQL.
This guide shows how to use curl
to interact with the GraphQL API, including authentication, queries, mutations, and file uploads.
Endpoint
https://uthana.com/graphql
Authentication
Use the -u
flag to authenticate with your API key:
curl -s "https://uthana.com/graphql" \
-u YOUR_API_KEY: \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data-raw '{<your-query-or-mutation>}'
or 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 as a query parameter:
curl 'https://uthana.com/graphql?api_key=<your-api-key>'
Making GraphQL queries with cURL
Send a POST request with your query or mutation in the JSON body:
Get motion details
curl -X POST https://uthana.com/graphql \
-u <your-api-key>: \
-H "Content-Type: application/json" \
-d '{
"query": "query GetMotion($id: String!) { motion(id: $id) { id name created } }",
"variables": { "id": "m3G3XSJrjEJH" }
}'
Get org info
curl -X POST https://uthana.com/graphql \
-u <your-api-key>: \
-H "Content-Type: application/json" \
-d '{"query":"{ org { id name } }"}'
Download a motion
To download a motion file (fbx
or glb
), use the following endpoint:
https://uthana.com/motion/file/motion_viewer/{character_id}/{motion_id}/{type}/{character_id}-{motion_id}.{type}
- app_id: The application context (always use
motion_viewer
) - character_id: The character's ID
- motion_id: The motion's ID
- type: The file type/format (
fbx
,glb
)
Download a motion as FBX
curl -L "https://uthana.com/motion/file/motion_viewer/<character-id>/<motion-id>/fbx/<character-id>-<motion-id>.fbx" \
-u <your-api-key>: \
-o motion.fbx
- Replace
<character-id>
and<motion-id>
with your actual IDs. - The
-L
flag follows redirects (if any). - The
-o
flag saves the file asmotion.fbx
.
Generate motion from text (text-to-motion)
curl -X POST https://uthana.com/graphql \
-u <your-api-key>: \
-H "Content-Type: application/json" \
-d '{
"query": "mutation CreateTextToMotion($character_id: String!, $prompt: String!) { create_text_to_motion(character_id: $character_id, prompt: $prompt) { ok job_id motion { id name } } }",
"variables": { "character_id": "<your-character-id>", "prompt": "walk casually" }
}'
Upload files (multipart/form-data)
To upload files (e.g., for create_character
or create_video_to_motion
), use the GraphQL multipart request spec:
Upload a character
curl -X POST https://uthana.com/graphql \
-u <your-api-key>: \
-F 'operations={
"query": "mutation ($file: Upload!, $name: String!) { create_character(file: $file, name: $name) { ok character_id } }",
"variables": { "file": null, "name": "My Character" }
}' \
-F 'map={ "0": ["variables.file"] }' \
-F '0=@/path/to/character.fbx'
Auto-rig a character
If you have a character that doesn't have a rig, you can auto-rig it using the create_rig
mutation. You will need to upload your un-rigged character file first, and then call create_rig
with the character ID.
The create_character
upload will fail, but will return a character ID in the response:
{
"data": {
"create_character": {
"ok": false,
"character_id": "<your-character-id>"
}
}
}
You can then use the character ID to call create_rig
:
curl -X POST https://$HOST/graphql \
-u "$UTHANA_APIKEY:" \
-H "Content-Type: application/json" \
-d '{
"query":"mutation AutoRigCharacter { create_rig(character_id: \"<your-character-id>\", filename: \"<your-filename>\") { ok character_id error } }"
}'
Complete examples
Create a text-to-motion job and download the motion as FBX and GLB
#!/bin/bash
set -eo pipefail
# Set your API key and character ID for easy reuse
UTHANA_APIKEY=<your-api-key>
HOST="uthana.com"
CHARACTER_ID=<your-character-id>
# Create a text-to-motion job
MUTATION='{
"query": "mutation CreateTextToMotion($character_id: String!, $model: String!, $prompt: String!) {create_text_to_motion(character_id: $character_id, model: $model, prompt: $prompt) {motion{id name}}}",
"variables": {"character_id": "'$CHARACTER_ID'", "model": "text-to-motion", "prompt": "a person runs in a circle"},
"operationName": "CreateTextToMotion"
}'
RES="$(curl -s "https://$HOST/graphql" \
-u $UTHANA_APIKEY: \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data-raw "$MUTATION"
)"
echo $RES
MOTION_ID="$(echo $RES | jq -r .data.create_text_to_motion.motion.id)"
echo "motion_id: $MOTION_ID"
# Download the motion as FBX
curl -s --remote-name "https://$HOST/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/fbx/$CHARACTER_ID-$MOTION_ID.fbx" \
-u $UTHANA_APIKEY:
# Download the motion as GLB
curl -s --remote-name "https://$HOST/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/glb/$CHARACTER_ID-$MOTION_ID.glb" \
-u $UTHANA_APIKEY:
# View the motion in the Uthana Web UI
echo "https://$HOST/app/play/$CHARACTER_ID/$MOTION_ID"
Upload a character
#!/bin/bash
set -eo pipefail
# Set your API key and character ID for easy reuse
UTHANA_APIKEY=<your-api-key>
HOST="uthana.com"
CHARACTER_NAME="My Character"
# Your existing fbx or glb file.
FILE="$1"
if [ ! -e "$FILE" ]; then
echo "$FILE is missing?"
fi
# Upload & create character
# In this request we auto-retarget to our internal skeleton, so the first time
# we see a skeleton this might be slow, but if you use the same skeleton again,
# it should be faster. Please contact us if you have issues during this process.
# You can also include a motion prompt to generate a motion from text in the same
# request, or omit `prompt` to just create the character.
RES="$(curl -s -X POST "https://$HOST/graphql" \
-u $UTHANA_APIKEY: \
-H 'Accept: application/json' \
--form $'operations={"query":"mutation CreateCharacter($file: Upload!, $name: String!) {\\n create_character(file: $file, name: $name) {character_id motion_id}\\n}","variables":{"file":null,"name":"'"$CHARACTER_NAME"'","prompt":"a person runs in a circle"},"operationName":"CreateCharacter"}' \
--form 'map={ "nFile": ["variables.file"] }' \
--form nFile=@$FILE
)"
echo $RES
CHARACTER_ID="$(echo $RES | jq -r .data.create_character.character_id)"
MOTION_ID="$(echo $RES | jq -r .data.create_character.motion_id)"
# List existing characters
curl -s "https://$HOST/graphql" \
-u $UTHANA_APIKEY: \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data-raw $'{"query":"{ characters { id name created } }"}'
# View the character in the Uthana Web UI
echo "https://$HOST/app/play/$CHARACTER_ID/$MOTION_ID"
Auto-rig a character
#!/bin/bash
set -eo pipefail
# Set your API key and character ID for easy reuse
UTHANA_APIKEY=<your-api-key>
HOST="uthana.com"
CHARACTER_NAME="My Character"
# Your existing fbx or glb file.
FILE="$1"
if [ ! -e "$FILE" ]; then
echo "$FILE is missing?"
fi
# Upload & create character
RES="$(curl -s -X POST "https://$HOST/graphql" \
-u $UTHANA_APIKEY: \
-H 'Accept: application/json' \
--form $'operations={"query":"mutation CreateCharacter($file: Upload!, $name: String!) {\\n create_character(file: $file, name: $name) {character_id motion_id}\\n}","variables":{"file":null,"name":"'"$CHARACTER_NAME"'"},"operationName":"CreateCharacter"}' \
--form 'map={ "nFile": ["variables.file"] }' \
--form nFile=@$FILE
)"
echo $RES
CHARACTER_ID="$(echo $RES | jq -r .data.create_character.character_id)"
OK="$(echo $RES | jq -r .data.create_character.ok)"
ERROR="$(echo $RES | jq -r .errors[0].message)"
FRIENDLY_ERROR="$(echo $RES | jq -r .data.create_character.error)"
# If the error contains the "No skeleton found" message, we can try to auto-rig it:
if [[ "$ERROR" == *"no rig found in scene"* ]] || [[ "$FRIENDLY_ERROR" == *"No rig found in scene"* ]]; then
echo "No rig found, trying to auto-rig..."
# Fail (exit 1) if there is no character ID
if [ "$CHARACTER_ID" = "null" ]; then
echo "No character ID found, cannot auto-rig"
exit 1
fi
MUTATION='{
"query": "mutation AutoRigCharacter { create_rig(character_id: \"'$CHARACTER_ID'\", filename: \"'$FILE'\") { ok character_id error } }"
}'
RES="$(curl -s -X POST "https://$HOST/graphql" \
-u $UTHANA_APIKEY: \
-H "Content-Type: application/json" \
-d "$MUTATION")"
echo $RES
OK="$(echo $RES | jq -r .data.create_rig.ok)"
ERROR="$(echo $RES | jq -r .errors[0].message)"
if [ "$OK" = "false" ]; then
echo "Failed to auto-rig character, error is: $ERROR"
exit 1
fi
CHARACTER_ID="$(echo $RES | jq -r .data.create_rig.character_id)"
echo "Auto-rigged character with ID: $CHARACTER_ID"
else
echo "Failed to create character, error is: $ERROR"
exit 1
fi
Error handling
Errors are returned in the errors
array of the GraphQL response:
{
"errors": [
{
"message": "Rate limit exceeded",
"extensions": {
"code": "RATE_LIMIT_EXCEEDED",
"resetAt": "2025-01-01T00:00:00Z"
}
}
]
}
Rate limits
Rate limits are enforced on a per-organization basis. Contact the Uthana team to discuss your needs.