Kling motion control: send image_url or avatar_handle, not both

Sume's Kling 3.0 Motion Control takes exactly one visual source: image_url or an avatar_id/avatar_handle. A short Python check before the $0.1575/s submit.

5 min readSume
All posts

Kling 3.0 Motion Control on Sume needs one visual source for the character: either image_url, or an avatar_id or avatar_handle for a ready avatar. Sending both, or neither, is refused. The driving video goes in motion_video_url and must be a public HTTPS URL.

The rule

The schema shares its visual-source contract with VEED Fabric 1.0 and MiniMax H3 Max Lip Sync: image_url XOR avatar_id/avatar_handle. That is useful, because a client that already builds a Fabric request only needs to swap audio_url for motion_video_url.

Visual-source combinations, Sume schema read 2026-10-08
You sendResult
image_url onlyAccepted
avatar_handle onlyAccepted
avatar_id onlyAccepted
image_url and avatar_handleRefused
NeitherRefused
reference_image_urls or reference_video_urlsRefused, the body is strict

A request builder

This Python builds the JSON for the call and refuses ambiguous input. It uses only the standard library and stops if SUME_API_KEY is missing.

import json, os, urllib.request

def build(driver_url, seconds, image_url=None, avatar_handle=None):
    if bool(image_url) == bool(avatar_handle):
        raise ValueError("send exactly one of image_url or avatar_handle")
    if not 1 <= seconds <= 30:
        raise ValueError("duration_seconds must be 1 to 30")
    body = {"motion_video_url": driver_url, "duration_seconds": seconds}
    body["image_url" if image_url else "avatar_handle"] = image_url or avatar_handle
    return body

key = os.environ["SUME_API_KEY"]
body = build("https://media.sume.com/driver.mp4", 12,
             image_url="https://media.sume.com/still.png")
req = urllib.request.Request(
    "https://api.sume.com/v1/kling/3.0/motion-control",
    data=json.dumps(body).encode(),
    headers={"Authorization": f"Bearer {key}",
             "Content-Type": "application/json",
             "Idempotency-Key": "motion-demo-001"})
print(urllib.request.urlopen(req).read().decode())

After the submit

The call returns a job. Poll GET /v1/jobs/{id}/status and then /result, or use a webhook. A 12-second driver reserves 12 x $0.1575 = $1.89. Keep the same Idempotency-Key when you retry after a network error, so a second submit does not create a second charge.

Why the XOR rule exists

A single request must say who the character is exactly once. If both were allowed, the server would have to guess which wins, and a wrong guess would cost $0.1575 for every second of the driver. Failing at validation is free, so the rule protects your balance.

The same rule applies on VEED Fabric and H3 Max Lip Sync, so one request-building function can serve all three, with the extra field varying: audio_url and duration for the talking routes, and motion_video_url plus duration_seconds here. Note that the Fabric and lip-sync bodies also take a resolution, while Kling motion control has no resolution setting.

Cost sanity check for the sample above: a 12-second driver is 12 x $0.1575 = $1.89, and the catalog's estimate for the route is 79 cents, which is a default-length estimate and not your price.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume