Skip to main content
The Queue API is the primary way to generate and transform videos with the Android SDK. Submit jobs that are processed asynchronously, then poll for status or observe progress as a Kotlin Flow.
All video generation (text-to-video, image-to-video, video-to-video, restyling, motion control) uses the Queue API. For realtime camera transformation, use the Realtime API.

Overview

The Queue API provides five methods:
MethodDescription
submit()Submit a job and get a job ID immediately
status()Check the status of a submitted job
result()Download the completed video as ByteArray
submitAndPoll()Submit and auto-poll until completion
submitAndObserve()Submit and return a Flow of progress updates

Quick Start

import ai.decart.sdk.DecartClient
import ai.decart.sdk.DecartClientConfig
import ai.decart.sdk.VideoModels
import ai.decart.sdk.queue.FileInput
import ai.decart.sdk.queue.QueueJobResult
import ai.decart.sdk.queue.VideoEditInput

val client = DecartClient(context, DecartClientConfig(apiKey = "your-api-key"))

val input = VideoEditInput(
    prompt = "Cinematic color grade, soft contrast",
    data = FileInput.fromUri(videoUri),
)

when (val result = client.queue.submitAndPoll(VideoModels.LUCY_2_V2V, input)) {
    is QueueJobResult.Completed -> {
        val output = java.io.File(context.cacheDir, "output.mp4")
        output.writeBytes(result.data)
    }
    is QueueJobResult.Failed -> {
        Log.e("Decart", "Job failed: ${result.error}")
    }
    else -> Unit
}

client.release()

Submit a Job

Submit a video generation job and receive a job ID immediately:
val input = VideoEditInput(
    prompt = "Transform to anime style",
    data = FileInput.fromUri(videoUri),
)

val job = client.queue.submit(VideoModels.LUCY_2_V2V, input)

println("Job ID: ${job.jobId}")
println("Status: ${job.status}")  // PENDING
Returns: JobSubmitResponse
  • jobId: String - Unique job identifier
  • status: JobStatus - Initial status (PENDING)

Check Job Status

Poll the status of a submitted job:
val status = client.queue.status(job.jobId)
println("Status: ${status.status}")  // PENDING, PROCESSING, COMPLETED, or FAILED
Status Values:
StatusDescription
PENDINGJob is queued, waiting to be processed
PROCESSINGJob is currently being processed
COMPLETEDJob finished successfully
FAILEDJob failed or timed out

Get Job Result

Download the completed video as raw bytes:
val status = client.queue.status(job.jobId)

if (status.status == JobStatus.COMPLETED) {
    val videoBytes = client.queue.result(job.jobId)

    val output = java.io.File(context.cacheDir, "output.mp4")
    output.writeBytes(videoBytes)
}
Returns: ByteArray - The output video (MP4).

Submit and Poll

The easiest way to use the Queue API. Submit a job and automatically poll until completion:
val result = client.queue.submitAndPoll(
    model = VideoModels.LUCY_2_V2V,
    input = VideoEditInput(
        prompt = "Cinematic color grade",
        data = FileInput.fromUri(videoUri),
    ),
    onStatusChange = { status ->
        Log.d("Decart", "Status: ${status.status}")
    }
)

when (result) {
    is QueueJobResult.Completed -> {
        val output = java.io.File(context.cacheDir, "output.mp4")
        output.writeBytes(result.data)
    }
    is QueueJobResult.Failed -> {
        Log.e("Decart", "Failed: ${result.error}")
    }
    else -> Unit
}
Parameters:
  • model (required) - Video model from VideoModels
  • input (required) - Typed input (e.g., VideoEditInput, TextToVideoInput)
  • onStatusChange (optional) - Callback invoked on each status change
submitAndPoll does not throw on job failure — it returns QueueJobResult.Failed. It will throw QueueSubmitException, QueueStatusException, or QueueResultException on network/API errors.

Submit and Observe (Flow)

Get a reactive Flow of progress updates, ideal for Jetpack Compose UIs:
client.queue.submitAndObserve(VideoModels.LUCY_2_V2V, input)
    .collect { update ->
        when (update) {
            is QueueJobResult.InProgress -> {
                // Pending or processing
                Log.d("Decart", "Status: ${update.status}")
                updateProgressUI(update.status)
            }
            is QueueJobResult.Completed -> {
                Log.d("Decart", "Done: ${update.data.size} bytes")
                saveVideo(update.data)
            }
            is QueueJobResult.Failed -> {
                Log.e("Decart", "Failed: ${update.error}")
                showError(update.error)
            }
        }
    }
