Skip to main content

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 as motion.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($prompt: String!) { create_text_to_motion(prompt: $prompt) { motion { id name } } }",
"variables": { "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) { character {id name} } }",
"variables": { "file": null, "name": "My Character" }
}' \
-F 'map={ "0": ["variables.file"] }' \
-F '0=@/path/to/character.fbx'

Auto-rig a character

Auto-rigging is handled automatically if the character doesn't have a rig. To disable auto-rigging, set auto_rig: false in the mutation's parameters:

- "... { create_character(file: $file, name: $name) { character {id name} } }",
+ "... { create_character(file: $file, name: $name, auto_rig: false) { character {id name} } }",

Download a character

To download a character in a neutral pose, use the following curl command:

curl -L "https://uthana.com/motion/bundle/<character-id>/character.<fbx,glb>" \
-u <your-api-key>: \
-o output-file-name.<fbx,glb>

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($model: String!, $prompt: String!) { create_text_to_motion(model: $model, prompt: $prompt) { motion { id name } } }",
"variables": {"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?"
exit 1
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.

# If your character doesn't have a rig, it will be auto-rigged automatically
# (this can be disabled by setting the `auto_rig` parameter to false).
OPERATIONS='{"query":"mutation CreateCharacter($file: Upload!, $name: String!) { create_character(file: $file, name: $name) { character { id name } } }","variables":{"file":null,"name":"'$CHARACTER_NAME'"},"operationName":"CreateCharacter"}'
MAP='{"0":["variables.file"]}'

RES="$(curl -s -X POST "https://$HOST/graphql" \
-u $UTHANA_APIKEY: \
-H 'Accept: application/json' \
--form "operations=$OPERATIONS" \
--form "map=$MAP" \
--form "0=@$FILE"
)"
echo $RES

# Check for GraphQL errors
ERROR="$(echo $RES | jq -r '.errors[0].message // empty')"
if [ ! -z "$ERROR" ]; then
echo "Failed to create character: $ERROR"
exit 1
fi

# Extract character ID from successful response
CHARACTER_ID="$(echo $RES | jq -r .data.create_character.character.id)"
if [ "$CHARACTER_ID" = "null" ] || [ -z "$CHARACTER_ID" ]; then
echo "No character ID returned, upload may have failed"
exit 1
fi

echo "Successfully created character with ID: $CHARACTER_ID"

# List existing characters
echo "Listing all 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 } }"}'

# Download the character
curl -L "https://$HOST/motion/bundle/$CHARACTER_ID/character.fbx" -u $UTHANA_APIKEY: -o "$CHARACTER_NAME.fbx"
echo "Downloaded character to: $CHARACTER_NAME.fbx"

# View the character in the Uthana Web UI
echo "View character at: https://$HOST/app/play/$CHARACTER_ID"

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.