Creative Lab — 键帽 API
将一张原始照片转换为全彩定制机械键盘键帽,分为两个阶段:prototype(原型) 阶段会根据您输入的照片生成一张“已完成键帽”的设计效果图。确认该效果图后,build(构建) 阶段会在一次运行中将其转换为带纹理的 3D 键帽模型——白模生成、在校准后的默认姿态下自动定位与切割、全模型上色以及最终装配,都在同一个构建任务内完成。这两个阶段通过 input_task_id 与 candidate_id 相互关联。
POST /openapi/creative-lab/keycap/v1/prototypePOST /openapi/creative-lab/keycap/v1/build
两个 POST endpoint 均需要付费的 subscription 计划。免费计划账户发起的请求将被拒绝,并返回
402 Payment Required。
创建键帽原型任务
根据源照片生成成品键帽设计渲染图。任务结果携带一个 image_urls 数组(成品键帽的展示渲染图)和一个并行的 candidate_ids 数组;两者都只包含一个条目。如果结果不是你想要的,可以再次调用此 endpoint 以获得另一张渲染图——每次调用都会单独计费。将 candidate_id 连同原型任务 ID 一起传递给构建 endpoint。
有关响应格式,请参阅
键帽原型任务对象。
参数
- Name
- image_url
- Type
- string
- 必选
- Description
供 Meshy 转换为键帽设计图像的源照片。我们目前支持
.jpg、.jpeg、.png和.webp格式。格式是通过解码图像数据来检测的,而不是根据 URL 的文件扩展名——一个没有扩展名的 URL,或是一个会重定向的 URL,只要其字节能解码为受支持的格式,就同样可用。系统会跟随 HTTP 重定向。EXIF 方向信息会被规范化,因此旋转过的手机照片会按其显示的方向被使用。
限制条件:每边至少
32像素,总像素数最多178,956,970,下载后的字节数最多20,000,000。对于 data URI,该限制适用于解码后的字节,因此源文件本身可以达到该大小上限——base64 文本本身会大约多出三分之一,这会影响你的请求体大小,但不影响此限制。data URI 必须声明image/*内容类型和;base64。提供图像有两种方式:
- 可公开访问的 URL:可从公共互联网访问的 URL。
- Data URI:图像的 base64 编码 data URI。data URI 示例:
data:image/jpeg;base64,<your base64-encoded image data>。
- Name
- name
- Type
- string
- Description
用于显示的可选任务名称。最多 100 个字符。
- Name
- remove_background
- Type
- boolean
- 默认值 false
- Description
当设置为
true时,image_urls中返回的展示渲染图是一张已移除背景的透明 RGBA PNG,以便你可以将其合成到任意背景上。这仅适用于展示渲染图。构建 endpoint 所使用的候选项不受影响,因此无论哪种方式,3D 结果都是相同的。
返回值
响应的 result 属性包含新创建的键帽原型任务的任务 id。轮询获取任务 endpoint,或订阅流式接口,直到任务达到 SUCCEEDED 状态,然后取出 candidate_ids 中的条目,连同任务 ID 一起传递给构建 endpoint。
失败模式
- Name
400 - Bad Request- Description
请求不可接受。常见原因:
- 缺少参数:
image_url为必填项。 - 图像格式无效:提供的
image_url不是受支持的格式(.jpg、.jpeg、.png、.webp)。 - 图像尺寸超出范围:图像过小、超过最大文件大小,或超过最大像素数。
- URL 不可访问:无法下载
image_url(404 或 timeout)。 - Data URI 无效:base64 字符串格式有误。
- 内容被标记:输入图像被 NSFW moderation 标记。
- 缺少参数:
- Name
401 - Unauthorized- Description
身份验证(authentication)失败。请检查你的 API key。
- Name
402 - Payment Required- Description
该账户处于免费计划(创建任务需要付费计划),或 credits 不足。
- Name
403 - Forbidden- Description
输入图像被知识产权 moderation 标记。
- Name
429 - Too Many Requests- Description
你已超出速率限制。
- Name
500 - Internal Server Error- Description
发生了意外的服务器端错误——例如内容审核服务不可用、暂存输入图像失败,或任务无法创建。在这种情况下不会创建任务,因此重试是安全的。
Request
# Stage 1: generate a finished-keycap design render
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype \
-X POST \
-H "Authorization: Bearer ${YOUR_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"image_url": "<your publicly accessible image url or base64-encoded data URI>"
}'
Response
{
"result": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef"
}
创建键帽构建任务
从一个成功的原型任务及其某个候选项生成最终的带纹理 3D 键帽模型。单个构建任务端到端地运行整个流程——根据所选设计生成白模、使用经过校准的默认姿态自动将其放置并裁切到键帽底座上(无需交互式调整)、对完整模型进行上色,以及最终的组装与导出。构建通常需要 3–7 分钟,当多个构建并发运行时会趋向时间上限。 有关响应结构,请参阅键帽构建任务对象。
参数
- Name
- input_task_id
- Type
- string
- 必选
- Description
通过同一个 OpenAPI endpoint 创建的原型任务的任务 ID。该原型必须由同一个 Meshy 账户创建,必须已达到
SUCCEEDED状态,并且必须至少生成了一个候选项。通过 webapp 创建的原型任务不被接受——构建 endpoint 只接受由
POST /openapi/creative-lab/keycap/v1/prototype生成的原型任务,其他任何来源都会被以404拒绝。
- Name
- candidate_id
- Type
- string
- 必选
- Description
要构建的候选项,取自成功的原型任务的
candidate_ids数组。必须属于该任务;任何其他值都会被以400拒绝。
- Name
- name
- Type
- string
- Description
用于展示的可选任务名称。最多 100 个字符。
options
可选的 geometry 调整。每个字段都有一个经过校准的默认值——只需发送你想覆盖的字段即可。
- Name
- base_model
- Type
- string
- 默认值 cherry-mx-1x1-r1
- Description
用于构建的键帽底座。目前唯一可用的值是
cherry-mx-1x1-r1——一个标准的 Cherry MX 轮廓 1u 键帽。计划中会新增 3–5 种主流标准尺寸;不支持自定义尺寸。
- Name
- head_size_mm
- Type
- number
- 默认值 23
- Description
雕刻头部的目标尺寸,单位为毫米:其最长维度会被缩放到该值。范围:
[10, 40]。大于约32.9的值可能会被缩小,以使头部仍能符合底座的保护性外形限制,因此实际交付的最长维度可能小于请求值。目前任务对象上不会回显实际应用的值——如果你需要确认实际获得的尺寸,请测量下载模型中keycap-headmesh 的包围盒。
- Name
- vertical_offset_mm
- Type
- number
- 默认值 0
- Description
在头部被放置到底座上之前对其施加的垂直偏移量,单位为毫米。范围:
[-5, 5]。
返回值
响应的 result 属性包含新创建的键帽构建任务的任务 id。轮询 获取任务 endpoint 或订阅 流,直到任务达到 SUCCEEDED,然后从 model_urls.glb 和 model_urls.obj_zip 下载产物。
GLB 和 OBJ 压缩包均以真实世界的毫米比例导出,采用 Y 轴朝上的坐标系,且键帽正面朝向 +Z。
失败模式
- Name
400 - Bad Request- Description
请求不可接受。常见原因:
- 缺少参数:
input_task_id和candidate_id为必填项。 - UUID 无效:
input_task_id不是有效的 UUID。 - 父任务尚未成功:所引用的原型任务尚未达到
SUCCEEDED。 - 没有候选项:原型任务已成功,但未产生任何候选项。
- 未知的候选项:
candidate_id不属于输入任务的候选项之一。 - 选项超出范围:
options中的某个字段超出了其允许的范围或枚举集合。
- 缺少参数:
- Name
401 - Unauthorized- Description
authentication 失败。请检查你的 API key。
- Name
402 - Payment Required- Description
该账户处于免费计划(创建任务需要付费计划)或 credits 不足。
- Name
404 - Not Found- Description
所引用的原型任务不存在、属于其他用户,或是通过 webapp 创建的(只有 API 模式下的原型任务才能串联进入构建阶段)。
- Name
429 - Too Many Requests- Description
你已超出速率限制。
- Name
500 - Internal Server Error- Description
发生了意外的服务器端错误——例如内容审核(moderation)服务不可用、暂存输入图片失败,或任务无法创建。在这种情况下不会创建任何任务,因此可以安全地重试。
Request
# Stage 2: build the chosen candidate into a 3D keycap
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build \
-X POST \
-H "Authorization: Bearer ${YOUR_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"input_task_id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"candidate_id": "0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b",
"options": {
"base_model": "cherry-mx-1x1-r1",
"head_size_mm": 23,
"vertical_offset_mm": 0
}
}'
Response
{
"result": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af"
}
获取 Keycap 任务
根据有效的任务 id 获取一个原型(prototype)或构建(build)任务。URL 路径
必须与任务所处的阶段相匹配——通过 /prototype/:id 获取
构建任务会返回 404,反之亦然。
响应结构请参考 Keycap 原型任务对象 和 Keycap 构建任务对象。
参数
- Name
- id
- Type
- path
- Description
要获取的 keycap 任务的唯一标识符。
返回值
响应中包含 keycap 任务对象。具体结构取决于所请求的 阶段。
Request
# Prototype
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef \
-H "Authorization: Bearer ${YOUR_API_KEY}"
# Build
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Prototype Response
{
"id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"type": "creative-lab-keycap-prototype",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142456000,
"started_at": 1753142460000,
"finished_at": 1753142516000,
"expires_at": 1753401716000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 12,
"image_urls": [
"https://assets.meshy.ai/***/design-1.png?Expires=***"
],
"candidate_ids": [
"0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
]
}
Build Response
{
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"type": "creative-lab-keycap-build",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142600000,
"started_at": 1753142610000,
"finished_at": 1753143050000,
"expires_at": 1753402250000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 50,
"model_urls": {
"glb": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***",
"obj_zip": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***"
},
"process_image_urls": {
"head_design": "https://assets.meshy.ai/***/head-design.png?Expires=***",
"composite": "https://assets.meshy.ai/***/composite.png?Expires=***",
"base_canvas": "https://assets.meshy.ai/***/base-canvas.png?Expires=***"
}
}
删除一个 Keycap 任务
取消一个 keycap 任务。如果任务仍处于 PENDING 状态,创建时消耗的
credits 将被退还。已经处于 IN_PROGRESS 状态的任务会被取消,但不会退款
(worker 可能已经在消耗资源)。已经到达终止状态
(SUCCEEDED、FAILED、CANCELED)的任务无法被取消。
URL 路径必须与任务所处的阶段匹配 —— 对
/prototype/:buildId 执行 DELETE 会返回 404。
路径参数
- Name
- id
- Type
- path
- Description
要取消的 keycap 任务的唯一标识符。
返回值
成功时返回 204 No Content,响应体为空。
失败模式
- Name
400 - Bad Request- Description
该任务已处于终止状态,无法被取消。
- Name
404 - Not Found- Description
该任务不存在、属于其他用户,或其所处阶段与 URL 路径不匹配。
- Name
500 - Internal Server Error- Description
在取消过程中发生了意外的服务器端错误。该任务可能已被取消,也可能未被取消——请在重试之前重新读取以确认。
Request
curl --request DELETE \
--url https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Response
// Returns 204 No Content on success (empty body).
流式获取 Keycap 任务
通过 Server-Sent Events (SSE) 流式获取 keycap 任务的实时更新。
URL 路径必须与任务所处的阶段一致——在
/prototype/:buildId/stream 处打开流会发出一个携带
status_code: 404 的 event: error payload 并关闭该流。
参数
- Name
- id
- Type
- path
- Description
要流式获取的 keycap 任务的唯一标识符。
返回
以 Server-Sent Events 的形式返回一系列 Keycap Prototype
或 Keycap Build 任务对象。每一帧都携带该阶段的完整任务对象——与
Get endpoint 返回的形状相同——因此当任务处于 PENDING 或 IN_PROGRESS 状态时,
输出字段只是尚未填充(为 null、[] 或 {}),且
finished_at 为 null。
Request
curl -N https://api.meshy.ai/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/stream \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Response Stream
// Error event example (wrong stage or task not found)
event: error
data: {
"status_code": 404,
"message": "Task not found"
}
// Message event examples illustrate task progress.
// Every frame is the full task object; fields not yet populated are null / empty.
// The PENDING frame below is abbreviated to the fields that change.
event: message
data: {
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"progress": 0,
"status": "PENDING"
}
event: message
data: {
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"type": "creative-lab-keycap-build",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142600000,
"started_at": 1753142610000,
"finished_at": 1753143050000,
"expires_at": 1753402250000,
"task_error": null,
"consumed_credits": 50,
"model_urls": {
"glb": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***",
"obj_zip": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***"
},
"process_image_urls": {
"head_design": "https://assets.meshy.ai/***/head-design.png?Expires=***"
}
}
列出 Keycap 任务
检索单个阶段的 keycap 任务的分页列表。URL 路径用于选择阶段——/prototype 返回原型(prototype)任务;
/build 返回构建(build)任务。另一个阶段的任务不会包含在任一响应中。
路径参数
- Name
- stage
- Type
- path
- 必选
- Description
prototype或build之一。该集合只返回阶段与 URL 匹配的任务——请求/prototype永远不会返回构建任务,反之亦然。
查询参数
- Name
- page_num
- Type
- integer
- 默认值 1
- Description
用于分页的页码。
- Name
- page_size
- Type
- integer
- 默认值 10
- Description
每页数量限制。最大允许值为
100项。
- Name
- sort_by
- Type
- string
- 默认值 -created_at
- Description
用于排序的字段。可用值:
+created_at:按创建时间升序排序。-created_at:按创建时间降序排序。
返回
返回该阶段对应任务对象的分页列表——当列出 /prototype 时为
keycap 原型任务对象,
当列出 /build 时为
keycap 构建任务对象。
Request
# List prototype tasks
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype?page_size=10 \
-H "Authorization: Bearer ${YOUR_API_KEY}"
# List build tasks
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build?page_size=10 \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Response (List Prototype Tasks)
[
{
"id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"type": "creative-lab-keycap-prototype",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142456000,
"started_at": 1753142460000,
"finished_at": 1753142516000,
"expires_at": 1753401716000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 12,
"image_urls": [
"https://assets.meshy.ai/***/design-1.png?Expires=***"
],
"candidate_ids": [
"0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
]
}
]
键帽原型任务对象
键帽原型任务对象是 Meshy 用来跟踪的一个工作单元,用于根据源照片生成一张成品键帽设计图像。此阶段的输出会通过 input_task_id 和 candidate_id 链接到构建阶段。
属性
- Name
- id
- Type
- string
- Description
任务的唯一标识符。虽然我们在实现细节上使用了 k-可排序的 UUID 作为任务 id,但你不应该对 id 的格式做任何假设。
- Name
- type
- Type
- string
- Description
任务的类型。取值为
creative-lab-keycap-prototype。
- Name
- name
- Type
- string
- Description
创建任务时提供的任务名称。如果未提供名称,则为空字符串。
- Name
- status
- Type
- string
- Description
任务的状态。可能的取值为
PENDING、IN_PROGRESS、SUCCEEDED、FAILED、CANCELED之一。
- Name
- progress
- Type
- integer
- Description
任务的进度。如果任务尚未开始,此属性为
0。任务成功后,此值将变为100。
- Name
- created_at
- Type
- timestamp
- Description
任务创建时的 timestamp,以毫秒为单位。
timestamp 表示自 1970 年 1 月 1 日 UTC 起经过的毫秒数,遵循 RFC 3339
标准。 例如,2023 年 9 月 1 日星期五 GMT 时间中午 12:00:00 表示为1693569600000。这适用于 Meshy API 中的所有 timestamp。
- Name
- started_at
- Type
- timestamp
- Description
任务开始时的 timestamp,以毫秒为单位。如果任务尚未开始,此属性为
0。
- Name
- finished_at
- Type
- timestamp
- Description
任务完成时的 timestamp,以毫秒为单位。如果任务尚未完成,此属性为
0。
- Name
- expires_at
- Type
- timestamp
- Description
任务结果过期时的 timestamp,以毫秒为单位。
- Name
- preceding_tasks
- Type
- integer
- Description
排在前面的任务数量。
此字段的值仅在任务状态为
PENDING时才有意义。
- Name
- task_error
- Type
- object
- Description
失败任务的错误详情。完整的
task_error对象参考请参阅错误。
- Name
- consumed_credits
- Type
- integer
- Description
此任务消耗的 credits 数量。达到
SUCCEEDED状态的任务将按其所处阶段收取全额费用。从未成功创建的任务(请求时返回4xx,包括 moderation 拒绝)完全不收费。达到FAILED状态的任务返回0——费用会被退回,包括异步 moderation 阻止的情况。通过DELETE取消任务时,只有在任务仍处于PENDING状态时才会退款;已经处于IN_PROGRESS状态的任务仍将被收费,因为相应的工作已经产生。
- Name
- image_urls
- Type
- array of strings
- Description
成品键帽设计渲染图的可下载 URL——展示该候选方案作为成品键帽的效果。仅包含一个条目;
image_urls[i]对应candidate_ids[i]。在任务达到SUCCEEDED之前为空。此 URL 仅用于展示;构建 endpoint 消费的是candidate_ids,而非这些 URL。与model_urls拥有相同的 URL 生命周期:经过签名、无需Authorization请求头、在expires_at之前有效,并且在重新读取任务时保持稳定。
- Name
- candidate_ids
- Type
- array of strings
- Description
不透明的候选标识符,与
image_urls一一对应。请将与你所选设计相匹配的条目作为构建请求的candidate_id传入。不要对这些 id 的格式做任何假设。
Example Keycap Prototype Task Object
{
"id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"type": "creative-lab-keycap-prototype",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142456000,
"started_at": 1753142460000,
"finished_at": 1753142516000,
"expires_at": 1753401716000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 12,
"image_urls": [
"https://assets.meshy.ai/***/design-1.png?Expires=***"
],
"candidate_ids": [
"0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
]
}
键帽构建任务对象
键帽构建任务对象是 Meshy 用于跟踪的一个工作单元,用于根据一个成功的原型任务和一个选定的候选方案生成最终的带纹理 3D 键帽。单次构建会运行完整流水线——白模生成、自动落座与切割、上色、组装和导出。
属性
- Name
- id
- Type
- string
- Description
任务的唯一标识符。
- Name
- type
- Type
- string
- Description
任务的类型。此值为
creative-lab-keycap-build。
- Name
- name
- Type
- string
- Description
创建任务时提供的任务名称。如果未提供名称,则为空字符串。
- Name
- status
- Type
- string
- Description
任务的状态。可能的值为
PENDING、IN_PROGRESS、SUCCEEDED、FAILED、CANCELED之一。
- Name
- progress
- Type
- integer
- Description
任务的进度。如果任务尚未开始,此属性将为
0。一旦任务成功,此值将变为100。
- Name
- created_at
- Type
- timestamp
- Description
任务创建时的时间戳,以毫秒为单位。
- Name
- started_at
- Type
- timestamp
- Description
任务开始时的时间戳,以毫秒为单位。
- Name
- finished_at
- Type
- timestamp
- Description
任务完成时的时间戳,以毫秒为单位。
- Name
- expires_at
- Type
- timestamp
- Description
任务结果过期时的时间戳,以毫秒为单位。
- Name
- preceding_tasks
- Type
- integer
- Description
排在前面的任务数量。仅当状态为
PENDING时有意义。
- Name
- task_error
- Type
- object
- Description
失败任务的错误详情。有关完整的
task_error对象参考,请参阅 Errors。
- Name
- consumed_credits
- Type
- integer
- Description
此任务消耗的 credits 数量。达到
SUCCEEDED状态的任务将按其阶段的全额收取费用。从未成功创建的任务(请求时返回4xx,包括被 moderation 拒绝的情况)完全不收费。达到FAILED状态的任务返回0——费用会被退还,包括异步 moderation 阻止的情况。通过DELETE取消任务仅在任务仍处于PENDING状态时才会退款;已处于IN_PROGRESS状态的任务仍会被收费,因为相关工作已经付出。
- Name
- model_urls
- Type
- object
- Description
生成的模型产物的可下载 URL。GLB 和 OBJ 压缩包均以真实世界毫米比例导出,Y 轴朝上,键帽正面朝向 +Z。网格命名为
keycap-head和keycap-base;当底座回退为图案填充时,还会包含第三个网格keycap-base-interior,用于表示轴柄腔体。请勿假定网格数量固定为两个。这些是签名 URL:请求时不要携带
Authorization请求头。它们在expires_at之前保持有效,即finished_at之后 3 天;在此期间重新读取任务会返回相同的 URL,而不是重新签名的新 URL。请在此之前自行下载并保存文件——过期链接无法刷新。- Name
glb- Type
- string
- Description
最终带纹理的
model.glb的可下载 URL。
- Name
obj_zip- Type
- string
- Description
包含
model.obj、model.mtl以及其 MTL 实际引用的 texture PNG 文件的压缩包的可下载 URL。纯色底座仅提供keycap-head.png;带图案的底座还会提供keycap-base.png。
- Name
- process_image_urls
- Type
- object
- Description
中间过程图像的可下载 URL,以种类作为键。与
model_urls具有相同的 URL 生命周期:已签名、无需Authorization请求头、在expires_at之前有效,且在重新读取任务时保持稳定。当前发出的种类包括:head_design—— 构建所使用的所选候选方案的设计图像(始终存在)。composite—— 所选候选方案的成品键帽展示渲染图(在可用时存在)。base_canvas—— 已绘制的键帽底座画布(在可用时存在)。
请将该键集视为开放式的;未来可能会新增种类,且不构成破坏性变更。
Example Keycap Build Task Object
{
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"type": "creative-lab-keycap-build",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142600000,
"started_at": 1753142610000,
"finished_at": 1753143050000,
"expires_at": 1753402250000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 50,
"model_urls": {
"glb": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***",
"obj_zip": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***"
},
"process_image_urls": {
"head_design": "https://assets.meshy.ai/***/head-design.png?Expires=***",
"composite": "https://assets.meshy.ai/***/composite.png?Expires=***",
"base_canvas": "https://assets.meshy.ai/***/base-canvas.png?Expires=***"
}
}
端到端示例
完整流程:从一张照片创建原型(prototype),轮询直到状态变为
SUCCEEDED,从 candidate_ids 中选择一个候选项,使用该候选项创建构建(build),
轮询构建直到状态变为 SUCCEEDED,然后从 model_urls 中下载 GLB 和
OBJ 压缩包。
该示例以编程方式选择第一个候选项。在实际集成中,你会将 image_urls
中的条目展示给最终用户并让他们选择;所选的索引与 candidate_ids 一一对应。
Complete flow
#!/usr/bin/env bash
set -euo pipefail
# Requires curl and jq. Point IMAGE_PATH at a local photo, or IMAGE_URL at a public one:
# export MESHY_API_KEY=msy_...
# export IMAGE_PATH=./portrait.jpg # or: export IMAGE_URL=https://...
: "${MESHY_API_KEY:?export MESHY_API_KEY first}"
if [[ -z "${IMAGE_PATH:-}" && -z "${IMAGE_URL:-}" ]]; then
echo "export IMAGE_PATH (local file) or IMAGE_URL (public url) first" >&2
exit 1
fi
BASE="https://api.meshy.ai/openapi/creative-lab/keycap/v1"
AUTH="Authorization: Bearer $MESHY_API_KEY"
# api METHOD URL [curl args...] -> prints the response body, non-zero on failure.
# Note we do not use -f/--fail: it discards the body, and the body is the only
# place the reason appears.
api() {
local method=$1 url=$2 out http_code body
shift 2
out=$(curl --silent --show-error --max-time 60 --write-out $'\n%{http_code}' \
-X "$method" "$url" -H "$AUTH" "$@") || return 1
http_code=${out##*$'\n'}
body=${out%$'\n'*}
if ((http_code >= 400)); then
echo "HTTP $http_code for $url: $body" >&2
return 1
fi
printf '%s' "$body"
}
# Each task gets its own 40-minute budget.
poll() {
local kind=$1 id=$2 delay=5 task_status deadline
deadline=$(($(date +%s) + 2400))
while :; do
if (($(date +%s) >= deadline)); then
echo "gave up waiting for $kind $id" >&2
return 1
fi
task_status=$(api GET "$BASE/$kind/$id" | jq -r '.status')
echo "$kind: $task_status"
case "$task_status" in
SUCCEEDED) return 0 ;;
FAILED | CANCELED) return 1 ;;
esac
sleep "$delay"
delay=$((delay * 2 > 30 ? 30 : delay * 2))
done
}
# Build the request body in a file. A base64 data URI must never go on the
# command line or into an exported variable - a photo of any real size will
# exceed the OS argument limit.
BODY=$(mktemp)
trap 'rm -f "$BODY"' EXIT
if [[ -n "${IMAGE_PATH:-}" ]]; then
# Declare the real type: the API accepts JPEG, PNG and WebP.
case "$(printf '%s' "${IMAGE_PATH##*.}" | tr 'A-Z' 'a-z')" in
png) MIME=image/png ;;
webp) MIME=image/webp ;;
*) MIME=image/jpeg ;;
esac
{
printf '{"image_url":"data:%s;base64,' "$MIME"
base64 <"$IMAGE_PATH" | tr -d '\n'
printf '"}'
} >"$BODY"
else
printf '{"image_url":"%s"}' "$IMAGE_URL" >"$BODY"
fi
# 1. Create the prototype task
PROTO_ID=$(api POST "$BASE/prototype" \
-H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')
# 2. Wait for the design render
poll prototype "$PROTO_ID"
# 3. Pick a candidate (first one here; show image_urls to a user in production)
CANDIDATE_ID=$(api GET "$BASE/prototype/$PROTO_ID" | jq -r '.candidate_ids[0]')
# 4. Create the build task
jq -n --arg p "$PROTO_ID" --arg c "$CANDIDATE_ID" \
'{input_task_id: $p, candidate_id: $c}' >"$BODY"
BUILD_ID=$(api POST "$BASE/build" \
-H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')
# 5. Wait for the model (a build usually takes 3-7 minutes)
poll build "$BUILD_ID"
# 6. Download the artifacts. These are signed URLs: no Authorization header,
# and they stay valid for 3 days after the task finishes.
TASK=$(api GET "$BASE/build/$BUILD_ID")
curl --silent --show-error --fail --max-time 900 \
-o keycap.glb "$(jq -r '.model_urls.glb' <<<"$TASK")"
curl --silent --show-error --fail --max-time 900 \
-o keycap-obj.zip "$(jq -r '.model_urls.obj_zip' <<<"$TASK")"
echo "Done: keycap.glb + keycap-obj.zip"