Creative Lab — 鍵帽 API

將一張原始照片分兩個階段轉換為全彩客製化機械鍵盤鍵帽:prototype(原型) 階段會根據你輸入的照片產生 一張「成品鍵帽」設計效果圖。在確認該效果圖之後,build(構建) 階段會在一次執行中將其轉換為 帶貼圖的 3D 鍵帽模型——白模生成、在校準的預設姿態上自動就位與切割、全模型上色, 以及最終組裝,均在同一個構建任務內完成。這兩個階段透過 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

建立鍵帽原型任務

根據來源照片產生成品鍵帽設計渲染圖。任務結果會攜帶一個 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

    身份驗證失敗。請檢查你的 API 金鑰。

  • Name
    402 - Payment Required
    Description

    該帳戶處於免費方案中(建立任務需要付費方案),或積分不足。

  • Name
    403 - Forbidden
    Description

    輸入圖片被智慧財產權 moderation 標記。

  • Name
    429 - Too Many Requests
    Description

    你已超出速率限制。

  • Name
    500 - Internal Server Error
    Description

    發生了非預期的伺服器端錯誤——例如內容審核服務無法使用、暫存輸入圖片失敗,或任務無法建立。在這種情況下不會建立任務,因此重試是安全的。

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

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

創建鍵帽構建任務

從一個成功的原型任務及其某個候選方案生成最終的帶貼圖 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-head mesh 的包圍盒。

  • Name
    vertical_offset_mm
    Type
    number
    預設值 0
    Description

    在頭部安置到底座之前施加的垂直偏移量,以毫米為單位。範圍:[-5, 5]

返回值

回應的 result 屬性包含新創建的鍵帽構建任務的任務 id。輪詢取得任務 endpoint 或訂閱串流,直到任務到達 SUCCEEDED,然後從 model_urls.glbmodel_urls.obj_zip 下載產物。

失敗模式

  • Name
    400 - Bad Request
    Description

    請求不可接受。常見原因:

    • 缺少參數input_task_idcandidate_id 是必需的。
    • 無效的 UUIDinput_task_id 不是有效的 UUID。
    • 父任務未成功:所引用的原型任務尚未到達 SUCCEEDED
    • 沒有候選方案:原型任務成功但未產生任何候選方案。
    • 未知的候選方案candidate_id 不是輸入任務的候選方案之一。
    • 參數超出範圍options 中的某個欄位超出了其允許的範圍或列舉集合。
  • Name
    401 - Unauthorized
    Description

    身份驗證失敗。請檢查你的 API 金鑰。

  • Name
    402 - Payment Required
    Description

    該帳戶處於免費方案(創建任務需要付費方案)或積分不足。

  • Name
    404 - Not Found
    Description

    所引用的原型任務不存在、屬於其他使用者,或是透過 webapp 創建的(只有 API 模式的原型任務才能連結到構建)。

  • Name
    429 - Too Many Requests
    Description

    你已超出速率限制。

  • Name
    500 - Internal Server Error
    Description

    發生了意外的伺服器端錯誤——例如內容 moderation 服務不可用、暫存輸入圖片失敗,或無法創建任務。這種情況下不會創建任何任務,因此重試是安全的。

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

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

取得 Keycap 任務

根據有效的任務 id 取得原型(prototype)或構建(build)任務。URL 路徑必須與任務所處的階段相符——透過 /prototype/:id 取得構建任務會回傳 404,反之亦然。

有關回應結構,請參閱 Keycap 原型任務物件Keycap 構建任務物件

參數

  • Name
    id
    Type
    path
    Description

    要取得的 keycap 任務的唯一識別碼。

回傳

回應包含 keycap 任務物件。其結構取決於所請求的具體階段。

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

刪除 Keycap 任務

取消一個 keycap 任務。如果任務仍處於 PENDING 狀態,則會退還建立時消耗的 積分。已經處於 IN_PROGRESS 狀態的任務會被取消但不予退款(worker 可能已經在 消耗資源)。已經達到終態(SUCCEEDEDFAILEDCANCELED)的任務無法取消。

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

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).

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