Emits:
  1. QueueJobResult.InProgress - On each poll while pending/processing
  2. QueueJobResult.Completed - Terminal: contains the video bytes
  3. QueueJobResult.Failed - Terminal: contains the error message
submitAndObserve() returns a cold Flow that runs on Dispatchers.IO. Collect it in viewModelScope or lifecycleScope for automatic cancellation.

Input Types

Each model category has a typed input class that enforces required fields at compile time.

VideoEditInput

For video-to-video editing models: LUCY_2_V2V, LUCY_PRO_V2V, LUCY_FAST_V2V.
val input = VideoEditInput(
    prompt = "Cinematic color grade, soft contrast",  // required (can be empty)
    data = FileInput.fromUri(videoUri),                // required
    referenceImage = FileInput.fromUri(refImageUri),   // optional
    seed = 42,                                         // optional (0–4294967295)
    resolution = "720p",                               // optional
    enhancePrompt = true,                              // optional
)
prompt
String
required
Text prompt describing the desired edit. Max 1000 characters. Can be empty string.
data
FileInput
required
Video file to transform.
referenceImage
FileInput
Optional reference image for style guidance.
seed
Int
Seed for reproducible output (0–4294967295).
resolution
String
default:"720p"
Output resolution.
enhancePrompt
Boolean
default:"true"
Whether to AI-enhance the prompt.

VideoRestyleInput

For video restyling: LUCY_RESTYLE_V2V. Must provide exactly one of prompt or referenceImage (mutually exclusive).
// Option A: Restyle with prompt
val restyle = VideoRestyleInput(
    data = FileInput.fromUri(videoUri),
    prompt = "Oil painting style with bold brushstrokes",
    enhancePrompt = true,
)

// Option B: Restyle with reference image
val restyle = VideoRestyleInput(
    data = FileInput.fromUri(videoUri),
    referenceImage = FileInput.fromUri(styleImageUri),
    seed = 7,
)

client.queue.submit(VideoModels.LUCY_RESTYLE_V2V, restyle)
prompt and referenceImage are mutually exclusive. Providing both throws an IllegalArgumentException. When using referenceImage, enhancePrompt must not be true.

TextToVideoInput

For text-to-video generation: LUCY_PRO_T2V. No media file required.
val input = TextToVideoInput(
    prompt = "Drone shot over snowy mountains at sunrise",
    orientation = "landscape",  // "landscape" or "portrait"
    resolution = "720p",        // "720p" or "480p"
    enhancePrompt = true,
    seed = 123,
)

client.queue.submit(VideoModels.LUCY_PRO_T2V, input)

ImageToVideoInput

For image-to-video animation: LUCY_PRO_I2V, LUCY_DEV_I2V.
val input = ImageToVideoInput(
    prompt = "Slow cinematic push-in with gentle motion",
    data = FileInput.fromUri(imageUri),
    resolution = "720p",
    enhancePrompt = true,
)

client.queue.submit(VideoModels.LUCY_PRO_I2V, input)

MotionVideoInput

For trajectory-guided motion: LUCY_MOTION. Animates a detected object along a path.
val input = MotionVideoInput(
    data = FileInput.fromUri(imageUri),
    trajectory = listOf(
        TrajectoryPoint(frame = 0, x = 0.5f, y = 0.5f),
        TrajectoryPoint(frame = 6, x = 0.65f, y = 0.4f),
        TrajectoryPoint(frame = 12, x = 0.8f, y = 0.35f),
    ),
    seed = 42,
)

client.queue.submit(VideoModels.LUCY_MOTION, input)
TrajectoryPoint:
  • frame: Int - Frame number (≥ 0)
  • x: Float - Normalized x coordinate (0.0–1.0)
  • y: Float - Normalized y coordinate (0.0–1.0)
Trajectory must have 2–1000 points. The object at the first point is auto-detected — no text prompt is needed.

FileInput

FileInput is a sealed class that wraps common Android file sources. All queue input types use it for file fields.
import ai.decart.sdk.queue.FileInput

// From gallery picker or camera capture result
val fromUri = FileInput.fromUri(contentUri)

// From a file on disk
val fromFile = FileInput.fromFile(java.io.File("/path/to/video.mp4"))

// From raw bytes in memory
val fromBytes = FileInput.fromBytes(videoBytes, "video/mp4")

// From an InputStream (streaming — read once at submission)
val fromStream = FileInput.fromInputStream(inputStream, "video/mp4")
Factory methodSourceBest for
fromUri(uri)Android content UriGallery picker, camera capture, content providers
fromFile(file)java.io.FileFiles on local storage
fromBytes(bytes, mimeType)ByteArrayIn-memory data
fromInputStream(stream, mimeType)InputStreamStreaming from network or assets
fromUri() and fromFile() stream directly from disk into the HTTP body without buffering in memory, avoiding OOM errors on large video files.

