lowpoly 将于 2026年10月30日 下线。 请在这些日期前切换模型,以免请求报错。 查看所有弃用信息

Creative Lab 键帽 API

通过两个阶段,将一张照片变成机械键盘的全彩定制键帽。原型任务根据照片生成成品键帽的图像。构建任务将该图像转换为带纹理的 3D 键帽模型。它将原型任务的 ID 作为 input_task_id,并将图像的候选 ID 作为 candidate_id。

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

两个 POST 端点都需要付费计划。免费计划账户发出的请求将返回 402。


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

创建 Keycap prototype 任务

根据一张照片生成一张成品键帽的图像。任务的 image_urls 中保存该图像,candidate_ids 中保存其 ID,二者各有一条记录。如果你不喜欢这个设计,可以再创建一个 prototype 任务。Meshy 会对每个 prototype 任务单独收费。

要构建 3D 键帽,请将此任务的 ID 作为 input_task_id 传给 build endpoint,并将 candidate_ids 中的那条记录作为 candidate_id 传入。有关该任务各字段的说明,请参阅Keycap prototype 任务对象。

Request

POST
/openapi/creative-lab/keycap/v1/prototype
# 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"
}

参数

  • Name
    image_url
    Type
    string
    必选
    Description

    要转换为键帽设计的照片,可以是公开可访问的 URL,或是一个 data URI(data:image/jpeg;base64,<your base64-encoded image data>)。

    图片必须是 JPG、JPEG、PNG 或 WebP 文件,且不超过 20 MB(20,000,000 字节)。每条边至少为 32 像素,图片总像素数最多为 178,956,970。超出这些限制的图片会返回 400。

    Meshy 会根据图像数据来检测格式,而不是依据 URL 中的文件扩展名。没有扩展名的 URL 也可以正常工作,并且 Meshy 会跟随 HTTP 重定向。Meshy 会应用 EXIF 方向信息,因此旋转过的手机照片会按照其实际显示的方向呈现。

    data URI 必须使用 base64 编码,并带有图像类型,例如 data:image/png;base64,...,且解码后的图像必须是 JPEG、PNG 或 WebP 格式。20 MB 的限制适用于解码后的图像,因此 base64 文本本身可能会大约再大三分之一。

  • Name
    name
    Type
    string
    Description

    任务的名称,最多 100 个字符。该名称会在任务的 name 字段中返回。超出长度限制会返回 400。

  • Name
    remove_background
    Type
    boolean
    默认值 false
    Description

    是否从 prototype 图像中移除背景。设为 true 会返回一张透明的 RGBA PNG,这样你就可以将键帽放置在任意背景之上。如果 Meshy 无法移除背景,它会返回带有原背景的图像,任务仍然会成功。

    无论如何,build 阶段用于生成 3D 效果的头部设计都会保留其背景。build 阶段会根据 image_urls 中的图像来绘制键帽底座,如果你发送了 true,则不带其背景。build 阶段会将该图像以 process_image_urls.composite 的形式返回。

返回值

result 字段保存新创建的 prototype 任务的 ID。获取该任务或对其进行流式获取,直到 status 变为 SUCCEEDED。然后将该 ID 作为 input_task_id 传给 build endpoint,并将 candidate_ids 中的那条记录作为 candidate_id 传入。

失败情形

  • Name
    400 - Bad Request
    Description

    请求无法执行。例如:

    • 缺少 image_url。
    • 图像不是 JPG、JPEG、PNG 或 WebP 文件。
    • 图像过小、超过 20 MB,或超过最大像素数限制。
    • Meshy 无法下载该图像,例如因为该 URL 返回 404 或超时。
    • data URI 不是有效的 base64 编码。
    • 该图像被标记为 NSFW(不适宜内容)。

    错误信息会说明具体发现的问题。修复请求后再重新发送。

  • Name
    401 - Unauthorized
    Description

    API key 缺失或无效。请检查 Authorization 请求头。

  • Name
    402 - Payment Required
    Description

    以下情况之一适用:

    • 账户的积分不足。
    • 该 API key、workspace 或成员已达到其积分上限。
    • 该账户处于免费套餐。

    请检查你的积分和套餐,然后重试。

  • Name
    403 - Forbidden
    Description

    你的团队已开启IP 过滤功能,且 Meshy 将该图像标记为涉及第三方知识产权。请发送你拥有使用权利的图像。

  • Name
    429 - Too Many Requests
    Description

    已达到以下限制之一:

    • 你发送的请求数超过了速率限制所允许的数量。请等待几秒后重试。请参阅速率限制。
    • 排队或正在运行的任务过多。请等待部分任务完成后再重试。
    • 在没有剩余积分的无限制套餐下,你已达到可在无积分情况下运行的任务数量上限。请等待某个任务完成,或增加积分。
  • Name
    500 - Internal Server Error
    Description

    Meshy 遇到了服务器错误,例如由于内容审核(moderation)或知识产权检查未能运行,或 Meshy 无法存储该图像。Meshy 不会创建该任务,并会退还其已扣除的任何积分。请重试该请求。


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

