Welcome to MMAI
MMAI turns ideas into usable 3D character motion — for creators in the browser and for agents on the same stack. Describe the performance, direct pose and path when the shot has to land, then preview and export. This guide covers the first motion through full API integration.
Overview
MMAI uses a state-of-the-art motion diffusion model to generate full-body 3D animations from a single text prompt. Describe any human movement — from a subtle wave to a complex martial arts combo — and the AI produces a ready-to-use animation file within seconds.
Your First Motion
- 1
Open the Dashboard
Navigate to magicmotion.ai/dashboard. Constraint tools sit above the timeline. The Pose tool reveals Pose from image.
- 2
Lock a pose
Drop a photo, or pose the rig and add a pose key. Optional: draw a path or drop a mark so travel is directed too.
- 3
Describe the action
Prompt still drives style. Example: "person walks to the mark, then holds this pose with confidence". Keep the first clip around 3 seconds.
- 4
Generate the shot
Any pose, path, or mark directs generation (2× credits). Describe-only stays at 1 credit per second.
- 5
Download your file
Export FBX from the panel. From My Motions you can re-download any previous generation, including saved constraints.
Constrained Motion
Constrained motion lets you combine a text prompt with spatial control on the 3D stage. Your prompt still drives style and action — constraints tell the system where the character should be and how specific body parts should move at key moments.
Three authoring tools
Click the studio floor to pin the character root at a specific frame. Use the timeline to scrub and add more stops — great for turns, staging marks, and hold positions.
Waypoints appear as glowing markers on the ground and timeline.
Draw a spline on the floor to define how the character travels through the scene. Combine with your prompt — e.g. "walks confidently" along the path you sketched.
Switch back to waypoints anytime; both share the same root track.
Keyframe full-body poses or individual hands and feet. Pose the character with the on-screen controls, then add a key at the current frame. Ghost previews show saved poses on the timeline.
Full-body keys lock the entire posture; end-effector keys target left/right hand or foot independently.
Quick workflow
- 1
Open the Create tab
In the dashboard, make sure you are on Create. The 3D stage loads with constraint tools in the toolbar above the timeline.
- 2
Write your prompt
Describe the action as you normally would — constrained motion still needs a clear prompt for style and intent.
- 3
Author constraints
Select a tool (waypoint, path, or pose key), click or pose on the stage, and place keys on the timeline. Scrub playback to preview ghost overlays.
- 4
Generate
Click Generate. The credit estimate updates automatically when constraints are present. Results preview in the stage like any other motion.
- 5
Save or export
Download FBX from the panel or find the generation later in My Motions. Constraint data is stored with the generation for reload.
Understanding Credits
Credits are consumed at 1 credit per second of animation for standard text-to-motion jobs. A 5-second clip costs 5 credits; a 10-second clip costs 10. Constrained motion jobs use a 2× multiplier by default (10 credits for a 5-second constrained clip). The REST API uses this same pool. You do not need a subscription — sign up, add credits (or start a plan for a monthly allotment), and generate a key.
| Plan | Monthly Credits | API + agents |
|---|---|---|
| Starter Magic | 300 | Credits |
| Creator MagicPopular | 1,000 | Credits |
| Pro Magic | 10,000 | Credits |
One-time credit bundles work without a plan. Agents meter the same credits as the studio.
Timeline Editor
Available on Creator Magic and Pro Magic plans, the Timeline Editor lets you sequence and blend multiple motion clips into a single animation.
Exporting Animations
Generated animations are delivered as FBX files. The animation_url in every API response is a signed URL to download the FBX directly.
FBXUnreal Engine, Unity, Blender, Maya, 3ds Max
Primary output format. Signed URL expires after 1 hour.
GLBWeb (Three.js, Babylon.js), AR/VR, Sketchfab
Available as an additional export from the dashboard.
BVHBlender, MotionBuilder, iClone, Cascadeur
Raw motion capture format. Available as an additional export from the dashboard.
Custom Characters
Bring Your Own Character (BYOC) is available on Creator Magic and above. Import any humanoid character and MMAI's smart retargeting applies generated animations to your skeleton automatically.
Learn by Watching
Step-by-step video guides for every skill level.
Getting Started
MMAI in 5 Minutes
A rapid tour of the dashboard — prompts, parameters, generation, and your first download.
Understanding the Dashboard
Deep-dive into every control: the 3D stage, control panel, credit counter, and more.
Timeline Walkthrough
Timeline Editor Basics
Add clips, set transitions, and preview a blended multi-motion sequence in real time.
Complex Motion Sequences
Build a 30-second showcase by chaining idle, walk, jump, and land animations.
Character Import
Importing from Meshy & Tripo
Export a character from Meshy AI or Tripo, import into MMAI, and apply a motion in minutes.
Blender to MMAI Workflow
Prepare a Blender character for BYOC — correct T-pose, clean armature, and export settings.
API Integration
API Quick Start in JavaScript
Call the generate-motion endpoint, handle the response, and save the animation URL in Node.js.
Async Polling Pattern
Use async_mode to kick off long generations and poll job-status until completion.
Integrate in Minutes
The MMAI REST API lets you generate animations programmatically. No subscription required — an account, credits, and an API key. Agents spend the same credit pool as the studio.
Get Your API Key
- 1Sign up at magicmotion.ai
- 2Go to your Account page → API tab
- 3Generate and copy your API key
Authentication
All API requests require a Bearer token in the Authorization header.
Authorization: Bearer your_api_key_hereYou can also send X-API-Key: your_api_key_here. Use base URL https://www.magicmotion.ai so Bearer auth is not dropped on redirects.
Verify your key at any time without using credits:
curl -X GET https://www.magicmotion.ai/api/test-api-key \
-H "Authorization: Bearer your_api_key_here"First Request
curl -X POST https://www.magicmotion.ai/api/generate-motion \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12
}'const generateAnimation = async (prompt, options = {}) => {
const response = await fetch('https://www.magicmotion.ai/api/generate-motion', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.MAGIC_MOTION_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt,
length: options.length ?? 5,
guidance_scale: options.guidance_scale ?? 4,
...options,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`MMAI API Error: ${error.message}`);
}
return response.json();
};
const result = await generateAnimation('jumping with joy', {
length: 6,
guidance_scale: 10,
});
console.log('Animation URL:', result.animation_url);
console.log('Credits remaining:', result.metadata.credits_remaining);import requests
import os
def generate_animation(prompt, length=5, guidance_scale=4, seed=None):
headers = {
'Authorization': f'Bearer {os.getenv("MAGIC_MOTION_API_KEY")}',
'Content-Type': 'application/json',
}
data = {'prompt': prompt, 'length': length, 'guidance_scale': guidance_scale}
if seed:
data['seed'] = seed
response = requests.post(
'https://www.magicmotion.ai/api/generate-motion',
headers=headers,
json=data,
)
response.raise_for_status()
return response.json()
result = generate_animation('running in place', length=7, guidance_scale=8)
print(f"Animation URL: {result['animation_url']}")
print(f"Credits remaining: {result['metadata']['credits_remaining']}")Handling Responses
A successful synchronous response (200 OK):
{
"success": true,
"job_id": "run_abc123def456",
"animation_url": "https://storage.supabase.co/...",
"metadata": {
"prompt": "A Person dancing with excitement",
"original_prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12,
"seed": "1234567",
"credits_used": 8,
"credits_remaining": 492
}
}animation_url is a signed URL that expires after 1 hour. Download the file promptly and store it on your own infrastructure.Advanced Usage
Scale your integration with constrained motion, async generation, and intelligent polling.
Constrained Motion API
Send a constraints array with your generation request to enable spatial control. You do not need to set engine manually — non-empty constraints automatically route to constrained motion.
| Request | Engine | Credits |
|---|---|---|
| No constraints (or empty array) | Standard text-to-motion | length × 1 |
| Non-empty constraints array | Constrained motion | length × 2 (default) |
Constraint object types
| type | Purpose |
|---|---|
root2d | Ground-plane waypoints or paths — smooth_root_2d, frame_indices, optional global_root_heading. |
fullbody | Full-body pose keyframes — local_joints_rot, root_positions, frame_indices. |
end-effector | Hand or foot targets — joint_names, local_joints_rot, frame_indices. |
curl -X POST https://www.magicmotion.ai/api/generate-motion \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A person walks forward confidently",
"length": 5,
"async_mode": true,
"constraints": [{
"type": "root2d",
"frame_indices": [0, 60],
"smooth_root_2d": [[0, 0], [1.5, 0]],
"global_root_heading": [[1, 0], [1, 0]]
}]
}'generation_engine and metadata.poll_url. When polling constrained jobs, prefer the returned poll_url — it includes the correct engine routing before generation metadata is written.curl -X POST https://www.magicmotion.ai/api/kimodo/validate-constraints \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"constraints":[{"type":"root2d","frame_indices":[0],"smooth_root_2d":[[0,0]]}]}'Async Mode
By default the API waits for generation to complete before responding. For longer clips or batch pipelines, pass async_mode: true to receive a job_id immediately (202 Accepted) without holding the connection open.
# Start generation — returns immediately
curl -X POST https://www.magicmotion.ai/api/generate-motion \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12,
"async_mode": true
}'
# Response (202 Accepted)
# {
# "success": true,
# "job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
# "status": "IN_PROGRESS",
# "message": "Generation started. Poll /api/job-status/{job_id} for updates.",
# "metadata": { ..., "estimated_time_seconds": 48 }
# }Polling for Status
Poll GET /api/job-status/{job_id} until the status is COMPLETED or FAILED.
async function waitForAnimation(jobId, apiKey) {
const POLL_MS = 3000;
const MAX_MS = 10 * 60 * 1000; // 10 minutes
const start = Date.now();
while (Date.now() - start < MAX_MS) {
await new Promise((r) => setTimeout(r, POLL_MS));
const res = await fetch(
`https://www.magicmotion.ai/api/job-status/${jobId}`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
);
const data = await res.json();
if (data.status === 'COMPLETED') return data.animation_url;
if (data.status === 'FAILED')
throw new Error(`Generation failed: ${data.message}`);
// IN_QUEUE or IN_PROGRESS — keep polling
}
throw new Error('Timed out waiting for animation');
}Rate Limits & Best Practices
| Limit | Value |
|---|---|
| Credit cost | 1 credit per second of animation |
| Max animation length | 10 seconds |
| Max generation time | 10 minutes |
| Creator Magic credits | 1,000 / month |
| Pro Magic credits | 10,000 / month |
- Use async_mode for all production jobs to avoid holding HTTP connections.
- Download and cache animation URLs immediately — signed URLs expire after 1 hour.
- Pin the seed parameter when you need reproducible outputs across runs.
- Check credits_remaining in every response to avoid failed generations.
- On a 408 timeout, retry with async_mode: true and poll the returned job_id.
API Reference
Full parameter reference for every endpoint. Base URL: https://magicmotion.ai
/api/generate-motionGenerate a 3D character animation from a text prompt. Optionally include spatial constraints for waypoints, paths, or pose keys. Returns the completed animation URL (sync) or a job ID to poll (async).
Request body
| Parameter | Type | Description | Default |
|---|---|---|---|
promptrequired | string | Text description of the animation, e.g. "dancing", "waving hello". | |
length | integer | Duration in seconds. Range: 2–10. Standard jobs cost 1 credit per second; constrained jobs cost 2× by default. | 5 |
guidance_scale | integer | How closely the model follows the prompt. Range: 3–20. | 4 |
seed | string | Optional seed for reproducible results. Omit for a random result. | |
async_mode | boolean | If true, returns job_id immediately (202 Accepted) without waiting for generation. | false |
constraints | array | Spatial constraint objects (root2d, fullbody, end-effector). Non-empty array enables constrained motion automatically. | |
engine | string | Optional. "kimodo" for constrained motion; "hymotion" for standard. Usually omitted — routing is automatic from constraints. |
Response — sync (200 OK)
{
"success": true,
"job_id": "run_abc123def456",
"generation_id": 1234,
"generation_engine": "hymotion",
"animation_url": "https://storage.supabase.co/...",
"metadata": {
"prompt": "A Person dancing with excitement",
"original_prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12,
"seed": "1234567",
"credits_used": 8,
"credits_remaining": 492,
"constraints_count": 0
}
}Response — async (202 Accepted)
{
"success": true,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"generation_id": null,
"generation_engine": "kimodo",
"status": "IN_PROGRESS",
"message": "Generation started. Use the job_id to check status via GET /api/job-status/{job_id}",
"request_id": "abc123",
"metadata": {
"prompt": "A person walks forward confidently",
"original_prompt": "walks forward confidently",
"length": 5,
"guidance_scale": 12,
"seed": "1234567",
"estimated_time_seconds": 48,
"constraints_count": 1,
"poll_url": "/api/job-status/e8f184eb-c082-43de-8963-2ae33eb6006c-u2?engine=kimodo&seed=1234567&length=5&guidance_scale=12&started_at=2026-02-19T10:00:00.000Z"
}
}/api/kimodo/validate-constraintsValidate constraint JSON schema before submitting a generation job. Does not consume credits or run GPU inference. Accepts API key or signed-in session auth.
| Parameter | Type | Description | |
|---|---|---|---|
constraintsrequired | array | Array of constraint objects to validate. |
{
"success": true,
"valid": true,
"constraints_count": 1
}/api/job-status/{job_id}Check the status of an async generation job. Poll after calling /generate-motion with async_mode: true. The response keeps job_id as the canonical polling identifier.
| Parameter | Type | Description | Default |
|---|---|---|---|
job_idrequired | string | The job_id returned by a previous call to /generate-motion with async_mode: true. |
{
"success": true,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"generation_id": null,
"status": "IN_PROGRESS",
"message": "Job is currently being processed",
"request_id": "def456",
"checked_at": "2024-12-30T18:57:14.005Z"
}{
"success": true,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"generation_id": 1234,
"generation_engine": "kimodo",
"status": "COMPLETED",
"animation_url": "https://storage.supabase.co/...",
"request_id": "def456",
"completed_at": "2024-12-30T18:57:14.005Z"
}{
"success": false,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"status": "FAILED",
"message": "Motion generation failed. Please try again with different parameters.",
"request_id": "def456",
"failed_at": "2024-12-30T18:57:14.005Z"
}/api/test-api-keyValidate an API key and retrieve account information without consuming credits.
{
"success": true,
"message": "API key is valid",
"account": {
"user_id": "uuid-here",
"plan": "Creator Magic",
"credits_available": 450,
"features": {
"api_access": true,
"priority_support": true,
"advanced_models": true,
"batch_processing": false
},
"subscription_active": true
},
"endpoints": {
"motion_generation": "/api/generate-motion",
"job_status": "/api/job-status/{job_id}",
"list_generations": "/api/generations",
"get_generation_by_id": "/api/generations/{id}"
}
}/api/generationsRetrieve a paginated, searchable list of all your previous generations.
Query parameters
| Parameter | Type | Description | Default |
|---|---|---|---|
page | integer | 1-based page number. | 1 |
pageSize | integer | Items per page. Range: 1–100. | 24 |
sort | string | newest · oldest · name_asc · name_desc · length_asc · length_desc | newest |
query | string | Searches generation folder name and prompt text. | |
include_download_urls | boolean | If true, each item includes animation_url when available. | false |
curl -X GET "https://www.magicmotion.ai/api/generations?page=1&pageSize=20&sort=newest&query=dance&include_download_urls=false" \
-H "Authorization: Bearer your_api_key_here"{
"success": true,
"page": 1,
"pageSize": 20,
"sort": "newest",
"total": 128,
"totalPages": 7,
"include_download_urls": false,
"items": [
{
"id": 1234,
"prompts_id": 4567,
"created_at": "2026-02-19T14:32:55.901Z",
"files_parent_folder": "dance_loop_energy",
"seed": 1234567,
"guidance_scale": 8,
"anim_length": 6,
"generation_engine": "kimodo",
"prompt_texts": ["A Person dancing with high energy"]
}
]
}/api/generations/{id}Retrieve a single generation by numeric ID, or by Runpod job UUID when mapped.
| Parameter | Type | Description | Default |
|---|---|---|---|
idrequired | string | Numeric generation id or job UUID when mapping exists. |
curl -X GET https://www.magicmotion.ai/api/generations/1234 \
-H "Authorization: Bearer your_api_key_here"{
"success": true,
"generation": {
"id": 1234,
"prompts_id": 4567,
"created_at": "2026-02-19T14:32:55.901Z",
"files_parent_folder": "dance_loop_energy",
"seed": 1234567,
"guidance_scale": 8,
"anim_length": 6,
"prompt_texts": ["A Person dancing with high energy"],
"animation_url": "https://storage.supabase.co/...",
"download_available": true
}
}Error Codes
| Status | Meaning | Resolution |
|---|---|---|
| 400 | Invalid parameters | Check parameter ranges (length 2–10, guidance_scale 3–20). |
| 401 | Invalid or missing API key | Verify your Authorization header. |
| 402 | Insufficient credits | Top up credits or upgrade your plan. |
| 403 | API access denied | Use a valid API key from a registered account. A subscription is not required. |
| 408 | Generation timed out | Retry with async_mode: true and poll the returned job_id. |
| 500 | Internal server error | Retry the request. Contact support if persistent. |
Ready to build?
Jump into the dashboard to generate your first motion, or grab an API key and start integrating — credits only, no subscription.
MMAI Platform Docs · v1.2 · Updated May 2026