流式取得 Keycap 任務

透過 Server-Sent Events(SSE)流式取得 keycap 任務的即時更新。 URL 路徑必須與任務所處的階段相符 —— 在 /prototype/:buildId/stream 處開啟串流會發出一個帶有 status_code: 404event: error payload,隨後關閉該串流。

參數

  • Name
    id
    Type
    path
    Description

    要進行流式傳輸的 keycap 任務的唯一識別碼。

回傳

以 Server-Sent Events 的形式回傳一個由 Keycap PrototypeKeycap 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 example (wrong stage or task not found)
event: error
data: {
  "status_code": 404,
  "message": "Task not found"
}

// Message event examples illustrate task progress.
// For PENDING or IN_PROGRESS tasks, the response stream will not include all fields.
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)

List Keycap Tasks

取得單一階段中你的 keycap 任務的分頁列表。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 時回傳 keycap 原型任務物件, 列出 /build 時回傳 keycap 建置任務物件

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"
    ]
  }
]

鍵帽原型任務物件

鍵帽原型任務(Keycap Prototype Task)物件是 Meshy 用於追蹤的一個工作單元,用於根據來源照片生成一張成品鍵帽設計圖。該階段的輸出會透過 input_task_idcandidate_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

    任務的狀態。可能的取值為 PENDINGIN_PROGRESSSUCCEEDEDFAILEDCANCELED 之一。

  • Name
    progress
    Type
    integer
    Description

    任務的 progress。如果任務尚未開始,此屬性為 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 物件參考請參見 錯誤

  • Name
    consumed_credits
    Type
    integer
    Description

    此任務消耗的積分數量。達到 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 僅用於展示;建構端點使用的是 candidate_ids,而非這些 URL。其 URL 生命週期與 model_urls 相同:經過簽署、無需 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 用於追蹤的一個工作單元,用來根據成功的原型任務和選定的候選方案生成最終的帶 texture 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

    任務的狀態。可能的值為 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,包括 moderation 拒絕)完全不收費。達到 FAILED 狀態的任務回傳 0——費用會被退還,包括非同步 moderation 攔截的情況。透過 DELETE 取消任務僅在任務仍處於 PENDING 狀態時才會退款;已處於 IN_PROGRESS 狀態的任務仍會被收費,因為相關工作已經耗費。

  • Name
    model_urls
    Type
    object
    Description

    生成的模型檔案的可下載 URL。GLB 和 OBJ 壓縮檔均以真實世界的毫米級比例Y 軸朝上匯出,鍵帽正面朝向 +Z 方向。mesh 分別命名為 keycap-headkeycap-base;當底座回退為圖案填充時,還會存在第三個 mesh keycap-base-interior,用於表示卡柱空腔。請勿假定一定只有兩個 mesh。

    這些是簽名 URL:取得時無需攜帶 Authorization 請求標頭。它們在 expires_at(即 finished_at 之後 3 天)之前一直有效,在此期間重新讀取任務將回傳相同的 URL,而不是重新簽名的 URL。請在此之前自行下載並儲存這些檔案——過期的連結無法刷新。

    • Name
      glb
      Type
      string
      Description

      最終帶 texture 的 model.glb 的可下載 URL。

    • Name
      obj_zip
      Type
      string
      Description

      指向包含 model.objmodel.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=***"
  }
}

端到端範例

完整流程:從一張照片建立原型,輪詢直到狀態變為 SUCCEEDED,從 candidate_ids 中選擇一個候選項,使用該候選項建立建置任務, 輪詢建置任務直到狀態變為 SUCCEEDED,然後從 model_urls 下載 GLB 和 OBJ 壓縮檔。

此範例以程式化方式選擇了第一個候選項。在實際整合中,你應當將 image_urls 中的項目展示給最終使用者,讓他們進行選擇;所選的索引與 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"