创建 Keycap build 任务

根据一个成功的 prototype 任务及其候选项之一,生成带贴图的 3D keycap。相关字段请参阅 Keycap build 任务对象。

一个 build 任务会执行每一个步骤。它先根据设计生成一个 3D 模型并为其上色,然后以经过校准的默认姿态将模型嵌入并裁切到 keycap 底座上,因此你无需手动调整。最后,它会组装 keycap 并导出文件。一次 build 通常需要 3 到 7 分钟。

Request

POST
/openapi/creative-lab/keycap/v1/build
# 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"
}

参数

  • Name
    input_task_id
    Type
    string
    必选
    Description

    要构建的 prototype 任务的 ID。它必须是你的账户通过 POST /openapi/creative-lab/keycap/v1/prototype 创建的任务,且状态为 SUCCEEDED。

    如果 ID 不是你通过 API 创建的 Keycap prototype 任务之一,将返回 404,这也包括在 web app 中创建的 prototype。

  • Name
    candidate_id
    Type
    string
    必选
    Description

    要构建的候选项,来自 prototype 任务的 candidate_ids 数组。如果值不在该数组中,将返回 400。

  • Name
    name
    Type
    string
    Description

    任务的名称,最多 100 个字符。它会在任务的 name 字段中返回。名称过长将返回 400。

options

可选的 geometry 设置。每个字段都有经过校准的默认值,因此只需发送你想更改的字段。

  • Name
    base_model
    Type
    string
    默认值 cherry-mx-1x1-r1
    Description

    用于构建的 keycap 底座。目前唯一可用的值是 cherry-mx-1x1-r1,即标准的 Cherry MX profile 1u keycap。不支持自定义尺寸,其他任何值都会返回 400。

  • Name
    head_size_mm
    Type
    number
    默认值 23
    Description

    头部沿其最长维度的尺寸,单位为毫米,范围为 10 到 40。Meshy 会缩放头部,使其最长维度与该值匹配。超出范围的值将返回 400。

    当值大于约 32.9 时,Meshy 可能会缩小尺寸,以使头部符合底座的保护性占用面积限制。此时头部生成的结果可能会比你要求的更小。任务对象不会返回 Meshy 实际应用的尺寸。要检查该尺寸,请测量模型中 keycap-head mesh 的包围盒。

  • Name
    vertical_offset_mm
    Type
    number
    默认值 0
    Description

    在 Meshy 将头部嵌入底座之前对其施加的垂直偏移量,单位为毫米,范围为 -5 到 5。超出范围的值将返回 400。

返回值

result 字段保存新 build 任务的 ID。获取任务或对其进行流式传输,直到 status 为 SUCCEEDED。然后从 model_urls.glb 下载 GLB 文件,并从 model_urls.obj_zip 下载 OBJ 压缩包。

失败模式

  • Name
    400 - Bad Request
    Description

    请求无法运行。例如:

    • input_task_id 缺失或不是有效的任务 ID。
    • prototype 任务的状态不是 SUCCEEDED。如果仍在运行,请等待后重试。如果已失败或被取消,请创建一个新的 prototype 任务。
    • prototype 任务成功了,但没有候选项。请创建一个新的 prototype 任务。
    • candidate_id 缺失,或不在 prototype 任务的 candidate_ids 中。请从该数组中发送一个值。
    • options 中的某个字段超出范围,或 base_model 不是可用的值。

    错误消息会说明发现的具体问题。

  • Name
    401 - Unauthorized
    Description

    API key 缺失或无效。请检查 Authorization 请求头。

  • Name
    402 - Payment Required
    Description

    以下情况之一适用:

    • 账户没有足够的积分。
    • 该 API key、workspace 或成员已达到其积分限额。
    • 账户处于免费计划。

    请检查你的积分和计划,然后重试。

  • Name
    404 - Not Found
    Description

    input_task_id 与你的账户通过 API 创建的任何 Keycap prototype 任务都不匹配。在 web app 中创建的 prototype 同样会返回 404。请发送来自 prototype 创建响应中的 result ID。

  • Name
    429 - Too Many Requests
    Description

    达到以下限制之一:

    • 你发送的请求数超过了速率限制所允许的数量。请等待几秒后重试。参见速率限制。
    • 你排队或正在运行的任务过多。请等待部分任务完成后重试。
    • 在没有积分的无限制计划下,你达到了在没有积分时可运行的任务数量上限。请等待某个任务完成,或添加积分。
  • Name
    500 - Internal Server Error
    Description

    由于服务器错误,Meshy 无法创建任务。Meshy 会退还其已扣除的所有积分。请重试该请求。


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

