curl --request POST \
--url https://apis.viggle.ai/v1/characters \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form image='@example-file' \
--form 'image_url=<string>' \
--form name= \
--form type=render \
--form enhance=false \
--form joint_set=body \
--form model_precision=0.5 \
--form filter_low_quality=false \
--form render_thumbnail=false \
--form 'task_id=<string>'import requests
url = "https://apis.viggle.ai/v1/characters"
files = { "image": ("example-file", open("example-file", "rb")) }
payload = {
"image_url": "<string>",
"name": "",
"type": "render",
"enhance": "false",
"joint_set": "body",
"model_precision": "0.5",
"filter_low_quality": "false",
"render_thumbnail": "false",
"task_id": "<string>"
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('image', '<string>');
form.append('image_url', '<string>');
form.append('name', '');
form.append('type', 'render');
form.append('enhance', 'false');
form.append('joint_set', 'body');
form.append('model_precision', '0.5');
form.append('filter_low_quality', 'false');
form.append('render_thumbnail', 'false');
form.append('task_id', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://apis.viggle.ai/v1/characters', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apis.viggle.ai/v1/characters",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/characters"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apis.viggle.ai/v1/characters")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/characters")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"name": "<string>",
"progress": 50,
"capabilities": [
"<string>"
],
"created_at": "<string>",
"completed_at": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
},
"type": "render",
"vsplat": {
"status": "queued",
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
}
},
"glb": {
"status": "queued",
"skeletons": [
"mixamo"
],
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
}
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}从图片创建角色
从图片创建可复用角色,并可选提取 3D vsplat。
curl --request POST \
--url https://apis.viggle.ai/v1/characters \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form image='@example-file' \
--form 'image_url=<string>' \
--form name= \
--form type=render \
--form enhance=false \
--form joint_set=body \
--form model_precision=0.5 \
--form filter_low_quality=false \
--form render_thumbnail=false \
--form 'task_id=<string>'import requests
url = "https://apis.viggle.ai/v1/characters"
files = { "image": ("example-file", open("example-file", "rb")) }
payload = {
"image_url": "<string>",
"name": "",
"type": "render",
"enhance": "false",
"joint_set": "body",
"model_precision": "0.5",
"filter_low_quality": "false",
"render_thumbnail": "false",
"task_id": "<string>"
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('image', '<string>');
form.append('image_url', '<string>');
form.append('name', '');
form.append('type', 'render');
form.append('enhance', 'false');
form.append('joint_set', 'body');
form.append('model_precision', '0.5');
form.append('filter_low_quality', 'false');
form.append('render_thumbnail', 'false');
form.append('task_id', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://apis.viggle.ai/v1/characters', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apis.viggle.ai/v1/characters",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/characters"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apis.viggle.ai/v1/characters")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/characters")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nrender\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"joint_set\"\r\n\r\nbody\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model_precision\"\r\n\r\n0.5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter_low_quality\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"render_thumbnail\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"task_id\"\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"name": "<string>",
"progress": 50,
"capabilities": [
"<string>"
],
"created_at": "<string>",
"completed_at": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
},
"type": "render",
"vsplat": {
"status": "queued",
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
}
},
"glb": {
"status": "queued",
"skeletons": [
"mixamo"
],
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
}
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}status 为 ready 后再将其 ID 用于渲染。
type 决定输出:render(默认)仅创建可用于渲染的 2D 角色,vsplat 仅创建 3D 高斯泼溅模型,all 同时创建两者并分别计费。vsplat 提取就绪后,通过导出 3D 角色获取下载链接。
type=render 固定 1 积分;type=vsplat 固定 25 积分,设置 enhance=true 时为 30 积分。增强仅影响 vsplat 提取,30 积分替代原有 25 积分,并非额外叠加。type=all 合计为 26 积分,增强时为 31 积分。详见计费与保留期限。请求参数
使用multipart/form-data。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
image | file | 二选一 | — | 直接上传的角色源图片,支持 PNG、JPEG、WebP。与 image_url 二选一。 |
image_url | string | 二选一 | — | 可公开访问的图片 HTTP(S) URL。替代 image,并在开始接收素材时保持可访问。 |
name | string | 否 | "" | 详情和列表中显示的名称,便于识别角色,不影响生成。 |
type | string | 否 | render | render 创建 2D 角色(1 积分),vsplat 创建 3D 模型(25 积分,增强时 30),all 同时创建(26 或 31 积分)。 |
enhance | boolean | 否 | false | 是否对 vsplat 提取执行额外的 AI 增强。仅 vsplat/all 生效;render 下不生效、不收费。将 vsplat 费用由 25 改为 30 积分。 |
image 与 image_url 必须二选一。以下参数仅适用于 type=vsplat 或 all。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
model_precision | number | 否 | — | 模型提取精度,范围 (0, 1]。在编码之前的 PKL 提取阶段生效。 |
filter_low_quality | boolean | 否 | false | 编码前剔除低重要性高斯点的总开关。为 true 时移除低于提取服务质量阈值的点。 |
joint_set | string | 否 | body | vsplat 绑定的骨架:full、expression 或 body,不影响价格。 |
render_thumbnail | boolean | 否 | false | 是否在提取时生成标准角色预览缩略图。渲染来源和姿势由服务内部选择。 |
task_id | string | 否 | — | type=vsplat 的调用者自定义幂等 ID。同一任务重试使用稳定且唯一的值;type=all 的配套提取使用新建角色 ID 作为键。 |
joint_set 骨架: full 保留全部 441 个关节,包含完整面部骨架,支持表情动画;expression 保留 119 个身体及表情关节;body 仅保留 86 个身体关节,移除全部面部关节,不能播放面部动画。减少关节只改变关节列表与蒙皮矩阵 W 的列,不改变高斯点数量;因此 body 仍保留完整面部细节,只是无法让面部运动。省略时,V1 会填入 body(提取服务内部默认虽为 full,但 V1 会先填入 body)。驱动该角色的动作应使用相同的 joint_set。这与动作导出的 download_type=metahuman 骨架兼容,关节一一对应,配合使用无需重定向。见导出 3D 动作。download_type=mixamo 则是通用的 50 关节骨架,用于驱动自己的角色网格时仍需重定向。响应参数
返回200 OK 和 Character 对象。
| 字段 | 类型 | 始终返回 | 说明 |
|---|---|---|---|
id | string | 是 | 角色公开 ID,例如 char_550e8400-e29b-41d4-a716-446655440000,请原样保存。 |
status | string | 是 | 初始通常为 queued,终态为 ready/failed。all 必须等 2D 和 vsplat 都就绪才为 ready;任一失败则为 failed,可结合 vsplat.status 定位。 |
name | string | 是 | 传入的名称,或空字符串。 |
progress | integer 或 null | 是 | 可用时为 0–100 的进度。 |
capabilities | string[] | 是 | 就绪前为空;具备视频渲染能力的就绪角色包含 video_render。 |
created_at | string 或 null | 是 | 带 UTC 偏移的 ISO 8601 时间,如 2026-07-31T09:15:22+00:00。 |
completed_at | string 或 null | 是 | ISO 8601 完成时间,完成前为 null。 |
error | object 或 null | 是 | 失败时的结构化错误,否则为 null。 |
type | string | 是 | 请求中的 render、vsplat 或 all。 |
vsplat | object 或 null | 是 | 仅 render 时为 null;否则为 {status, error},表示提取自身的状态。无下载链接,下载请调用导出 3D 角色。 |
glb | null | 是 | 角色始终为 null。 |
{
"id": "char_550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"name": "Presenter",
"progress": 0,
"capabilities": [],
"created_at": "2026-07-31T09:15:22+00:00",
"completed_at": null,
"error": null,
"type": "render",
"vsplat": null,
"glb": null
}
示例
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, _ := writer.CreateFormFile("image", "character.png")
file, _ := os.Open("character.png")
defer file.Close()
io.Copy(part, file)
writer.WriteField("name", "Presenter")
writer.Close()
req, _ := http.NewRequest("POST", "https://apis.viggle.ai/v1/characters", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("VIGGLE_API_KEY"))
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
const form = new FormData();
form.append("image", imageFile);
form.append("name", "Presenter");
const response = await fetch("https://apis.viggle.ai/v1/characters", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` },
body: form,
});
const character = await response.json();
import os
import requests
with open("character.png", "rb") as image:
response = requests.post(
"https://apis.viggle.ai/v1/characters",
headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"},
files={"image": image},
data={"name": "Presenter"},
)
response.raise_for_status()
character = response.json()
curl -X POST "https://apis.viggle.ai/v1/characters" \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F "[email protected]" \
-F "name=Presenter"
-F "type=vsplat";若同时保留渲染能力,使用 all:
curl -X POST "https://apis.viggle.ai/v1/characters" \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F "[email protected]" \
-F "type=all" \
-F "model_precision=0.8" \
-F "render_thumbnail=true"
type=vsplat 或 type=all 下添加 -F "enhance=true"。单独使用 enhance 不生效;vsplat 费用变为固定 30 积分,而非 25:
curl -X POST "https://apis.viggle.ai/v1/characters" \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F "[email protected]" \
-F "type=vsplat" \
-F "enhance=true"
joint_set 默认为 body。保留表情关节时添加 -F "joint_set=expression",保留全部 441 个关节时使用 full。驱动动作也应使用相同的 joint_set:
curl -X POST "https://apis.viggle.ai/v1/characters" \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F "[email protected]" \
-F "type=vsplat" \
-F "joint_set=expression"
下一步
使用获取角色轮询返回的 ID,等待就绪。若请求了 vsplat,再通过导出 3D 角色下载。授权
服务端 SDK 使用项目 API 密钥,Remote MCP 使用 OAuth 访问令牌。不要在浏览器代码中暴露项目密钥。
请求头
可选的调用者自定义关联 ID,最长 128 字符。服务会在响应头返回实际使用的值,便于追踪和支持排查。
1 - 128可选来源标签,最长 128 字符,用于识别 SDK、集成、产品入口或内部工作流。
1 - 128请求体
image 与 image_url 必须二选一。type 选择输出,详见创建角色。其后的提取参数仅适用于 vsplat 或 all。工作流路径、输出 URI、多视图、缩略图相机及姿势、渲染来源和静止模式均由服务管理。
直接上传的角色源图片,支持 PNG、JPEG、WebP。与 image_url 二选一。
可公开访问的图片 HTTP(S) URL。替代 image,并在开始接收素材时保持可访问。
详情和列表中显示的名称,便于识别角色,不影响生成。
render 创建 2D 角色(1 积分),vsplat 创建 3D 模型(25 积分,增强时 30),all 同时创建(26 或 31 积分)。
render, vsplat, all 是否对 vsplat 提取执行额外的 AI 增强。仅 vsplat/all 生效;render 下不生效、不收费。将 vsplat 费用由 25 改为 30 积分。
vsplat 绑定的骨架:full、expression 或 body,不影响价格。
expression, body, full 模型提取精度,范围 (0, 1]。在编码之前的 PKL 提取阶段生效。
0 < x <= 1编码前剔除低重要性高斯点的总开关。为 true 时移除低于提取服务质量阈值的点。
是否在提取时生成标准角色预览缩略图。渲染来源和姿势由服务内部选择。
type=vsplat 的调用者自定义幂等 ID。同一任务重试使用稳定且唯一的值;type=all 的配套提取使用新建角色 ID 作为键。
1响应
请求成功。
角色和动作共用的资源结构。列表中的 progress 为 null,顶层 error 当前始终为 null,失败请检查 status。角色 type 为 render、vsplat、all,动作为 render、glb、all,旧资源默认报告 render。vsplat/glb 仅包含提取状态,下载链接通过角色或动作的 /export 获取。未请求提取时对应字段为 null。all 需等 2D 与 3D 均就绪才为 ready;任一失败则顶层 failed,子对象状态可用于定位。
素材公开 ID。角色通常以 char_ 开头,动作通常以 mot_ 开头。
1素材整体生命周期。所需能力就绪后才能用于渲染或导出。
queued, processing, ready, failed, cancelled 创建时提供或导入时生成的显示名称;不支持名称的流程可能返回空字符串。
详情中尽可能提供 0–100 的处理百分比,列表中或不可用时为 null。
0 <= x <= 100素材具备的能力。就绪且可渲染时包含 video_render。
带 UTC 偏移的 ISO 8601 时间,例如 2026-07-31T09:15:22+00:00。
所有请求处理进入终态时的 ISO 8601 时间,带 UTC 偏移;处理中为 null。
保留的顶层错误详情,目前始终为 null。通过 status 判断失败,并在适用时检查提取子对象。
Show child attributes
Show child attributes
创建时请求的输出。render 为可渲染的 2D 素材,vsplat/glb 为 3D 输出,all 同时请求两种支持的输出。
render, vsplat, glb, all 角色 vsplat 提取状态。动作及 type=render 的角色为 null。这里只返回状态,不回显 model_precision 等创建参数。
Show child attributes
Show child attributes
动作 3D 动画提取或生成状态。角色及 type=render 的动作为 null。
Show child attributes
Show child attributes

