创意工坊 — 键帽 API

把你的照片变成全彩定制机械键盘键帽。整个流程分两段:prototype 用源照片 生成 1 张「成品键帽」设计效果图,确认后 build 一条龙生成带贴图的 3D 键帽模型 —— 白模生成、按校准好的默认姿态自动落座与切割、整体上色、组装导出 全部在一个 build 任务内完成。两段通过 input_task_idcandidate_id 串联。

  • POST /openapi/creative-lab/keycap/v1/prototype
  • POST /openapi/creative-lab/keycap/v1/build

POST/openapi/creative-lab/keycap/v1/prototype

创建键帽 Prototype 任务

从源照片生成 1 张成品键帽设计效果图。任务结果携带 image_urls 数组(成品键帽的展示渲染图)以及与之平行的 candidate_ids 数组,各含 1 项。 若效果不满意,可再次调用本接口重新生成(每次调用单独计费)。把图片展示 给用户挑选后,将对应的 candidate_id 与 prototype 任务 ID 一起传给 build 端点。响应结构详见 Prototype 任务对象

参数

  • Name
    image_url
    Type
    string
    必选
    Description

    提供给 Meshy 用于生成键帽设计图的源照片。目前支持 .jpg.jpeg.png.webp 格式。

    格式由解码图片数据判定,不看 URL 的文件扩展名 —— 没有扩展名、或会发生跳转的 URL 都可以,只要字节能解码成受支持的格式。HTTP 跳转会被跟随。EXIF 方向会被归一化,所以手机拍的旋转照片会按看起来的方向使用。

    限制:每边至少 32 像素、总像素不超过 178,956,970、下载后不超过 20,000,000 字节。data URI 的上限按 base64 解码后的字节计算,所以源文件本身可以达到该上限 —— 变大约三分之一的是 base64 文本,那影响的是请求体大小,不是这个上限。data URI 必须声明 image/* 内容类型与 ;base64

    可通过两种方式提供图片:

    • 公网可访问 URL:可从公网访问的 URL。
    • Data URI:图片的 base64 编码 data URI,示例:data:image/jpeg;base64,<your base64-encoded image data>
  • Name
    name
    Type
    string
    Description

    可选的任务名称,仅用于展示。最长 100 个字符。

返回值

响应的 result 字段返回新创建的键帽 prototype 任务的 id。轮询 查询任务 或订阅 流式更新 直到任务状态变为 SUCCEEDED,再从 candidate_ids 中选定一个候选, 连同任务 ID 一起传给 build 端点

失败情形

  • Name
    400 - Bad Request
    Description

    请求不可接受。常见原因:

    • 缺少参数image_url 为必填。
    • 图片格式不支持image_url 不是受支持的格式(.jpg.jpeg.png.webp)。
    • 图片尺寸超限:图片过小、超出最大文件体积或超出最大像素数。
    • URL 无法访问image_url 下载失败(404 或超时)。
    • Data URI 无效:base64 字符串格式错误。
    • 内容被标记:输入图片被 NSFW 审核标记。
  • Name
    401 - Unauthorized
    Description

    鉴权失败。请检查你的 API key。

  • Name
    402 - Payment Required
    Description

    账号处于免费套餐(创建任务需要付费套餐),或积分不足。

  • Name
    403 - Forbidden
    Description

    输入图片被知识产权审核标记。

  • Name
    429 - Too Many Requests
    Description

    超出速率限制。

  • Name
    500 - Internal Server Error
    Description

    服务端发生未预期的错误,例如内容审核服务不可用、输入图片暂存失败,或任务创建失败。此时不会创建任务,可安全重试。

Request

POST
/openapi/creative-lab/keycap/v1/prototype
# 第一段:生成 1 张成品键帽设计效果图
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"
}

POST/openapi/creative-lab/keycap/v1/build

创建键帽 Build 任务

从一个已成功的 prototype 任务及其选定候选生成最终的带贴图 3D 键帽模型。 单个 build 任务一条龙跑完整条流水线 —— 用选定设计图生成白模、按校准好的 默认姿态自动落座并切割到键帽底座(无需交互调整)、整体上色、最终组装导出。 一次 build 通常需要 3–7 分钟,多个 build 并发时接近上限。响应结构详见 Build 任务对象

参数

  • Name
    input_task_id
    Type
    string
    必选
    Description

    通过本 OpenAPI 端点创建的 prototype 任务 ID。该 prototype 必须由同一个 Meshy 账号创建、已达到 SUCCEEDED 状态,且至少产出一个候选。

    通过网页版创建的 prototype 任务不被接受 —— build 端点只接受由 POST /openapi/creative-lab/keycap/v1/prototype 产出的 prototype 任务,其他来源一律返回 404

  • Name
    candidate_id
    Type
    string
    必选
    Description

    要构建的候选,取自已成功 prototype 任务的 candidate_ids 数组。必须是该任务的候选之一,否则返回 400

  • Name
    name
    Type
    string
    Description

    可选的任务名称,仅用于展示。最长 100 个字符。

options

可选的几何调节参数。每个字段都有校准好的默认值 —— 只发送需要覆盖的字段即可。

  • 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-head 网格的包围盒。

  • Name
    vertical_offset_mm
    Type
    number
    默认值 0
    Description

    头部落座到底座前施加的垂直偏移,单位毫米。范围:[-5, 5]

返回值

响应的 result 字段返回新创建的键帽 build 任务的 id。轮询 查询任务 或订阅 流式更新 直到任务状态变为 SUCCEEDED,再从 model_urls.glbmodel_urls.obj_zip 下载产物。

失败情形

  • Name
    400 - Bad Request
    Description

    请求不可接受。常见原因:

    • 缺少参数input_task_idcandidate_id 为必填。
    • UUID 无效input_task_id 不是合法的 UUID。
    • 父任务未成功:引用的 prototype 任务尚未达到 SUCCEEDED
    • 没有候选:prototype 任务虽成功但未产出候选。
    • 候选不存在candidate_id 不是该 prototype 任务的候选之一。
    • 参数超出范围options 中某字段超出允许范围或枚举集合。
  • Name
    401 - Unauthorized
    Description

    鉴权失败。请检查你的 API key。

  • Name
    402 - Payment Required
    Description

    账号处于免费套餐(创建任务需要付费套餐),或积分不足。

  • Name
    404 - Not Found
    Description

    引用的 prototype 任务不存在、属于其他用户,或由网页版创建(只有 API 模式的 prototype 任务才能串联 build)。

  • Name
    429 - Too Many Requests
    Description

    超出速率限制。

  • Name
    500 - Internal Server Error
    Description

    服务端发生未预期的错误,例如内容审核服务不可用、输入图片暂存失败,或任务创建失败。此时不会创建任务,可安全重试。

Request

POST
/openapi/creative-lab/keycap/v1/build
# 第二段:把选定候选构建成 3D 键帽
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"
}

GET/openapi/creative-lab/keycap/v1/(prototype|build)/:id

查询键帽任务

通过有效的任务 id 查询 prototype 或 build 任务。URL 路径必须与任务所属 阶段匹配 —— 通过 /prototype/:id 查询 build 任务会返回 404,反之亦然。

响应结构详见 Prototype 任务对象Build 任务对象

参数

  • Name
    id
    Type
    path
    Description

    要查询的键帽任务的唯一标识符。

返回值

响应包含键帽任务对象,具体结构取决于所查询的阶段。

Request

GET
/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef
# 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=***"
  }
}

DELETE/openapi/creative-lab/keycap/v1/(prototype|build)/:id

删除键帽任务

取消一个键帽任务。若任务仍处于 PENDING,创建时消耗的积分将退还;已进入 IN_PROGRESS 的任务会被取消但不退积分(worker 可能已经在消耗资源);已到达 终态(SUCCEEDEDFAILEDCANCELED)的任务无法取消。

URL 路径必须与任务所属阶段匹配 —— 对 /prototype/:buildId 执行 DELETE 会返回 404

路径参数

  • Name
    id
    Type
    path
    Description

    要取消的键帽任务的唯一标识符。

返回值

成功时返回 204 No Content,响应体为空。

失败情形

  • Name
    400 - Bad Request
    Description

    任务已处于终态,无法取消。

  • Name
    404 - Not Found
    Description

    任务不存在、属于其他用户,或所属阶段与 URL 路径不匹配。

  • Name
    500 - Internal Server Error
    Description

    取消过程中服务端发生未预期的错误。任务可能已取消也可能未取消,重试前请重新读取任务确认状态。

Request

DELETE
/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef
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

// 成功时返回 204 No Content(空响应体)。

GET/openapi/creative-lab/keycap/v1/(prototype|build)/:id/stream

流式订阅键帽任务

通过 Server-Sent Events(SSE)实时订阅键帽任务的状态更新。URL 路径必须与 任务所属阶段匹配 —— 在 /prototype/:buildId/stream 上打开流会发出一条 event: errorstatus_code: 404)后关闭。

参数

  • Name
    id
    Type
    path
    Description

    要订阅的键帽任务的唯一标识符。

返回值

以 Server-Sent Events 形式返回 Prototype 任务对象Build 任务对象 的流。对 PENDINGIN_PROGRESS 的任务,流中只包含必要的 progressstatus 字段。

Request

GET
/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/stream
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: error
data: {
  "status_code": 404,
  "message": "Task not found"
}

// message 事件示例(任务进度)。
// 对 PENDING 或 IN_PROGRESS 的任务,流中不会包含全部字段。
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=***"
  }
}

GET/openapi/creative-lab/keycap/v1/(prototype|build)

列出键帽任务

分页列出你在单一阶段下的键帽任务。URL 路径决定阶段 —— /prototype 只返回 prototype 任务;/build 只返回 build 任务。两个响应都不会包含另一阶段的任务。

路径参数

  • Name
    stage
    Type
    path
    必选
    Description

    prototypebuild。集合只返回与 URL 匹配阶段的任务 —— 请求 /prototype 永远不会返回 build 任务,反之亦然。

查询参数

  • 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 时为 Prototype 任务对象,列出 /build 时为 Build 任务对象

Request

GET
/openapi/creative-lab/keycap/v1/prototype
# 列出 prototype 任务
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype?page_size=10 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

# 列出 build 任务
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"
    ]
  }
]

Prototype 任务对象

键帽 Prototype 任务对象是 Meshy 跟踪的一个工作单元,用于从源照片生成 1 张 成品键帽设计效果图。此阶段的产出通过 input_task_idcandidate_id 串联进 build 阶段

属性

  • Name
    id
    Type
    string
    Description

    任务的唯一标识符。实现细节上我们使用 k-sortable UUID 作为任务 id,但你不应对 id 的格式做任何假设。

  • Name
    type
    Type
    string
    Description

    任务类型。取值为 creative-lab-keycap-prototype

  • Name
    name
    Type
    string
    Description

    创建任务时提供的任务名称。未提供时为空字符串。

  • Name
    status
    Type
    string
    Description

    任务状态。取值为 PENDINGIN_PROGRESSSUCCEEDEDFAILEDCANCELED 之一。

  • Name
    progress
    Type
    integer
    Description

    任务进度。任务尚未开始时为 0;任务成功后为 100

  • Name
    created_at
    Type
    timestamp
    Description

    任务创建时间的时间戳,单位毫秒。

  • Name
    started_at
    Type
    timestamp
    Description

    任务开始时间的时间戳,单位毫秒。任务尚未开始时为 0

  • Name
    finished_at
    Type
    timestamp
    Description

    任务结束时间的时间戳,单位毫秒。任务尚未结束时为 0

  • Name
    expires_at
    Type
    timestamp
    Description

    任务结果过期时间的时间戳,单位毫秒。

  • Name
    preceding_tasks
    Type
    integer
    Description

    排在前面的任务数量。

  • Name
    task_error
    Type
    object
    Description

    失败任务的错误详情。完整的 task_error 对象参考见 Errors

  • Name
    consumed_credits
    Type
    integer
    Description

    此任务消耗的积分数。进入 SUCCEEDED 的任务按该阶段全额计费。从未被创建的任务(请求时即返回 4xx,包括内容审核拒绝)完全不计费。进入 FAILED 的任务返回 0 —— 费用会退还,异步的内容审核拦截也一样。通过 DELETE 取消仅在任务仍为 PENDING 时退款;已进入 IN_PROGRESS 的任务不退款,因为算力已经花掉了。

  • Name
    image_urls
    Type
    array of strings
    Description

    成品键帽设计效果图的下载 URL —— 展示该候选做成成品键帽后的效果。数组仅含 1 项,image_urls[i] 对应 candidate_ids[i]。任务达到 SUCCEEDED 前为空数组。该 URL 仅用于展示;build 端点消费的是 candidate_ids,而不是这些 URL。URL 生命周期与 model_urls 相同:签名链接、不需要 Authorization 头、有效至 expires_at,重复读取任务时保持不变。

  • Name
    candidate_ids
    Type
    array of strings
    Description

    image_urls 平行的不透明候选标识符。把你选中设计对应的条目作为 build 请求的 candidate_id 传入。不要对这些 id 的格式做任何假设。

Prototype 任务对象示例

{
  "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 任务对象

键帽 Build 任务对象是 Meshy 跟踪的一个工作单元,用于从已成功的 prototype 任务及选定候选生成最终的带贴图 3D 键帽。单个 build 一条龙跑完整条流水线 —— 白模生成、自动落座切割、上色、组装、导出。

属性

  • Name
    id
    Type
    string
    Description

    任务的唯一标识符。

  • Name
    type
    Type
    string
    Description

    任务类型。取值为 creative-lab-keycap-build

  • Name
    name
    Type
    string
    Description

    创建任务时提供的任务名称。未提供时为空字符串。

  • Name
    status
    Type
    string
    Description

    任务状态。取值为 PENDINGIN_PROGRESSSUCCEEDEDFAILEDCANCELED 之一。

  • 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

    此任务消耗的积分数。进入 SUCCEEDED 的任务按该阶段全额计费。从未被创建的任务(请求时即返回 4xx,包括内容审核拒绝)完全不计费。进入 FAILED 的任务返回 0 —— 费用会退还,异步的内容审核拦截也一样。通过 DELETE 取消仅在任务仍为 PENDING 时退款;已进入 IN_PROGRESS 的任务不退款,因为算力已经花掉了。

  • Name
    model_urls
    Type
    object
    Description

    生成模型产物的下载 URL。GLB 与 OBJ 产物均按真实毫米尺度导出,坐标系为 Y 轴向上,键帽正面朝向 +Z。网格命名为 keycap-headkeycap-base;当底座回退为图案填充时,还会多出第三个网格 keycap-base-interior(轴孔内壁)。请勿假设固定为两个网格。

    这些是签名链接:请求时不要Authorization 头。它们在 expires_at 之前有效,也就是 finished_at 之后 3 天;在这个窗口内重复读取任务会拿到完全相同的链接,而不是重新签名的新链接。请在此之前自行下载并保存文件 —— 过期的链接无法刷新。

    • Name
      glb
      Type
      string
      Description

      最终带贴图 model.glb 的下载 URL。

    • Name
      obj_zip
      Type
      string
      Description

      zip 包的下载 URL,内含 model.objmodel.mtl,以及 MTL 实际引用的贴图 PNG。纯色底座仅含 keycap-head.png;带图案的底座还会包含 keycap-base.png

  • Name
    process_image_urls
    Type
    object
    Description

    过程图的下载 URL,按 kind 作为键。URL 生命周期与 model_urls 相同:签名链接、不需要 Authorization 头、有效至 expires_at,重复读取任务时保持不变。当前会出现的 kind:

    • head_design —— build 实际消费的选定候选设计图(始终存在)。
    • composite —— 选定候选的成品键帽展示渲染图(存在时返回)。
    • base_canvas —— 上色后的键帽底座画布(存在时返回)。

    请把键集合视为开放集合;后续可能在不破坏兼容的前提下新增 kind。

Build 任务对象示例

{
  "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,轮询 build 到 SUCCEEDED,最后从 model_urls 下载 GLB 与 OBJ 包。

示例中程序化地选择了第一个候选。实际接入时应把 image_urls 里的那一项 展示给最终用户挑选;选中的下标与 candidate_ids 一一对应。

Complete flow

POST
/openapi/creative-lab/keycap/v1
#!/usr/bin/env bash
set -euo pipefail

# 需要 curl 与 jq。把 IMAGE_PATH 指向本地照片,或把 IMAGE_URL 指向公网图片:
#   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 参数...] -> 打印响应体,失败时返回非 0。
# 注意这里不用 -f/--fail:它会把响应体丢掉,而原因只出现在响应体里。
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"
}

# 每个任务各有自己的 40 分钟预算。
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
}

# 把请求体写进文件。base64 data URI 绝对不能出现在命令行或导出的环境变量里 ——
# 任何真实尺寸的照片都会超过操作系统的参数长度上限。
BODY=$(mktemp)
trap 'rm -f "$BODY"' EXIT
if [[ -n "${IMAGE_PATH:-}" ]]; then
  # 声明真实类型:接口支持 JPEG、PNG、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. 创建 prototype 任务
PROTO_ID=$(api POST "$BASE/prototype" \
  -H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')

# 2. 等待设计效果图
poll prototype "$PROTO_ID"

# 3. 选一个候选(这里取第一个;生产环境应把 image_urls 展示给用户选)
CANDIDATE_ID=$(api GET "$BASE/prototype/$PROTO_ID" | jq -r '.candidate_ids[0]')

# 4. 创建 build 任务
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. 等待模型(build 通常需要 3-7 分钟)
poll build "$BUILD_ID"

# 6. 下载产物。这些是签名链接:不需要 Authorization 头,
#    任务完成后 3 天内有效。
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"