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 陣列;兩者都只包含一個項目。如果結果不是你想要的,可以再次呼叫此端點以取得另一張渲染圖——每次呼叫都會單獨計費。將 candidate_id 連同原型任務 ID 一起傳遞給建置端點。 有關回應格式,請參閱 鍵帽原型任務物件

參數

  • 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,以便你可以將其合成到任意背景上。

    這僅適用於展示渲染圖。建置端點所使用的候選項不受影響,因此無論哪種方式,3D 結果都是相同的。

回傳值

回應的 result 屬性包含新建立的鍵帽原型任務的任務 id。輪詢取得任務端點,或訂閱串流介面,直到任務達到 SUCCEEDED 狀態,然後取出 candidate_ids 中的項目,連同任務 ID 一起傳遞給建置端點

失敗模式

  • 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 金鑰。

  • 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 端點建立的原型任務的任務 ID。該原型必須由同一個 Meshy 帳號建立,必須已達到 SUCCEEDED 狀態,並且必須至少生成了一個候選項。

    透過 webapp 建立的原型任務不被接受——建構端點只接受由 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。輪詢 取得任務 端點或訂閱 串流,直到任務達到 SUCCEEDED,然後從 model_urls.glbmodel_urls.obj_zip 下載成品。

失敗模式

  • Name
    400 - Bad Request
    Description

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

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

    身份驗證失敗。請檢查你的 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

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 負載並關閉該串流。

參數

  • Name
    id
    Type
    path
    Description

    要以串流方式取得的 keycap 任務的唯一識別碼。

回應

以 Server-Sent Events 的形式回傳一系列 Keycap PrototypeKeycap Build 任務物件。每一個訊框都攜帶該階段完整的任務物件——與 Get endpoint 回傳的結構相同——因此當任務處於 PENDINGIN_PROGRESS 狀態時, 輸出欄位只是尚未填入資料(為 null[]{}),且 finished_atnull

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

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

列出 Keycap 任務

檢索單個階段的 keycap 任務的分頁列表。URL 路徑用於選擇階段——/prototype 返回原型(prototype)任務; /build 返回構建(build)任務。另一個階段的任務不會包含在任一回應中。

路徑參數

  • Name
    stage
    Type
    path
    必選
    Description

    prototypebuild 之一。該集合只返回階段與 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

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

鍵帽原型任務物件

鍵帽原型任務物件是 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。與 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

    任務的狀態。可能的值為 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。網格命名為 keycap-headkeycap-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.objmodel.mtl 以及其 MTL 實際引用的紋理 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

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"