获取 Keycap 任务

返回一个 prototype 或 build 任务。路径必须与任务所处的阶段匹配。通过 /prototype/:id 请求 build 任务会返回 404,通过 /build/:id 请求 prototype 任务同样会返回 404。

字段说明请参阅 Keycap prototype 任务对象 和 Keycap build 任务对象。

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=***"
  }
}

参数

  • Name
    id
    Type
    path
    Description

    创建响应中返回的任务 ID。

返回值

prototype 任务对象 或 build 任务对象,具体取决于路径中指定的阶段。

失败情况

  • Name
    400 - Bad Request
    Description

    id 不是一个有效的任务 ID。请发送创建响应中返回的 result ID。

  • Name
    403 - Forbidden
    Description

    你的团队已开启 IP Filtering,并且 Meshy 检测到该 prototype 任务的图片涉及第三方知识产权问题。Build 任务以及 Enterprise 套餐团队不会遇到此错误。请使用你拥有使用权的图片来创建 prototype 任务。

  • Name
    404 - Not Found
    Description

    你的账户下没有该 ID 对应的 Keycap 任务,或者该任务所处的阶段与路径不匹配。请检查 ID,并使用 /prototype 或 /build 来匹配对应的任务。

  • Name
    500 - Internal Server Error
    Description

    Meshy 无法执行知识产权检查。请重试该请求。


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

删除 Keycap 任务

取消一个 Keycap 任务。取消 PENDING 任务会退还其积分。IN_PROGRESS 任务被取消时不会退款,因为工作进程已经开始处理它。已处于最终状态(SUCCEEDED、FAILED 或 CANCELED)的任务无法取消。

路径必须与任务所处的阶段匹配。例如,用 build 任务的 ID 对 /prototype/:id 执行 DELETE 会返回 404。

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

// Returns 204 No Content on success (empty body).

路径参数

  • Name
    id
    Type
    path
    Description

    要取消的任务的 ID。

返回值

204 No Content,响应体为空。

失败情况

  • Name
    400 - Bad Request
    Description

    以下情况之一适用:

    • id 不是有效的任务 ID。请发送创建响应中的 result ID。
    • 该任务已处于最终状态(SUCCEEDED、FAILED 或 CANCELED),因此无法取消。
  • Name
    404 - Not Found
    Description

    你的账户下不存在该 ID 对应的 Keycap 任务,或任务所处的阶段与路径不匹配。请检查 ID,并使用 /prototype 或 /build 来匹配该任务。

  • Name
    500 - Internal Server Error
    Description

    Meshy 在取消任务时遇到了服务器错误,因此该任务可能已取消,也可能未取消。请先获取任务以检查其 status,然后再重试。


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

流式获取 Keycap 任务

以服务器发送事件(SSE)的形式,流式获取 Keycap 任务的更新。路径必须与任务所处的阶段匹配。如果不匹配,例如在 /prototype/:id/stream 中使用 build 任务的 ID,流会发送一个 status_code: 404 的 error 事件,然后关闭连接。

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 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 following PENDING frame is shortened 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=***"
  }
}

参数

  • Name
    id
    Type
    path
    Description

    创建响应中返回的任务 ID。

返回值

一个由原型或构建任务对象组成的流。每个事件都包含完整的任务对象,格式与获取 Keycap 任务返回的一致。当任务处于 PENDING 或 IN_PROGRESS 状态时,其输出字段仍为空(null、[] 或 {}),且 finished_at 为 null。

如果 IP 过滤 功能拦截了任务的结果,流会在该处发送一个 error 事件,其中的 status_code 与 get 请求所返回的一致。


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

获取 Keycap 任务列表

返回某一阶段的 Keycap 任务列表,每次返回一页。路径决定阶段:/prototype 仅返回 prototype 任务,/build 仅返回 build 任务。

Request

GET
/openapi/creative-lab/keycap/v1/prototype
# 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"
    ]
  }
]

路径参数

  • Name
    stage
    Type
    path
    必选
    Description

    要列出的阶段,prototype 或 build。

