Install
openclaw skills install @jasonstarlight/phosor-ai-skillsCreate videos with MiniMax H3 Standard or Turbo (text-to-video, image-to-video, and reference-to-video) through Phosor AI, then use the same skill for images, speech, LoRA workflows, and e-commerce product or model photography. Use when the user wants to create H3 video from text, images, or references, compare Standard and Turbo, use the Phosor API, generate images or speech, use a custom LoRA, create e-commerce photography, or manage generation jobs.
openclaw skills install @jasonstarlight/phosor-ai-skillsMiniMax H3 Standard and Turbo are the primary video workflows in this skill. Use H3 for text-to-video, image-to-video, or reference-to-video, then use the same Phosor API for images, speech, LoRA workflows, and e-commerce product or model photography when needed.
For detailed API endpoints, parameters, pricing, and limits, see references/api.md.
Before preparing or troubleshooting an H3 prompt, read references/h3-prompt-guide.md. It covers actual dialogue versus camera/visual text, silence, reference/voice binding and enhancement preview.
Set your API key:
export PHOSOR_API_KEY="your-api-key-here"
Get an API key at phosor.ai → Settings → API Keys.
The CLI script is at scripts/phosor_client.py. All commands output JSON to stdout.
The client talks to https://phosor.ai by default, over HTTPS. Get your key from phosor.ai → Settings → API Keys.
python3 scripts/phosor_client.py --api-key <your-key> check-key
To point the same client at another Phosor endpoint, pass --base-url <url> (or set PHOSOR_BASE_URL).
HTTPS is required; plain http:// is accepted only for localhost, or when you explicitly add --allow-http.
Note: studio-analyze / studio-suite --image-url must be the full https S3 URL returned by upload-image, not the bare S3 key path — a bare key errors with unsupported URL scheme.
Use an exact model ID. Each H3 mode has a Standard and Turbo variant; Turbo is the faster option for the same input shape. H3 uses duration, resolution tier, and aspect ratio rather than Wan's frame controls.
| Input | Standard | Turbo |
|---|---|---|
| Text to video | minimax/h3/text-to-video | minimax/h3/text-to-video/turbo |
| Image to video | minimax/h3/image-to-video | minimax/h3/image-to-video/turbo |
| Reference to video | minimax/h3/reference-to-video | minimax/h3/reference-to-video/turbo |
Choose Standard or Turbo from the exact model ID before submitting. H3 output is 24fps and billed per output second; see the pricing section for current rates.
H3 is duration-based, not frame-based: it ignores --num-frames / --fps (output is
always 24fps) and bills per output second. Pick the frame size with
--resolution-tier + --aspect instead of --width/--height.
python3 scripts/phosor_client.py submit "A cat walking on a beach at sunset" \
--model minimax/h3/text-to-video \
--resolution-tier 768p --aspect 16:9 --duration 5
For the Turbo variant, keep the same inputs and add /turbo to the model ID:
python3 scripts/phosor_client.py submit "A cat walking on a beach at sunset" \
--model minimax/h3/text-to-video/turbo \
--resolution-tier 768p --aspect 16:9 --duration 5
# Upload first (direct URLs are not accepted); then submit with the returned s3_key
python3 scripts/phosor_client.py upload-image /path/to/first-frame.jpg
python3 scripts/phosor_client.py submit "The person starts dancing" \
--model minimax/h3/image-to-video \
--image-url "images/img-xxx.jpg" \
--end-image-url "images/img-yyy.jpg" \
--resolution-tier 480p --aspect 9:16 --duration 6
--end-image-url is optional and pins the closing frame.
Feed reference images, videos, and audio together; refer to them positionally in
the prompt as <Picture 1>, <Picture 2>, … At least one of --reference-image-urls /
--reference-video-urls is required.
python3 scripts/phosor_client.py submit \
"Use <Picture 1> and <Picture 2> as sequential keyframes; slow push-in, cinematic 35mm look." \
--model minimax/h3/reference-to-video \
--reference-image-urls "images/a.jpg,images/b.jpg" \
--reference-audio-urls "audio/voice.mp3" \
--resolution-tier 768p --aspect 16:9 --duration 5
Reference inputs are billed on top of the output — see Ref2VA Pricing below, and Ref2VA Reference Limits for the caps (the same at 480p and 768p).
For Turbo Reference-to-Video, use minimax/h3/reference-to-video/turbo with the same
reference flags and duration/resolution/aspect controls.
Wan is frame-based. Add /turbo to the model id for the fast, ~3x cheaper variant
(it ignores --steps / --guidance).
# Submit T2V job (480p, 81 frames, 16fps)
python3 scripts/phosor_client.py submit "A cat walking on a beach at sunset" \
--width 854 --height 480 --num-frames 81 --fps 16
# Check status
python3 scripts/phosor_client.py status <request_id>
# Get result (video URL)
python3 scripts/phosor_client.py result <request_id>
Two-step flow: upload image first, then submit with the returned S3 key.
# Step 1: Upload image
python3 scripts/phosor_client.py upload-image /path/to/photo.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit I2V job using the s3_key as image_url
python3 scripts/phosor_client.py submit "The person in the photo starts dancing" \
--image-url "images/img-xxx.jpg" --width 854 --height 480
# Submit T2I job (1024x1024, default settings)
python3 scripts/phosor_client.py submit "A futuristic city skyline at dusk" \
--model qwen-image/v2512/text-to-image --width 1024 --height 1024
# Generate multiple images at once (1-4)
python3 scripts/phosor_client.py submit "A futuristic city skyline at dusk" \
--model z-image/turbo/text-to-image --width 1024 --height 768 --num-images 4
# Check status and get result (image URL)
python3 scripts/phosor_client.py status <request_id>
python3 scripts/phosor_client.py result <request_id>
# Returns: {"data": {"image": {"url": "..."}, "seed": 12345}, ...}
Two-step flow: upload source image first, then submit with the returned S3 key.
# Step 1: Upload source image
python3 scripts/phosor_client.py upload-image /path/to/photo.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit I2I job using the s3_key as image_url
python3 scripts/phosor_client.py submit "Transform into oil painting style" \
--model z-image/turbo/image-to-image --image-url "images/img-xxx.jpg" \
--width 1024 --height 1024 --strength 0.7
Two-step flow: upload reference images first, then submit with S3 keys as image_urls.
# Step 1: Upload reference images (up to 3)
python3 scripts/phosor_client.py upload-image /path/to/ref1.jpg
python3 scripts/phosor_client.py upload-image /path/to/ref2.jpg
# Step 2: Submit image-edit job
python3 scripts/phosor_client.py submit \
"The girl in image 1 is wearing the outfit from image 2" \
--model qwen-image/v2511/image-edit \
--image-urls '["images/img-ref1.jpg","images/img-ref2.jpg"]' \
--width 1024 --height 1024
# Turbo variant (faster, Lightning LoRA built-in)
python3 scripts/phosor_client.py submit \
"The girl in image 1 is wearing the outfit from image 2" \
--model qwen-image/v2511/image-edit \
--image-urls '["images/img-ref1.jpg","images/img-ref2.jpg"]'
Two-step flow: upload both audio and reference image first, then submit with the returned S3 keys.
# Step 1: Upload reference image
python3 scripts/phosor_client.py upload-image /path/to/face.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit S2V job using the s3_key as image_url and audio URL as audio_url
python3 scripts/phosor_client.py submit "A person speaking naturally" \
--model wan/v2.2-a14b/speech-to-video \
--image-url "images/img-xxx.jpg" --audio-url "https://example.com/speech.wav" \
--width 854 --height 480
Two-step flow: upload both source video and reference image first, then submit.
# Step 1: Upload reference image
python3 scripts/phosor_client.py upload-image /path/to/character.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# Step 2: Submit Animate job using the s3_key as image_url and video URL as video_url
python3 scripts/phosor_client.py submit "The character performs the dance moves" \
--model wan/v2.2-a14b/animate \
--image-url "images/img-xxx.jpg" --video-url "https://example.com/dance.mp4" \
--width 854 --height 480
Use ltx-video/v2.3/image-audio-to-video, not an invented alias. Required inputs:
prompt, image_url; optional audio_url. For example:
python3 scripts/phosor_client.py submit \
--model ltx-video/v2.3/image-audio-to-video \
--prompt "The subject turns slowly toward the camera" \
--image-url "YOUR_UPLOADED_IMAGE_URL" --width 1280 --height 720 \
--num-frames 121 --fps 24
480p costs USD 0.0008/frame; 720p USD 0.0012/frame. 121 frames at 720p estimate USD 0.1452 / 1.452 credits. Do not use Wan's 16 fps conversion or frame alignment. 512p has a configured price but is not in the reviewed accepted-size whitelist. 1080p has no separate published rate key; confirm its quote before submitting. See LTX details.
Its own resolution set and always exactly 1 image (--num-images is ignored).
python3 scripts/phosor_client.py submit "A ceramic mug on a linen cloth, soft window light" \
--model openai/gpt-image-2/text-to-image --width 1024 --height 1024
Allowed sizes: 1024×1024 only. Any other size is rejected with 400 Invalid parameters.
It ignores --num-images, --steps and --guidance — the only parameters it accepts are
prompt, model, width, height and seed.
FLUX.2-dev has its own resolution whitelist and is fixed at 1 image per request
(--num-images and --steps are ignored).
python3 scripts/phosor_client.py submit "Editorial product photo, soft window light" \
--model flux2/dev/text-to-image --width 2048 --height 1536
python3 scripts/phosor_client.py upload-image /path/to/source.jpg
python3 scripts/phosor_client.py submit "Replace the background with a marble surface" \
--model flux2/dev/image-edit --image-url "images/img-xxx.jpg" \
--width 1024 --height 1024
TTS takes text (not a prompt) and is billed per character with a minimum charge.
python3 scripts/phosor_client.py submit-tts "Hello, welcome to Phosor AI." \
--speaker Sohee --language English
Video LoRA requires two .safetensors files (high_noise + low_noise). Image LoRA requires a single .safetensors file.
# Video LoRA: upload two .safetensors files
python3 scripts/phosor_client.py upload-lora high_noise.safetensors low_noise.safetensors --name "My Style"
# Image LoRA: import single .safetensors file via URL
python3 scripts/phosor_client.py import-lora \
"https://example.com/my_lora.safetensors" \
--name "My Image Style"
# Video LoRA: import two files via URL
python3 scripts/phosor_client.py import-lora \
"https://example.com/high_noise.safetensors" \
"https://example.com/low_noise.safetensors" \
--name "My Video Style"
# Check status, then use
python3 scripts/phosor_client.py lora-status <lora_id>
python3 scripts/phosor_client.py submit "A person walking" --lora-id <lora_id>
| Command | Description | Key Arguments |
|---|---|---|
check-key | Validate API key | — |
submit | Submit inference job (T2V/I2V/S2V/Animate/T2I/I2I) | prompt, --width, --height, --num-frames, --fps, --steps, --guidance, --image-url, --audio-url, --video-url, --lora-id, --lora-scale, --loras, --seed, --negative-prompt, --model, --num-images, --strength, --output-format |
status | Get job status | request_id |
result | Get job result (video or image URL) | request_id |
poll | Poll all pending jobs | — |
list | List locally tracked pending jobs | — |
history | Get job history | --limit |
upload-image | Upload image for I2V or I2I | file |
import-image | Import image from URL | url, --filename |
upload-lora | Upload LoRA (two .safetensors for video) | high_noise_file, low_noise_file, --name |
import-lora | Import LoRA from URLs (one or two files) | high_noise_url, [low_noise_url], --name |
loras | List LoRA models | --limit, --offset |
lora-status | Get LoRA upload/import status | lora_id |
save-lora | Activate a LoRA (extends expiry to 7 days) | lora_id, --name |
delete-lora | Delete a LoRA model | lora_id |
submit-tts | Submit a text-to-speech job (Qwen3-TTS) — keys off text, not a prompt | text, --speaker, --language, --seed, --temperature, --top-p, --top-k, --repetition-penalty |
models | List available video/image models (static offline reference) | — |
studio-features | List Image Studio endpoints, fields, billing (static offline reference) | — |
studio-pricing | Get live Image Studio pricing | — |
studio-analyze | AI-analyze a product/garment image or reference URL (freemium) | --target agent|product|model|reference, --image-url, --url, --prompt, --language |
studio-layouts | List the layout template library (query, then select) — static asset on the web front (phosor.ai), not a /api/v1 endpoint; needs no key; on a self-hosted deployment set PHOSOR_WEB_BASE_URL if the web front is not the gateway host | --module (product|clothing), --type (selling_point|aplus|white_bg|scene|closeup|size_chart) |
studio-suite | Generate a product image suite | --image-url, --layout-types, --count-per-type, --custom-suggestions, --template-ids (ids from studio-layouts, auto-expanded to custom_suggestions like the UI's manual pick — use this to get text-callout selling-point / A+ layouts and model templates), --product-info, --aspect-ratio, --gen-language, --model, --same-style-reference |
studio-clothing-suite | Generate a model/garment image suite | --image-urls, --main-image-types, --aplus-types, --product-info, --brand-config, --aspect-ratio, --gen-language, --model, --same-style-reference |
studio-status | Get Image Studio job status (separate id space, same request_id key) | request_id |
studio-cancel | Cancel a running generation — queued images refunded, already-generating ones charged | request_id |
studio-my-works | List past Image Studio generations | --task-type, --limit, --offset |
studio-call | Generic call for any other Image Studio endpoint (remove-bg, replace, inpaint, erase, handheld, translate, outpaint, recolor, enhance, upscale, scene-compose, scene-variation, real-model-swap, mannequin-swap, model-scene-swap, ai-outfit, pose-variation, ai-wearable) | method, path, --json |
Image Studio is a separate product surface for e-commerce sellers — AI product photography and model/clothing photography — reached through the same gateway and API key as video/LoRA, under the /api/v1/image-studio prefix. It has its own async namespace - the same key name request_id, but a separate id space: an Image Studio request_id is not valid on /api/v1/inference/status/... and vice versa - and its own pricing (flat per-image rate + freemium analyze quota, not per-frame). Full endpoint/parameter reference: references/api.md.
# 1. Upload the product photo
python3 scripts/phosor_client.py upload-image /path/to/product.jpg
# Returns: {"file_id": "img-xxx", "s3_key": "images/img-xxx.jpg", ...}
# 2. (Optional) AI-analyze it first for richer generation context
python3 scripts/phosor_client.py studio-analyze --target product --image-url "images/img-xxx.jpg"
# 3. Generate a product image suite
python3 scripts/phosor_client.py studio-suite --image-url "images/img-xxx.jpg" \
--layout-types "white_background,lifestyle_scene" --count-per-type 2
# 4. Poll for the result
python3 scripts/phosor_client.py studio-status <request_id>
python3 scripts/phosor_client.py upload-image /path/to/garment.jpg
python3 scripts/phosor_client.py studio-clothing-suite \
--image-urls "images/img-xxx.jpg" \
--main-image-types '{"model_shot":2,"selling_point":1}' \
--aplus-types '{"standard_aplus":1}'
python3 scripts/phosor_client.py studio-status <request_id>
The long tail of single-purpose editing endpoints doesn't get a dedicated subcommand — use studio-call with the exact field names from references/api.md:
python3 scripts/phosor_client.py studio-call POST /product/remove-bg \
--json '{"image_url": "images/img-xxx.jpg", "count": 2}'
python3 scripts/phosor_client.py studio-status <request_id>
X-API-Key (PHOSOR_API_KEY), including GET /pricing — there is no unauthenticated endpoint under this prefix.{"request_id": "...", "status": "pending"}; poll studio-status <request_id> until status is "done", "error" or "cancelled". Earlier revisions of this skill said the key was job_id - it is not, and reading it yields undefined. Image Studio ids live in a separate id space from video/LoRA: poll/status/result will not accept an Image Studio request_id.POST /jobs/{request_id}/cancel stops a running generation. Images still queued are refunded; images already generating are charged and cannot be stopped; images already delivered bill once through the normal path. The response reports the split as refunded_queued, charged_running and already_done, and the task then polls as status: "cancelled" - not an error.studio-pricing for the live rate. Partial success (e.g. 3 of 5 images) bills only the successes.agent/analyze, product/analyze, model/analyze share a daily free quota before per-call billing kicks in.model_attrs matters for model-photography endpoints (real-model-swap, mannequin-swap, ai-outfit, ai-wearable) — pass {gender, age_group, ethnicity, skin_tone, hair_color} explicitly; it is not reliably inferred from the source image alone.studio-features for the full offline endpoint/field catalog without leaving the terminal.| Preset | Width × Height | Max Frames (turbo) | Max Frames (standard) |
|---|---|---|---|
| 480p landscape | 854 × 480 | 161 | 161 |
| 480p portrait | 480 × 854 | 161 | 161 |
| 720p landscape | 1280 × 720 | 161 | 161 |
| 720p portrait | 720 × 1280 | 161 | 161 |
| 1080p landscape | 1920 × 1080 | 153 | 81 |
| 1080p portrait | 1080 × 1920 | 153 | 81 |
Standard (non-turbo) mode: 1080p is capped at 81 frames due to generation time limits.
| Preset | Width x Height | Max Frames |
|---|---|---|
| 480p landscape | 854 x 480 | 161 |
| 480p portrait | 480 x 854 | 161 |
| 512p square | 512 x 512 | 161 |
| 720p landscape | 1280 x 720 | 161 |
| 720p portrait | 720 x 1280 | 161 |
| Preset | Width × Height |
|---|---|
| Square small | 512 × 512 |
| Square | 1024 × 1024 |
| Landscape | 1024 × 768 |
| Portrait | 768 × 1024 |
| Wide landscape | 1280 × 768 |
| Tall portrait | 768 × 1280 |
--resolution-tier + --aspect)| Tier | 16:9 | 4:3 | 1:1 | 3:4 | 9:16 |
|---|---|---|---|---|---|
| 480p | 832 × 480 | 640 × 480 | 480 × 480 | 480 × 640 | 480 × 832 |
| 768p | 1344 × 768 | 1024 × 768 | 768 × 768 | 768 × 1024 | 768 × 1344 |
duration is 4–15 seconds (default 5). Output FPS is fixed at 24 and
frames_per_second is ignored.
minimax/h3/reference-to-video)Caps differ per tier, and the image cap is higher when you send only images:
| Limit | 480p | 768p |
|---|---|---|
| Reference images (with videos/audio present) | 5 | 5 |
| Reference images (images only) | 9 | 9 |
| Reference videos | 3 | 3 |
| Reference audios | 3 | 3 |
| Limit | Value |
|---|---|
| Total reference video length | 15.1 s (across all reference videos) |
| Reference video FPS ceiling | 24 |
| Reference audio length | 10 s each |
| Reference image longest edge | 2048 px |
| Reference image aspect ratio | ≤ 4.0 |
| Width × Height |
|---|
| 2048 × 1536 · 1536 × 2048 |
| 2048 × 1152 · 1152 × 2048 |
| 2048 × 2048 · 1024 × 1024 |
FLUX.2-dev does not accept the general image resolution list above, and always returns exactly 1 image.
Frames must follow 1 + 4*k where k >= 1 (e.g. 5, 9, 13, ... 81, 85, ...). Server auto-aligns down.
| Parameter | Default | Range |
|---|---|---|
frames_per_second | 16 | 4–60 |
num_inference_steps | 4 | 4–40 |
guidance_scale | 1.0 | 1.0–10.0 |
| Parameter | Default | Range | Notes |
|---|---|---|---|
num_images | 1 | 1–4 | Number of images to generate |
num_inference_steps | varies | 1–4 (z-image turbo), 1–40 (qwen-image) | Model-dependent max |
guidance_scale | varies | 1.0–20.0 | — |
strength | — | 0.0–1.0 | Image-to-image only: how much to transform the source |
output_format | png | png, jpeg | Output file format |
Model API inference jobs run concurrently up to a per-account cap; over it the submit
returns 429 and you retry after a job finishes.
| Tier | Plan | Concurrent Model API jobs |
|---|---|---|
| Free | — | 1 |
| 1 | Starter | 2 |
| 2 | Standard | 4 |
| 3 | Pro | 8 |
Image Studio runs on its own pool and does not consume this quota — a suite and a video generation can run at the same time.
python3 scripts/phosor_client.py submit "A person dancing" \
--loras '[{"lora_id": "lora-abc", "lora_scale": 0.8}, {"lora_id": "lora-def", "lora_scale": 0.5}]'
Files must be uploaded before use — direct URLs are NOT supported in submit --image-url:
upload-image / import-image → returns s3_key → use as --image-urlupload-lora / import-lora → returns lora_id → use as --lora-idPENDING → PROCESSING → COMPLETED / FAILED
The poll command checks all locally-tracked pending jobs and removes completed/failed ones.
Per output second, regardless of clip length:
| Model | 480p USD/sec | 768p USD/sec | 480p credits/sec | 768p credits/sec |
|---|---|---|---|---|
text-to-video, image-to-video | $0.0045 | $0.012 | 0.045 | 0.12 |
text-to-video/turbo, image-to-video/turbo | $0.006 | $0.009 | 0.06 | 0.09 |
reference-to-video | $0.0045 | $0.012 | 0.045 | 0.12 |
reference-to-video/turbo | $0.006 | $0.009 | 0.06 | 0.09 |
Output plus reference inputs:
total = duration x base_rate
+ max(0, reference_images - 5) x image_rate # first 5 images free
+ reference_video_seconds x base_rate # reference audio is free
| Model | Tier | Base USD/sec | Reference image | Reference audio | Reference video USD/sec |
|---|---|---|---|---|---|
reference-to-video | 480p | $0.0045 | first 5 free, then $0.0075 each | free | $0.0045 |
reference-to-video | 768p | $0.012 | first 5 free, then $0.0075 each | free | $0.012 |
Example — reference-to-video, 5s at 768p with 4 reference images: 5 x $0.012 = 0.6 credits.
5s at 480p with 9 images: 5 x $0.0045 + 4 x $0.0075 = 0.525 credits.
| Tier | Standard | + LoRA | Turbo |
|---|---|---|---|
| 480p | $0.0009375 | $0.00125 | $0.0003125 |
| 512p | $0.0013125 | $0.001625 | $0.0004375 |
| 720p | $0.001875 | $0.0021875 | $0.000625 |
| 1080p | $0.0025 | $0.003 | $0.0010938 |
LoRA multiplier on turbo: 1.2x.
| Tier | Cost |
|---|---|
| 480p | $0.0009375 |
| 512p | $0.0013125 |
| 720p | $0.001875 |
| Tier | Cost |
|---|---|
| 480p | $0.00125 |
| 512p | $0.00175 |
| 720p | $0.0025 |
| Model | USD | Credits |
|---|---|---|
| GPT Image 2 T2I | $0.03 | 0.3 |
| FLUX.2-dev T2I | $0.006 | 0.06 |
| FLUX.2-dev Image Edit | $0.012 | 0.12 |
| qwen-image T2I | $0.015 | 0.15 |
| qwen-image T2I + LoRA | $0.018 | 0.18 |
| qwen-image-edit | $0.003 | 0.03 |
| z-image turbo (T2I / I2I) | $0.0025 | 0.025 |
| z-image turbo + LoRA | $0.003 | 0.03 |
Flat per image at every resolution. Multiply by num_images (1–4) where the model
supports it; FLUX.2-dev is fixed at 1 image per request.
Qwen Image Edit Turbo has a configured USD 0.005 / 0.05 credits rate, but public submission availability is not verified. Do not offer it based on a price key alone. GPT Image 2, like FLUX.2-dev, is fixed at one image per request.
Model API prices do not price Image Studio suites or Drama workflows. Use their own contracts and price sources. Seedance Standard/Fast/paused variants and Kimi have no verified public Model API IDs in this review; current Drama sale status requires confirmation. Do not invent model IDs or infer availability from history.
Price reference checked 2026-11-04; live Model API prices take precedence. A price configuration is not proof of submission availability or successful delivery.
| Item | Cost |
|---|---|
| Qwen3-TTS | $0.00003 per character |
| Minimum charge | $0.003 per request |
Exchange rate: 10 credits = $1 USD. Credits pre-deducted, auto-refunded on failure.
Live rates: GET /api/v1/pricing/config — always prefer it over any table here.