Ruwana Developer Documentation

Avatar API: image + speech or uploaded audio.

Every Avatar request requires one avatar image and exactly one speech source. Use Ruwana TTS with public voice controls, or upload finished audio and let Platform measure its actual duration.

FieldContract
avatar_imageRequired PNG/JPEG/WebP · max 16 MB
speechText up to 3000 characters; mutually exclusive with audio
audioMP3/WAV/M4A/MP4 audio · max 32 MB; mutually exclusive with speech
resolution720p Standard (default) or 1080p Pro
voicePublic Ruwana voice name; TTS requests only
voice_speed80–130 · default 100; TTS requests only
emotionNeutral, Confident, Happy, Angry, Sad, Presenter or Luxury
promptOptional visual/performance direction

Voices and pricing

AxelMayaSiennaEthanMarcusEliasNovaCelesteTobyLilaKaiMimi

Standard · 720p

$0.11/sec

TTS included when speech is used.

Pro · 1080p

$0.22/sec

TTS included when speech is used.

Speech-driven Avatar

cURL
curl https://platform.ruwana.studio/v1/avatar/generate \
  -H "Authorization: Bearer $RUWANA_API_KEY" \
  -H "Idempotency-Key: avatar-tts-001" \
  -F "avatar_image=@portrait.jpg" \
  -F "speech=Welcome to the new collection." \
  -F "resolution=720p" \
  -F "voice=Maya" \
  -F "voice_speed=100" \
  -F "emotion=Confident" \
  -F "prompt=Natural premium presenter delivery with restrained gestures."
Node.js
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.set('avatar_image', new Blob([await readFile('portrait.jpg')], {type:'image/jpeg'}), 'portrait.jpg');
form.set('speech', 'Welcome to the new collection.');
form.set('resolution', '720p');
form.set('voice', 'Maya');
form.set('voice_speed', '100');
form.set('emotion', 'Confident');

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

Uploaded-audio Avatar

Python
import os, requests

with open('portrait.jpg','rb') as image, open('voice.mp3','rb') as audio:
    response = requests.post(
        'https://platform.ruwana.studio/v1/avatar/generate',
        headers={
            'Authorization': 'Bearer ' + os.environ['RUWANA_API_KEY'],
            'Idempotency-Key': 'avatar-audio-001'
        },
        files={
            'avatar_image': ('portrait.jpg', image, 'image/jpeg'),
            'audio': ('voice.mp3', audio, 'audio/mpeg')
        },
        data={'resolution':'1080p','emotion':'Luxury','prompt':'Controlled premium delivery.'}
    )
print(response.json())

Do not send voice or voice_speed with uploaded audio. Platform rejects TTS controls when the speech source is already prepared audio.

Prepared-audio duration

Platform initially creates the request around the minimum production reservation, then measures the actual prepared audio and adjusts the quote before Avatar video submission. Accepted Avatar duration is 3–30 seconds.