Ubah foto sumber menjadi keycap keyboard mekanik kustom berwarna penuh dalam
dua tahap: prototipe menghasilkan render desain "keycap selesai"
dari foto input Anda. Setelah Anda mengonfirmasi render tersebut, pembangunan mengubahnya menjadi
model keycap 3D bertekstur dalam satu kali proses — pembuatan model putih, penempatan dan pemotongan otomatis pada pose default yang terkalibrasi, pewarnaan model penuh, dan
perakitan akhir semuanya terjadi dalam satu tugas pembangunan. Kedua tahap tersebut terhubung
melalui input_task_id ditambah candidate_id.
POST /openapi/creative-lab/keycap/v1/prototype
POST /openapi/creative-lab/keycap/v1/build
Kedua endpoint POST memerlukan rencana langganan berbayar. Permintaan dari
akun rencana gratis ditolak dengan 402 Payment Required.
Hasilkan render desain keycap yang sudah jadi dari foto sumber. Hasil tugas
membawa array image_urls (render tampilan dari keycap yang sudah jadi) dan array candidate_ids paralel; keduanya memegang satu entri. Panggil endpoint ini lagi untuk render lain jika hasilnya tidak sesuai keinginan Anda — setiap panggilan dikenakan biaya terpisah. Berikan candidate_id bersama dengan ID tugas prototipe ke endpoint build.
Lihat
Objek Tugas Prototipe Keycap
untuk bentuk respons.
Parameter
Name
image_url
Type
string
Wajib
Description
Foto sumber untuk Meshy untuk diubah menjadi gambar desain keycap. Kami saat ini mendukung format .jpg, .jpeg, .png, dan .webp.
Format dideteksi dengan mendekode data gambar, bukan dari ekstensi file URL — URL tanpa ekstensi, atau yang mengarahkan ulang, berfungsi selama byte dapat didekode ke format yang didukung. Pengalihan HTTP diikuti. Orientasi EXIF dinormalisasi, sehingga foto ponsel yang diputar digunakan sesuai tampilannya.
Batasan: setidaknya 32 piksel di setiap sisi, paling banyak 178,956,970 piksel secara total, dan paling banyak 20,000,000 byte setelah diunduh. Untuk Data URI, batasan berlaku untuk byte yang didekode, sehingga file sumber itu sendiri dapat mencapai ukuran tersebut — teks base64 yang sekitar sepertiga lebih besar, yang penting untuk body permintaan Anda, bukan untuk batasan ini. Data URI harus menyatakan tipe konten image/* dan ;base64.
Ada dua cara untuk menyediakan gambar:
URL yang dapat diakses publik: URL yang dapat diakses dari internet publik.
Data URI: Data URI yang dikodekan base64 dari gambar. Contoh Data URI: data:image/jpeg;base64,<your base64-encoded image data>.
Name
name
Type
string
Description
Nama tugas opsional untuk tujuan tampilan. Maksimal 100 karakter.
Name
remove_background
Type
boolean
default false
Description
Ketika diatur ke true, render tampilan yang dikembalikan dalam image_urls adalah PNG RGBA transparan dengan latar belakang dihapus, sehingga Anda dapat menggabungkannya ke latar belakang mana pun.
Ini berlaku hanya untuk render tampilan. Kandidat yang dikonsumsi oleh endpoint build tidak terpengaruh, sehingga hasil 3D identik dalam kedua cara.
Mengembalikan
Properti result dari respons berisi id tugas dari tugas prototipe keycap yang baru dibuat. Pantau endpoint Dapatkan Tugas atau berlangganan stream hingga tugas mencapai SUCCEEDED, lalu ambil entri dari candidate_ids dan berikan, bersama dengan ID tugas, ke endpoint build.
Mode Kegagalan
Name
400 - Bad Request
Description
Permintaan tidak dapat diterima. Penyebab umum:
Parameter hilang: image_url diperlukan.
Format gambar tidak valid: image_url yang diberikan bukan format yang didukung (.jpg, .jpeg, .png, .webp).
Dimensi gambar di luar jangkauan: Gambar terlalu kecil, melebihi ukuran file maksimum, atau melebihi jumlah piksel maksimum.
URL tidak dapat dijangkau: image_url tidak dapat diunduh (404 atau timeout).
Data URI tidak valid: String base64 rusak.
Konten ditandai: Gambar input ditandai oleh moderation NSFW.
Name
401 - Unauthorized
Description
Autentikasi gagal. Silakan periksa kunci API Anda.
Name
402 - Payment Required
Description
Akun berada pada paket gratis (paket berbayar diperlukan untuk membuat tugas) atau tidak memiliki kredit yang cukup.
Name
403 - Forbidden
Description
Gambar input ditandai oleh moderation hak kekayaan intelektual.
Name
429 - Too Many Requests
Description
Anda telah melebihi batas laju Anda.
Name
500 - Internal Server Error
Description
Terjadi kesalahan server yang tidak terduga — misalnya layanan moderation konten tidak tersedia, staging gambar input gagal, atau tugas tidak dapat dibuat. Tidak ada tugas yang dibuat dalam kasus ini, jadi mencoba lagi aman.
Permintaan
POST
/openapi/creative-lab/keycap/v1/prototype
# Stage 1: generate a finished-keycap design rendercurlhttps://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype \-XPOST \-H"Authorization: Bearer ${YOUR_API_KEY}" \-H'Content-Type: application/json' \-d'{ "image_url": "<your publicly accessible image url or base64-encoded data URI>" }'
Menghasilkan model keycap 3D bertekstur akhir dari tugas prototipe yang berhasil dan salah satu kandidatnya. Satu tugas pembuatan menjalankan seluruh pipeline dari awal hingga akhir — pembuatan model putih dari desain yang dipilih, pemasangan dan pemotongan otomatis pada dasar keycap menggunakan pose default yang dikalibrasi (tidak perlu penyesuaian interaktif), pewarnaan model penuh, dan perakitan serta ekspor akhir. Pembuatan biasanya memakan waktu 3–7 menit, menuju akhir yang lebih tinggi ketika beberapa pembuatan berjalan bersamaan.
Lihat Objek Tugas Pembuatan Keycap untuk bentuk respons.
Parameter
Name
input_task_id
Type
string
Wajib
Description
ID tugas dari tugas prototipe yang dibuat melalui endpoint OpenAPI ini. Prototipe harus dibuat oleh akun Meshy yang sama, harus mencapai SUCCEEDED, dan harus menghasilkan setidaknya satu kandidat.
Tugas prototipe yang dibuat melalui webapp tidak diterima — endpoint pembuatan hanya menerima tugas prototipe yang dihasilkan oleh POST /openapi/creative-lab/keycap/v1/prototype dan menolak sumber lain dengan 404.
Name
candidate_id
Type
string
Wajib
Description
Kandidat untuk dibangun, diambil dari array candidate_ids dari tugas prototipe yang berhasil. Harus milik tugas tersebut; nilai lain ditolak dengan 400.
Name
name
Type
string
Description
Nama tugas opsional untuk tujuan tampilan. Maksimum 100 karakter.
options
Penyesuaian geometri opsional. Setiap bidang memiliki default yang dikalibrasi — kirim hanya yang ingin Anda ganti.
Name
base_model
Type
string
default cherry-mx-1x1-r1
Description
Dasar keycap untuk dibangun. Saat ini satu-satunya nilai yang tersedia adalah cherry-mx-1x1-r1 — profil Cherry MX standar 1u. 3–5 ukuran standar mainstream tambahan direncanakan; ukuran kustom tidak didukung.
Name
head_size_mm
Type
number
default 23
Description
Ukuran target dari kepala yang dipahat, dalam milimeter: dimensi terpanjangnya diskalakan ke nilai ini. Rentang: [10, 40]. Nilai di atas sekitar 32.9 dapat dikurangi sehingga kepala masih sesuai dengan batas jejak pelindung dasar, sehingga dimensi terpanjang yang dikirimkan bisa lebih kecil dari yang diminta. Nilai yang diterapkan tidak dikembalikan pada objek tugas hari ini — jika Anda perlu mengonfirmasi ukuran yang sebenarnya Anda terima, ukur kotak pembatas dari mesh keycap-head dalam model yang diunduh.
Name
vertical_offset_mm
Type
number
default 0
Description
Offset vertikal yang diterapkan pada kepala sebelum dipasang pada dasar, dalam milimeter. Rentang: [-5, 5].
Mengembalikan
Properti result dari respons berisi id tugas dari tugas pembuatan keycap yang baru dibuat. Polling endpoint Dapatkan Tugas atau berlangganan stream hingga tugas mencapai SUCCEEDED, kemudian unduh artefak dari model_urls.glb dan model_urls.obj_zip.
Baik bundel GLB maupun OBJ diekspor pada skala milimeter dunia nyata, dengan sistem koordinat Y-up dan bagian depan keycap menghadap +Z.
Mode Kegagalan
Name
400 - Bad Request
Description
Permintaan tidak dapat diterima. Penyebab umum:
Parameter hilang: input_task_id dan candidate_id diperlukan.
UUID tidak valid: input_task_id bukan UUID yang valid.
Induk tidak berhasil: Tugas prototipe yang dirujuk belum mencapai SUCCEEDED.
Tidak ada kandidat: Tugas prototipe berhasil tetapi tidak menghasilkan kandidat.
Kandidat tidak dikenal: candidate_id bukan salah satu kandidat tugas input.
Opsi di luar jangkauan: Salah satu bidang options berada di luar rentang yang diizinkan atau set enum.
Name
401 - Unauthorized
Description
Autentikasi gagal. Silakan periksa kunci API Anda.
Name
402 - Payment Required
Description
Akun berada pada paket gratis (paket berbayar diperlukan untuk membuat tugas) atau tidak memiliki kredit yang cukup.
Name
404 - Not Found
Description
Tugas prototipe yang dirujuk tidak ada, milik pengguna lain, atau dibuat melalui webapp (hanya tugas prototipe mode API yang dapat dirantai ke pembuatan).
Name
429 - Too Many Requests
Description
Anda telah melebihi batas laju Anda.
Name
500 - Internal Server Error
Description
Terjadi kesalahan tak terduga di sisi server — misalnya layanan moderasi konten tidak tersedia, staging gambar input gagal, atau tugas tidak dapat dibuat. Tidak ada tugas yang dibuat dalam kasus ini, jadi mencoba lagi aman.
Permintaan
POST
/openapi/creative-lab/keycap/v1/build
# Stage 2: build the chosen candidate into a 3D keycapcurlhttps://api.meshy.ai/openapi/creative-lab/keycap/v1/build \-XPOST \-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 } }'
Ambil tugas prototipe atau build yang diberikan dengan id tugas yang valid. Jalur URL
harus sesuai dengan tahap tugas — tugas build yang diambil melalui
/prototype/:id akan mengembalikan 404, dan sebaliknya.
Batalkan tugas keycap. Jika tugas masih PENDING, kredit yang digunakan saat pembuatan akan dikembalikan. Tugas yang sudah IN_PROGRESS dibatalkan tanpa pengembalian dana (pekerja mungkin sudah menggunakan sumber daya). Tugas yang sudah mencapai status akhir (SUCCEEDED, FAILED, CANCELED) tidak dapat dibatalkan.
Jalur URL harus sesuai dengan tahap tugas — DELETE pada
/prototype/:buildId mengembalikan 404.
Parameter Jalur
Name
id
Type
path
Description
Pengenal unik untuk tugas keycap yang akan dibatalkan.
Pengembalian
Mengembalikan 204 No Content jika berhasil dengan tubuh kosong.
Mode Kegagalan
Name
400 - Bad Request
Description
Tugas sudah dalam status akhir dan tidak dapat dibatalkan.
Name
404 - Not Found
Description
Tugas tidak ada, milik pengguna lain, atau tahapannya tidak sesuai dengan jalur URL.
Name
500 - Internal Server Error
Description
Terjadi kesalahan tak terduga di sisi server saat membatalkan. Tugas mungkin sudah atau belum dibatalkan — baca ulang untuk mengonfirmasi sebelum mencoba lagi.
Streaming pembaruan real-time untuk tugas keycap melalui Server-Sent Events (SSE).
Jalur URL harus sesuai dengan tahap tugas — membuka stream di
/prototype/:buildId/stream akan mengeluarkan satu event: error payload dengan
status_code: 404 dan menutup stream.
Parameter
Name
id
Type
path
Description
Pengenal unik untuk tugas keycap yang akan di-stream.
Mengembalikan
Mengembalikan stream dari objek tugas Keycap Prototype
atau Keycap Build sebagai
Server-Sent Events. Untuk tugas PENDING atau IN_PROGRESS, stream respons
hanya akan menyertakan bidang progress dan status yang diperlukan.
Ambil daftar tugas keycap Anda yang dipaginasi untuk satu tahap. Jalur URL
memilih tahap — /prototype mengembalikan tugas prototipe; /build
mengembalikan tugas build. Tugas dari tahap lain tidak termasuk dalam kedua
respons.
Parameter Jalur
Name
stage
Type
path
Wajib
Description
Baik prototype atau build. Koleksi hanya mengembalikan tugas
yang tahapnya sesuai dengan URL — mengambil /prototype tidak pernah
mengembalikan tugas build dan sebaliknya.
Parameter Kuery
Name
page_num
Type
integer
default 1
Description
Nomor halaman untuk paginasi.
Name
page_size
Type
integer
default 10
Description
Batas ukuran halaman. Maksimum yang diizinkan adalah 100 item.
Name
sort_by
Type
string
default -created_at
Description
Bidang untuk diurutkan. Nilai yang tersedia:
+created_at: Urutkan berdasarkan waktu pembuatan dalam urutan naik.
-created_at: Urutkan berdasarkan waktu pembuatan dalam urutan turun.
Objek Tugas Prototipe Keycap adalah unit kerja yang dilacak oleh Meshy untuk menghasilkan satu gambar desain keycap selesai dari foto sumber. Hasil dari tahap ini dihubungkan ke tahap pembuatan melalui input_task_id plus candidate_id.
Properti
Name
id
Type
string
Description
Pengenal unik untuk tugas. Meskipun kami menggunakan UUID yang dapat diurutkan secara k untuk id tugas sebagai detail implementasi, Anda tidak boleh membuat asumsi apa pun tentang format id.
Name
type
Type
string
Description
Jenis tugas. Nilainya adalah creative-lab-keycap-prototype.
Name
name
Type
string
Description
Nama tugas yang diberikan saat tugas dibuat. String kosong jika tidak ada nama yang diberikan.
Name
status
Type
string
Description
Status tugas. Nilai yang mungkin adalah salah satu dari PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Kemajuan tugas. Jika tugas belum dimulai, properti ini akan menjadi 0. Setelah tugas berhasil, ini akan menjadi 100.
Name
created_at
Type
timestamp
Description
Stempel waktu saat tugas dibuat, dalam milidetik.
Stempel waktu mewakili jumlah milidetik yang berlalu sejak 1 Januari 1970 UTC, mengikuti standar RFC 3339. Sebagai contoh, Jumat, 1 September 2023 12:00:00 PM GMT diwakili sebagai 1693569600000. Ini berlaku untuk semua stempel waktu dalam Meshy API.
Name
started_at
Type
timestamp
Description
Stempel waktu saat tugas dimulai, dalam milidetik. Jika tugas belum dimulai, properti ini akan menjadi 0.
Name
finished_at
Type
timestamp
Description
Stempel waktu saat tugas selesai, dalam milidetik. Jika tugas belum selesai, properti ini akan menjadi 0.
Name
expires_at
Type
timestamp
Description
Stempel waktu saat hasil tugas kedaluwarsa, dalam milidetik.
Name
preceding_tasks
Type
integer
Description
Jumlah tugas sebelumnya.
Nilai dari bidang ini hanya berarti jika status tugas adalah PENDING.
Name
task_error
Type
object
Description
Detail kesalahan untuk tugas yang gagal. Lihat Kesalahan untuk referensi lengkap objek task_error.
Name
consumed_credits
Type
integer
Description
Jumlah kredit yang dikonsumsi oleh tugas ini. Tugas yang mencapai SUCCEEDED dikenakan biaya penuh untuk tahapannya. Tugas yang tidak pernah dibuat (kesalahan 4xx saat permintaan, termasuk penolakan moderasi) tidak dikenakan biaya sama sekali. Tugas yang mencapai FAILED mengembalikan 0 — biaya dikembalikan, termasuk blok moderasi asinkron. Pembatalan melalui DELETE hanya mengembalikan biaya saat tugas masih PENDING; tugas yang sudah IN_PROGRESS tetap dikenakan biaya, karena pekerjaan telah dilakukan.
Name
image_urls
Type
array of strings
Description
URL yang dapat diunduh dari render desain keycap selesai — seperti apa kandidat terlihat sebagai keycap selesai. Memiliki satu entri; image_urls[i] sesuai dengan candidate_ids[i]. Kosong sampai tugas mencapai SUCCEEDED. URL ini hanya untuk tampilan; endpoint pembuatan mengonsumsi candidate_ids, bukan URL ini. Siklus hidup URL sama dengan model_urls: ditandatangani, tanpa header Authorization, berlaku hingga expires_at, dan stabil saat tugas dibaca ulang.
Name
candidate_ids
Type
array of strings
Description
Pengenal kandidat yang tidak transparan, paralel dengan image_urls. Kirim entri yang sesuai dengan desain pilihan Anda sebagai candidate_id permintaan pembuatan. Jangan membuat asumsi apa pun tentang format id ini.
Objek Tugas Pembuatan Keycap adalah unit kerja yang dilacak oleh Meshy untuk menghasilkan keycap 3D bertekstur akhir dari tugas prototipe yang berhasil dan kandidat yang dipilih. Satu pembuatan menjalankan seluruh pipeline — pembuatan model putih, pemasangan dan pemotongan otomatis, pewarnaan, perakitan, dan ekspor.
Properti
Name
id
Type
string
Description
Pengenal unik untuk tugas tersebut.
Name
type
Type
string
Description
Jenis tugas. Nilainya adalah creative-lab-keycap-build.
Name
name
Type
string
Description
Nama tugas yang diberikan saat tugas dibuat. String kosong jika tidak ada nama yang diberikan.
Name
status
Type
string
Description
Status dari tugas. Nilai yang mungkin adalah salah satu dari PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Kemajuan dari tugas. Jika tugas belum dimulai, properti ini akan bernilai 0. Setelah tugas berhasil, ini akan menjadi 100.
Name
created_at
Type
timestamp
Description
Stempel waktu saat tugas dibuat, dalam milidetik.
Name
started_at
Type
timestamp
Description
Stempel waktu saat tugas dimulai, dalam milidetik.
Name
finished_at
Type
timestamp
Description
Stempel waktu saat tugas selesai, dalam milidetik.
Name
expires_at
Type
timestamp
Description
Stempel waktu saat hasil tugas kedaluwarsa, dalam milidetik.
Name
preceding_tasks
Type
integer
Description
Jumlah tugas sebelumnya. Bermakna hanya ketika status adalah PENDING.
Name
task_error
Type
object
Description
Detail kesalahan untuk tugas yang gagal. Lihat Kesalahan untuk referensi objek task_error lengkap.
Name
consumed_credits
Type
integer
Description
Jumlah kredit yang dikonsumsi oleh tugas ini. Tugas yang mencapai SUCCEEDED dikenakan biaya penuh untuk tahapannya. Tugas yang tidak pernah dibuat (kesalahan 4xx saat permintaan, termasuk penolakan moderasi) tidak dikenakan biaya sama sekali. Tugas yang mencapai FAILED mengembalikan 0 — biaya dikembalikan, termasuk blok moderasi asinkron. Pembatalan melalui DELETE hanya mengembalikan biaya saat tugas masih PENDING; tugas yang sudah IN_PROGRESS tetap dikenakan biaya, karena pekerjaan telah dilakukan.
Name
model_urls
Type
object
Description
URL yang dapat diunduh untuk artefak model yang dihasilkan. Baik bundel GLB maupun OBJ diekspor pada skala milimeter dunia nyata, Y-up, dengan bagian depan keycap menghadap +Z. Mesh dinamai keycap-head dan keycap-base; ketika dasar kembali ke pola isian, mesh ketiga keycap-base-interior juga hadir untuk rongga batang. Jangan berasumsi hanya ada dua mesh.
Ini adalah URL yang ditandatangani: ambil tanpa header Authorization. Mereka tetap valid hingga expires_at, yaitu 3 hari setelah finished_at, dan membaca ulang tugas dalam jangka waktu tersebut mengembalikan URL yang identik daripada yang baru ditandatangani. Unduh dan simpan file sendiri sebelum itu — tidak ada cara untuk memperbarui tautan yang kedaluwarsa.
Name
glb
Type
string
Description
URL yang dapat diunduh ke model.glb bertekstur akhir.
Name
obj_zip
Type
string
Description
URL yang dapat diunduh ke bundel zip yang berisi model.obj, model.mtl, dan PNG tekstur yang sebenarnya dirujuk oleh MTL-nya. Basis warna solid hanya mengirimkan keycap-head.png; basis berpola juga mengirimkan keycap-base.png.
Name
process_image_urls
Type
object
Description
URL yang dapat diunduh untuk gambar proses antara, diindeks berdasarkan jenis. Siklus hidup URL sama seperti model_urls: ditandatangani, tanpa header Authorization, valid hingga expires_at, dan stabil saat tugas dibaca ulang. Jenis yang saat ini dihasilkan:
head_design — gambar desain kandidat yang dipilih yang dikonsumsi oleh pembuatan (selalu ada).
composite — render tampilan keycap selesai dari kandidat yang dipilih (ada jika tersedia).
base_canvas — kanvas dasar keycap yang dicat (ada jika tersedia).
Perlakukan set kunci sebagai terbuka; jenis baru dapat ditambahkan tanpa perubahan yang merusak.
Alur lengkap: buat prototipe dari foto, polling hingga SUCCEEDED, pilih kandidat dari candidate_ids, buat build dengan kandidat tersebut, polling build hingga SUCCEEDED, lalu unduh GLB dan bundel OBJ dari model_urls.
Contoh ini memilih kandidat pertama secara programatis. Dalam integrasi nyata, Anda akan menampilkan entri image_urls kepada pengguna akhir dan membiarkan mereka memilih; indeks yang dipilih memetakan 1:1 ke candidate_ids.
Alur lengkap
POST
/openapi/creative-lab/keycap/v1
#!/usr/bin/env bashset-euopipefail# 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:-}" ]]; thenecho"export IMAGE_PATH (local file) or IMAGE_URL (public url) first">&2exit1fiBASE="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 bodyshift2 out=$(curl--silent--show-error--max-time60--write-out$'\n%{http_code}' \-X"$method""$url"-H"$AUTH""$@") ||return1 http_code=${out##*$'\n'} body=${out%$'\n'*}if ((http_code >=400)); thenecho"HTTP $http_code for $url: $body">&2return1fiprintf'%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:; doif (($(date +%s) >= deadline)); thenecho"gave up waiting for $kind $id">&2return1fi task_status=$(apiGET"$BASE/$kind/$id"|jq-r'.status')echo"$kind: $task_status"case"$task_status"inSUCCEEDED)return0 ;;FAILED|CANCELED)return1 ;;esacsleep"$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"'EXITif [[ -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')"inpng) 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"elseprintf'{"image_url":"%s"}'"$IMAGE_URL">"$BODY"fi# 1. Create the prototype taskPROTO_ID=$(apiPOST"$BASE/prototype" \-H'Content-Type: application/json'--data-binary@"$BODY"|jq-r'.result')# 2. Wait for the design renderpollprototype"$PROTO_ID"# 3. Pick a candidate (first one here; show image_urls to a user in production)CANDIDATE_ID=$(apiGET"$BASE/prototype/$PROTO_ID"|jq-r'.candidate_ids[0]')# 4. Create the build taskjq-n--argp"$PROTO_ID"--argc"$CANDIDATE_ID" \'{input_task_id: $p, candidate_id: $c}'>"$BODY"BUILD_ID=$(apiPOST"$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)pollbuild"$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=$(apiGET"$BASE/build/$BUILD_ID")curl--silent--show-error--fail--max-time900 \-okeycap.glb"$(jq-r '.model_urls.glb' <<<"$TASK")"curl--silent--show-error--fail--max-time900 \-okeycap-obj.zip"$(jq-r '.model_urls.obj_zip' <<<"$TASK")"echo"Done: keycap.glb + keycap-obj.zip"