On this page

The Uthana GraphQL API provides a flexible way to query and mutate animation data.

## Endpoint

```text
https://uthana.com/graphql
```

## Authentication

Include your API key in the basic `Authorization` header, base64-encoded with a colon:

```bash
AUTH_STRING=$(echo -n "your-api-key:" | base64)
curl -H "Authorization: Basic $AUTH_STRING" \
     https://uthana.com/graphql
```

or in the query string (plaintext):

```bash
https://uthana.com/graphql?apikey=your-api-key
```

## Schema

### Queries

```graphql
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`: `String` Your organization's ID
- `name`: `String` Your organization's name
- `motion_download_secs_per_month`: `Float` The number of seconds of motion downloads per month allocated to the organization
- `motion_download_secs_per_month_remaining`: `Float` The number of seconds of motion downloads per month remaining for the organization
- `characters_allowed`: `Int` The maximum number of characters your organization can create per month
- `characters_allowed_remaining`: `Int` The number of character creation (upload, generate, or rig) slots remaining for the current month
- `users`: `[User]` The users in your organization

```graphql
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`: `String` Your user ID
- `name`: `String` Your name
- `email`: `String` Your email address
- `email_verified`: `Boolean` Whether your email address is verified
- `tz`: `String` Your timezone at last login
- `roles`: `[String]` Your roles
- `org`: `Org` Your organization
- `created`: `String` Your account creation timestamp
- `updated`: `String` Your account last update timestamp

```graphql
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`: `Boolean` Whether the motion download is allowed
- `reason`: `String` The reason the motion download is allowed or not

```graphql
query MotionDownloadAllowed($character_id: String, $motion_id: String) {
  motion_download_allowed(character_id: $character_id, motion_id: $motion_id) {
    allowed
    reason
  }
}
```

#### Character

- `id`: `String`
- `name`: `String` Character name
- `org_id`: `String` Character organization ID
- `assets`: `[Asset]` Character assets
- `created`: `String` Character creation timestamp
- `updated`: `String` Character last update timestamp
- `deleted`: `String` \| `null` Character deletion timestamp, or `null` if the character has not been deleted

```graphql
query Character($id: String!) {
  character(id: $id) {
    id
    name
    org_id
    created
    updated
    deleted
    assets {
      id
      filename
    }
  }
}
```

#### Characters

- `[Character]` List of `Character` objects in your organization

```graphql
query Characters {
  characters {
    id
    name
    # ...etc. (see Character object)
  }
}
```

#### Motion

- `id`: `String` Motion ID
- `name`: `String` Motion name
- `org_id`: `String` Motion organization ID
- `tags`: `Object` Motion tags
- `assets`: `[Asset]` Motion assets
- `labels`: `[Label]` Motion labels
- `rating`: `MotionRating` \| `null` Motion rating, or `null` if the motion has not been rated
- `favorite`: `MotionFavorite` \| `null` Motion favorite, or `null` if the motion has not been favorited
- `created`: `String` Motion creation timestamp
- `updated`: `String` Motion last update timestamp
- `deleted`: `String` \| `null` Motion deletion timestamp, or `null` if the motion has not been deleted

##### { MotionRating }

- `user_id`: `String` User ID who rated the motion
- `score`: `Int` The rating score, must be 0 (downvote) or 1 (upvote)
- `created`: `String` Rating creation timestamp
- `updated`: `String` Rating last update timestamp

##### { MotionFavorite }

- `user_id`: `String` User ID who favorited the motion
- `motion_id`: `String` Motion ID
- `created`: `String` Favorite creation timestamp
- `updated`: `String` Favorite last update timestamp

```graphql
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

```graphql
query Motions {
  motions {
    id
    name
    # ...etc. (see Motion object)
  }
}
```

#### Asset

- `id`: `String`
- `uid`: `String` Asset internal UUID
- `filename`: `String` Asset original filename
- `mimetype`: `String` Asset mimetype
- `type`: `String` Asset type, typically: `bundle`
- `sha256`: `String` Asset SHA-256 hash
- `metadata`: `Object` Asset metadata
- `size`: `Int` Asset size in bytes
- `created`: `String` Asset creation timestamp
- `updated`: `String` Asset last update timestamp
- `deleted`: `String` \| `null` Asset deletion timestamp, or `null` if the asset has not been deleted

#### Job

- `id`: `String`
- `status`: `String` Job status, one of: `RESERVED`, `FINISHED`, `FAILED`, `READY`
- `created_at`: `String` Job creation timestamp
- `started_at`: `String` Job start timestamp
- `ended_at`: `String` Job end timestamp
- `method`: `String` Job motion method
- `args`: `Object` Job arguments
- `result`: `Object` Job result

```graphql
query Job($id: String!) {
  job(id: $id) {
    id
    status
    started_at
    ended_at
    result
  }
}
```

#### Jobs

- `[Job]` List of `Job` objects

```graphql
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.

```graphql
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`.

```graphql
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 motion
  - `video-to-motion-2.1` — includes post-processing refinements; returns refined motion IDs
  - `video-to-motion-v2` — alias for `video-to-motion-2.0`, accepted for backward compatibility

```graphql
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`)

```graphql
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 containing `character_id`, `prefix`, and `suffix`.

**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).

```graphql
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`)

```graphql
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)

```graphql
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`: Downvote
  - `1`: Upvote

```graphql
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.

```graphql
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

```graphql
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](/content/docs/api/capabilities/locomotion/index.html) capability for end-to-end usage.

```graphql
query ListLocomotionStyles {
  locomotion_styles
}
```

### List locomotion motions

Filter the motions list to clips created with the locomotion generator:

```graphql
query ListLocomotionMotions {
  motions(methods: ["Locomotion"]) {
    id
    name
  }
}
```

### List jobs

```graphql
query ListJobs {
  jobs(method: "VideoToMotion") {
    id
    status
    est_processing_time
  }
}
```

### Generate locomotion

```graphql
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)

```graphql
mutation GenerateTextMotion {
  create_text_to_motion(prompt: "walk casually", model: "text-to-motion", foot_ik: true) {
    motion {
      id
      name
    }
  }
}
}
```
