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.
| Field | Contract |
|---|---|
character_image | Required PNG/JPEG/WebP · max 16 MB |
motion_video | Required MP4/MOV · max 100 MB · 3–30 seconds |
resolution | 720p (default) or 1080p |
character_orientation | video (default) or image |
keep_original_sound | Boolean · defaults true |
prompt | Optional 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.