查询参数

  • Name
    page_num
    Type
    integer
    默认值 1
    Description

    要返回的页码,范围从 1 到 255。0 返回第一页。超出 0 到 255 的值将返回 400。

  • Name
    page_size
    Type
    integer
    默认值 10
    Description

    每页返回的任务数量,最多 100。101 到 255 之间的值将返回 100 个任务,0 使用默认值。超出 0 到 255 的值将返回 400。

  • Name
    sort_by
    Type
    string
    默认值 -created_at
    Description

    排序方式。

    可用值:

    • -created_at:最新的排在最前
    • +created_at:最旧的排在最前

返回值

一个数组,包含与路径中阶段相匹配的prototype 任务对象或build 任务对象。


Keycap prototype 任务对象

一个 prototype 任务会根据照片生成一张成品键帽的图像。要将该设计转为 3D 键帽,请将该任务的 ID 作为 input_task_id 传给 build endpoint,并将其 candidate_ids 中的对应条目作为 candidate_id 传入。

Properties

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"
  ]
}
  • Name
    id
    Type
    string
    Description

    任务 ID。请勿依赖其具体格式。

  • Name
    type
    Type
    string
    Description

    任务类型,恒为 creative-lab-keycap-prototype。

  • Name
    name
    Type
    string
    Description

    你在创建任务时发送的 name;如果没有发送,则为 ""。

  • Name
    status
    Type
    string
    Description

    任务的状态。

    可用取值:

    • PENDING:在队列中等待
    • IN_PROGRESS:正在生成结果
    • SUCCEEDED:最终状态,已生成结果
    • FAILED:最终状态,失败原因见 task_error
    • CANCELED:最终状态,没有结果
  • Name
    progress
    Type
    integer
    Description

    已完成工作的百分比,范围从 0 到 100。任务开始前为 0,成功后为 100。

  • Name
    created_at
    Type
    timestamp
    Description

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

  • Name
    started_at
    Type
    timestamp
    Description

    某个 worker 开始处理该任务的时间,单位为 Unix 毫秒。在此之前为 null。

  • Name
    finished_at
    Type
    timestamp
    Description

    任务到达最终状态的时间,单位为 Unix 毫秒。在此之前为 null。

  • Name
    expires_at
    Type
    timestamp
    Description

    任务结果过期的时间,单位为 Unix 毫秒。该时间为 finished_at 之后 3 天;在任务完成之前,为 created_at 之后 3 天。对于可无限期保留 API 结果的 Enterprise 账户,该时间约为 100 年之后。另见资产保留期。

  • Name
    preceding_tasks
    Type
    integer
    Description

    任务处于 PENDING 状态时在队列中的位置。1 表示该任务将是下一个开始的任务。任务离开队列后为 0,在列表结果中也始终为 0。

  • Name
    task_error
    Type
    object
    Description

    任务失败的原因。除非 status 为 FAILED,否则为 null。另见 Errors。

  • Name
    consumed_credits
    Type
    integer
    Description

    该任务在扣除任何退款后实际收取的积分数量。返回错误的请求不会被扣费。失败的任务为 0,因为 Meshy 会对其进行退款。取消任务仅在其处于 PENDING 状态时才会退款。如果 Meshy 无法读取扣费信息,get 或 stream 响应会省略该字段,而 list 请求会返回 500。在这种情况下,请重试该请求。

  • Name
    image_urls
    Type
    array of strings
    Description

    成品键帽图像的下载 URL。任务成功后,该数组包含一个 URL;在此之前为空数组。image_urls[i] 展示的是 candidate_ids[i] 所对应的候选结果。

    这些 URL 仅用于展示。build endpoint 接收的是 candidate_ids 中的某个条目,而不是 URL。这些是签名 URL,获取时无需携带 Authorization 请求头。它们在 expires_at 之前始终有效,并且再次获取该任务会返回相同的 URL。

  • Name
    candidate_ids
    Type
    array of strings
    Description

    候选结果的 ID,顺序与 image_urls 相同。要构建某个候选结果,请在 build 请求中将其 ID 作为 candidate_id 发送。请勿依赖这些 ID 的具体格式。


键帽构建任务对象

构建任务会根据一个成功的原型任务及其候选项之一,生成带 texture 的 3D 键帽。它会生成 3D 模型、为其上色、将其嵌入并裁切到键帽底座上,然后组装并导出键帽。