Error Handling

Queue methods throw typed exceptions on network/API errors:
import ai.decart.sdk.queue.*

try {
    val result = client.queue.submitAndPoll(model, input)
    when (result) {
        is QueueJobResult.Completed -> saveVideo(result.data)
        is QueueJobResult.Failed -> showError(result.error)
        else -> Unit
    }
} catch (e: InvalidInputException) {
    Log.e("Decart", "Invalid input: ${e.message}")
} catch (e: QueueSubmitException) {
    Log.e("Decart", "Submit failed: ${e.message} (HTTP ${e.statusCode})")
} catch (e: QueueStatusException) {
    Log.e("Decart", "Status check failed: ${e.message}")
} catch (e: QueueResultException) {
    Log.e("Decart", "Download failed: ${e.message}")
}
Exception hierarchy:
ExceptionThrown when
QueueExceptionBase class for all queue errors
QueueSubmitExceptionJob submission fails
QueueStatusExceptionStatus check fails
QueueResultExceptionResult download fails
InvalidInputExceptionInput validation fails before submission
All queue exceptions include an optional statusCode: Int? for HTTP errors.

Manual Polling

For custom polling logic, combine submit(), status(), and result():
import kotlinx.coroutines.delay

// Submit job
val job = client.queue.submit(VideoModels.LUCY_PRO_T2V, input)
Log.d("Decart", "Job submitted: ${job.jobId}")

// Manual polling loop
while (true) {
    val current = client.queue.status(job.jobId)
    Log.d("Decart", "Status: ${current.status}")

    when (current.status) {
        JobStatus.COMPLETED -> {
            val videoBytes = client.queue.result(job.jobId)
            saveVideo(videoBytes)
            break
        }
        JobStatus.FAILED -> {
            Log.e("Decart", "Generation failed")
            break
        }
        else -> delay(1_500L)  // Wait before next poll
    }
}

Supported Models

ModelConstantInput TypeDescription
Lucy 2 V2VVideoModels.LUCY_2_V2VVideoEditInputVideo editing with optional reference image
Lucy Pro V2VVideoModels.LUCY_PRO_V2VVideoEditInputPro quality video-to-video
Lucy Fast V2VVideoModels.LUCY_FAST_V2VVideoEditInputFast video-to-video
Lucy Restyle V2VVideoModels.LUCY_RESTYLE_V2VVideoRestyleInputVideo restyling (prompt or reference image)
Lucy Pro T2VVideoModels.LUCY_PRO_T2VTextToVideoInputText-to-video generation
Lucy Pro I2VVideoModels.LUCY_PRO_I2VImageToVideoInputImage-to-video (Pro)
Lucy Dev I2VVideoModels.LUCY_DEV_I2VImageToVideoInputImage-to-video (Dev)
Lucy MotionVideoModels.LUCY_MOTIONMotionVideoInputTrajectory-guided motion video

API Reference

client.queue.submit(model, input)

Submit a job for processing. Suspend function. Parameters:
  • model: VideoModel - Video model from VideoModels
  • input: QueueJobInput - Typed input (e.g., VideoEditInput)
Returns: JobSubmitResponse
  • jobId: String - Unique job identifier
  • status: JobStatus - Initial status (PENDING)
Throws: InvalidInputException, QueueSubmitException

client.queue.status(jobId)

Check the status of a job. Suspend function. Parameters:
  • jobId: String - Job ID from submit()
Returns: JobStatusResponse
  • jobId: String - Job identifier
  • status: JobStatus - Current status
Throws: QueueStatusException

client.queue.result(jobId)

Download the result of a completed job. Suspend function. Parameters:
  • jobId: String - Job ID from submit()
Returns: ByteArray - Output video bytes (MP4) Throws: QueueResultException

client.queue.submitAndPoll(model, input, onStatusChange?)

Submit a job and auto-poll until completion. Suspend function. Parameters:
  • model: VideoModel - Video model
  • input: QueueJobInput - Typed input
  • onStatusChange: ((JobStatusResponse) -> Unit)? - Optional status callback
Returns: QueueJobResult.Completed or QueueJobResult.Failed Throws: QueueSubmitException, QueueStatusException, QueueResultException on network errors

client.queue.submitAndObserve(model, input)

Submit a job and return a Flow of progress updates. Parameters:
  • model: VideoModel - Video model
  • input: QueueJobInput - Typed input
Returns: Flow<QueueJobResult> - Emits InProgress, then Completed or Failed

client.queue.release()

Release HTTP resources held by the queue client.

Next Steps