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 均需要付費的訂閱方案。免費方案帳號發起的請求將被拒絕,並回傳
402 Payment Required。
建立鍵帽原型任務
根據來源照片產生成品鍵帽設計渲染圖。任務結果會攜帶一個 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
# 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 端點建立的原型任務的任務 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-headmesh 的包圍盒。
- Name
- vertical_offset_mm
- Type
- number
- 預設值 0
- Description
在頭部被放置到底座上之前對其施加的垂直偏移量,單位為毫米。範圍:
[-5, 5]。
回傳值
回應的 result 屬性包含新建立的鍵帽建構任務的任務 id。輪詢 取得任務 端點或訂閱 串流,直到任務達到 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
身份驗證失敗。請檢查你的 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 狀態,建立時消耗的
積分將被退還。已經處於 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 負載並關閉該串流。
參數
- 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
任務的 progress。如果任務尚未開始,此屬性為
0。任務成功後,此值將變為100。
- Name
- created_at
- Type
- timestamp
- Description
任務建立時的 時間戳,以毫秒為單位。
時間戳表示自 1970 年 1 月 1 日 UTC 起經過的毫秒數,遵循 RFC 3339
標準。 例如,2023 年 9 月 1 日星期五 GMT 時間中午 12:00:00 表示為1693569600000。這適用於 Meshy API 中的所有時間戳。
- 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
排在前面的任務數量。
此欄位的值僅在任務狀態為
PENDING時才有意義。
- 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
任務的狀態。可能的值為
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
此任務消耗的積分數量。達到
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 實際引用的紋理 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"