属性

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=***"
  }
}
  • Name
    id
    Type
    string
    Description

    任务 ID。请勿依赖其格式。

  • Name
    type
    Type
    string
    Description

    任务类型,始终为 creative-lab-keycap-build。

  • Name
    name
    Type
    string
    Description

    你创建任务时发送的 name,如果未发送则为 ""。

  • Name
    status
    Type
    string
    Description

    任务的状态。

    可用值:

    • PENDING:在队列中等待
    • IN_PROGRESS:正在生成结果
    • SUCCEEDED:最终状态,附带结果
    • FAILED:最终状态,原因见 task_error
    • CANCELED:最终状态,无结果
  • Name
    progress
    Type
    integer
    Description

    已完成工作的百分比,从 0 到 100。任务开始前为 0,成功后为 100。

  • Name
    created_at
    Type
    timestamp
    Description

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

  • Name
    started_at
    Type
    timestamp
    Description

    工作进程开始处理任务的时间,单位为 Unix 毫秒。在此之前为 null。

  • Name
    finished_at
    Type
    timestamp
    Description

    任务达到最终状态的时间,单位为 Unix 毫秒。在此之前为 null。

  • Name
    expires_at
    Type
    timestamp
    Description

    任务结果过期的时间,单位为 Unix 毫秒。该时间为 finished_at 之后 3 天,或在任务完成前为 created_at 之后 3 天。对于会无限期保留 API 结果的企业账户,该时间约为 100 年后。参见资产保留期。

  • Name
    preceding_tasks
    Type
    integer
    Description

    任务处于 PENDING 状态时在队列中的位置。1 表示该任务将是下一个开始执行的任务。任务离开队列后为 0,在列表结果中始终为 0。

  • Name
    task_error
    Type
    object
    Description

    任务失败的原因。除非 status 为 FAILED,否则为 null。参见Errors。

  • Name
    consumed_credits
    Type
    integer
    Description

    该任务在退款后实际扣除的积分数量。返回错误的请求不会被扣费。失败的任务为 0,因为 Meshy 会退还费用。仅当任务处于 PENDING 状态时取消才会退款。如果 Meshy 无法读取扣费信息,get 或 stream 响应会省略该字段,而 list 请求会返回 500。遇到这种情况,请重试该请求。

  • Name
    model_urls
    Type
    object
    Description

    3D 模型的下载 URL。Meshy 导出的 GLB 文件和 OBJ 包按真实世界比例(以毫米为单位)、Y 轴朝上,键帽正面朝向 +Z 方向。

    该模型包含名为 keycap-head 和 keycap-base 的 mesh。当 Meshy 回退为底座使用图案填充时,模型还会包含一个用于轴芯凹腔的 keycap-base-interior mesh。请勿假设模型一定只有两个 mesh。

    这些是签名 URL,因此获取时无需携带 Authorization 请求头。它们在 expires_at 之前保持有效,在此之前再次获取该任务会返回相同的 URL。过期的 URL 无法续期,因此请在到期前下载并保存文件。

    • Name
      glb
      Type
      string
      Description

      带 texture 的 GLB 文件 model.glb 的下载 URL。

    • Name
      obj_zip
      Type
      string
      Description

      OBJ 包的下载 URL,这是一个包含 model.obj、model.mtl 以及 model.mtl 所使用的 PNG texture 的 ZIP 文件。如果底座为纯色,唯一的 texture 是 keycap-head.png。如果底座带有图案,该 ZIP 文件还会包含 keycap-base.png。

  • Name
    process_image_urls
    Type
    object
    Description

    构建过程各步骤生成图片的下载 URL,按类型进行键值索引。与 model_urls 一样,它们也是签名 URL,因此获取时无需携带 Authorization 请求头。它们在 expires_at 之前保持有效,再次获取该任务会返回相同的 URL。

    它可能包含以下键:

    • head_design:所选候选项的设计图像,构建任务据此生成 3D 内容。始终存在。
    • composite:所选候选项完成后的键帽图像,底座正是根据它绘制而成。可能缺失。
    • base_canvas:绘制完成的键帽底座。可能缺失。

    Meshy 可能会新增键,因此遇到不认识的键请直接忽略。


端到端示例

该示例运行整个流程:

  1. 根据照片创建一个 prototype 任务。
  2. 获取任务,直到其 status 为 SUCCEEDED。
  3. 从 candidate_ids 中选取一个候选项。
  4. 使用该候选项创建一个 build 任务。
  5. 获取 build 任务,直到其 status 为 SUCCEEDED。
  6. 从 model_urls 下载 GLB 文件和 OBJ 压缩包。

该示例选择了第一个候选项。在您的应用中,可以向用户展示 image_urls 条目,让他们自行选择。所选图片在 image_urls 中的索引,就是其 ID 在 candidate_ids 中的索引。

Complete flow

POST
/openapi/creative-lab/keycap/v1
#!/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"