Ruwana Developer Documentation

Motion API: character image + motion video.

Motion requires exactly two uploaded media inputs. Platform derives the billable duration from the motion video, reserves the quoted wallet amount and returns a durable request when production is still running.

FieldContract
character_imageRequired PNG/JPEG/WebP · max 16 MB
motion_videoRequired MP4/MOV · max 100 MB · 3–30 seconds
resolution720p (default) or 1080p
character_orientationvideo (default) or image
keep_original_soundBoolean · defaults true
promptOptional motion direction

Pricing

720p

$0.18/sec

Billable seconds are parsed from the uploaded motion video.

1080p

$0.22/sec

The public output contract is 1080p or the request fails without success billing.

Submit Motion

cURL
curl https://platform.ruwana.studio/v1/motion/generate \
  -H "Authorization: Bearer $RUWANA_API_KEY" \
  -H "Idempotency-Key: motion-001" \
  -F "character_image=@character.png" \
  -F "motion_video=@movement.mp4" \
  -F "resolution=720p" \
  -F "character_orientation=video" \
  -F "keep_original_sound=true"
Node.js
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.set('character_image', new Blob([await readFile('character.png')], {type:'image/png'}), 'character.png');
form.set('motion_video', new Blob([await readFile('movement.mp4')], {type:'video/mp4'}), 'movement.mp4');
form.set('resolution', '720p');
form.set('character_orientation', 'video');
form.set('keep_original_sound', 'true');

const response = await fetch('https://platform.ruwana.studio/v1/motion/generate', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.RUWANA_API_KEY,
    'Idempotency-Key': 'motion-001'
  },
  body: form
});
console.log(await response.json());
Python
import os, requests

with open('character.png','rb') as character, open('movement.mp4','rb') as movement:
    response = requests.post(
        'https://platform.ruwana.studio/v1/motion/generate',
        headers={
            'Authorization': 'Bearer ' + os.environ['RUWANA_API_KEY'],
            'Idempotency-Key': 'motion-001'
        },
        files={
            'character_image': ('character.png', character, 'image/png'),
            'motion_video': ('movement.mp4', movement, 'video/mp4')
        },
        data={'resolution':'720p','character_orientation':'video','keep_original_sound':'true'}
    )
print(response.json())

Follow the durable request

curl https://platform.ruwana.studio/v1/requests/$REQUEST_ID \
  -H "Authorization: Bearer $RUWANA_API_KEY"

Do not send a client duration field. The multipart parser rejects it because Motion duration is server-derived.