Trasforma una foto sorgente in un keycap personalizzato per tastiera meccanica a colori in
due fasi: prototipo genera un rendering del design del "keycap finito"
dalla tua foto di input. Una volta confermato quel rendering, costruzione lo trasforma in un
modello 3D del keycap con texture in un'unica esecuzione — generazione del modello bianco, posizionamento e taglio automatici su una posa predefinita calibrata, colorazione del modello completo e
assemblaggio finale avvengono tutti all'interno di un'unica attività di costruzione. Le due fasi sono collegate
tramite input_task_id più candidate_id.
POST /openapi/creative-lab/keycap/v1/prototype
POST /openapi/creative-lab/keycap/v1/build
Entrambi gli endpoint POST richiedono un piano di abbonamento a pagamento. Le richieste da
account con piano gratuito vengono rifiutate con 402 Payment Required.
Genera un render del design del keycap finito dalla foto sorgente. Il risultato dell'attività contiene un array image_urls (il render di visualizzazione del keycap finito) e un array parallelo candidate_ids; entrambi contengono una singola voce. Chiama di nuovo questo endpoint per un altro render se il risultato non è quello che desideri — ogni chiamata è fatturata separatamente. Passa il candidate_id insieme all'ID dell'attività di prototipo all'endpoint di build. Consulta
L'oggetto dell'attività di prototipo di Keycap
per la forma della risposta.
Parametri
Name
image_url
Type
string
Obbligatorio
Description
Foto sorgente per Meshy da trasformare in immagini di design del keycap. Attualmente supportiamo i formati .jpg, .jpeg, .png e .webp.
Il formato è rilevato decodificando i dati dell'immagine, non dall'estensione del file dell'URL — un URL senza estensione, o uno che reindirizza, funziona finché i byte decodificano in un formato supportato. I reindirizzamenti HTTP sono seguiti. L'orientamento EXIF è normalizzato, quindi una foto ruotata del telefono viene utilizzata come appare.
Limiti: almeno 32 pixel su ciascun lato, al massimo 178,956,970 pixel in totale e al massimo 20,000,000 byte una volta scaricati. Per un Data URI il limite si applica ai byte decodificati, quindi il file sorgente stesso può essere fino a quella dimensione — è il testo base64 che è circa un terzo più grande, il che conta per il corpo della tua richiesta, non per questo limite. Un Data URI deve dichiarare un tipo di contenuto image/* e ;base64.
Ci sono due modi per fornire l'immagine:
URL accessibile pubblicamente: Un URL accessibile da internet pubblica.
Data URI: Un Data URI codificato in base64 dell'immagine. Esempio di un Data URI: data:image/jpeg;base64,<your base64-encoded image data>.
Name
name
Type
string
Description
Nome dell'attività opzionale per scopi di visualizzazione. Massimo 100 caratteri.
Name
remove_background
Type
boolean
predefinito false
Description
Quando impostato su true, il render di visualizzazione restituito in image_urls è un PNG RGBA trasparente con lo sfondo rimosso, così puoi comporlo su qualsiasi sfondo.
Questo si applica solo al render di visualizzazione. Il candidato che l'endpoint di build consuma non è influenzato, quindi il risultato 3D è identico in entrambi i casi.
Restituisce
La proprietà result della risposta contiene l'id dell'attività del prototipo di keycap appena creato. Interroga l'endpoint Ottieni un'attività o iscriviti al flusso fino a quando l'attività raggiunge SUCCEEDED, quindi prendi la voce da candidate_ids e passala, insieme all'ID dell'attività, all'endpoint di build.
Modalità di fallimento
Name
400 - Bad Request
Description
La richiesta non era accettabile. Cause comuni:
Parametro mancante: image_url è richiesto.
Formato immagine non valido: L'image_url fornito non è un formato supportato (.jpg, .jpeg, .png, .webp).
Dimensioni immagine fuori intervallo: L'immagine è troppo piccola, supera la dimensione massima del file o supera il conteggio massimo dei pixel.
URL irraggiungibile: L'image_url non può essere scaricato (404 o timeout).
Data URI non valido: La stringa base64 è malformata.
Contenuto segnalato: L'immagine di input è stata segnalata dalla moderation NSFW.
Name
401 - Unauthorized
Description
Autenticazione fallita. Si prega di controllare la tua chiave API.
Name
402 - Payment Required
Description
L'account è sul piano gratuito (è richiesto un piano a pagamento per creare attività) o non ha crediti sufficienti.
Name
403 - Forbidden
Description
L'immagine di input è stata segnalata dalla moderation della proprietà intellettuale.
Name
429 - Too Many Requests
Description
Hai superato il tuo limite di frequenza.
Name
500 - Internal Server Error
Description
Si è verificato un errore imprevisto lato server — ad esempio il servizio di moderation dei contenuti non era disponibile, la messa in scena dell'immagine di input è fallita, o l'attività non può essere creata. In questo caso non viene creata alcuna attività, quindi riprovare è sicuro.
Request
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>" }'
Genera il modello 3D finale del keycap texturizzato da un task di prototipo riuscito e uno dei suoi candidati. Un singolo task di costruzione esegue l'intera pipeline dall'inizio alla fine — generazione del modello bianco dal design scelto, posizionamento e taglio automatico sulla base del keycap utilizzando una posa predefinita calibrata (non è necessario alcun aggiustamento interattivo), colorazione del modello completo, e assemblaggio finale ed esportazione. Una costruzione tipicamente richiede 3–7 minuti, verso l'estremità superiore quando diversi task di costruzione vengono eseguiti contemporaneamente. Consulta L'oggetto Task di Costruzione Keycap per la forma della risposta.
Parametri
Name
input_task_id
Type
string
Obbligatorio
Description
L'ID del task di un prototipo creato tramite questo stesso endpoint OpenAPI. Il prototipo deve essere stato creato dallo stesso account Meshy, deve aver raggiunto SUCCEEDED, e deve aver prodotto almeno un candidato.
I task di prototipo creati tramite l'app web non sono accettati — l'endpoint di costruzione accetta solo task di prototipo prodotti da POST /openapi/creative-lab/keycap/v1/prototype e rifiuta qualsiasi altra fonte con 404.
Name
candidate_id
Type
string
Obbligatorio
Description
Il candidato da costruire, preso dall'array candidate_ids del task di prototipo riuscito. Deve appartenere a quel task; qualsiasi altro valore viene rifiutato con 400.
Name
name
Type
string
Description
Nome del task opzionale per scopi di visualizzazione. Massimo 100 caratteri.
options
Regolazione opzionale della geometria. Ogni campo ha un valore predefinito calibrato — invia solo quelli che desideri sovrascrivere.
Name
base_model
Type
string
predefinito cherry-mx-1x1-r1
Description
La base del keycap su cui costruire. Attualmente l'unico valore disponibile è cherry-mx-1x1-r1 — un keycap standard Cherry MX profilo 1u. Sono pianificate 3–5 dimensioni standard principali aggiuntive; le dimensioni personalizzate non sono supportate.
Name
head_size_mm
Type
number
predefinito 23
Description
Dimensione target della testa scolpita, in millimetri: la sua dimensione più lunga è scalata a questo valore. Intervallo: [10, 40]. I valori superiori a circa 32.9 possono essere ridotti in modo che la testa rientri ancora nel limite dell'impronta protettiva della base, quindi la dimensione più lunga consegnata può essere inferiore a quella richiesta. Il valore applicato non viene restituito oggi sull'oggetto del task — se hai bisogno di confermare la dimensione effettivamente ricevuta, misura il bounding box della mesh keycap-head nel modello scaricato.
Name
vertical_offset_mm
Type
number
predefinito 0
Description
Offset verticale applicato alla testa prima che venga posizionata sulla base, in millimetri. Intervallo: [-5, 5].
Restituisce
La proprietà result della risposta contiene l'id del task del nuovo task di costruzione keycap creato. Interroga l'endpoint Ottieni un Task o iscriviti al stream fino a quando il task raggiunge SUCCEEDED, quindi scarica gli artefatti da model_urls.glb e model_urls.obj_zip.
Sia il pacchetto GLB che quello OBJ sono esportati a scala reale in millimetri, con un sistema di coordinate Y-up e la parte anteriore del keycap rivolta verso +Z.
Modalità di Fallimento
Name
400 - Bad Request
Description
La richiesta non era accettabile. Cause comuni:
Parametro mancante: input_task_id e candidate_id sono richiesti.
UUID non valido: L'input_task_id non è un UUID valido.
Genitore non riuscito: Il task di prototipo di riferimento non ha ancora raggiunto SUCCEEDED.
Nessun candidato: Il task di prototipo è riuscito ma non ha prodotto candidati.
Candidato sconosciuto: candidate_id non è uno dei candidati del task di input.
Opzioni fuori intervallo: Uno dei campi options è caduto al di fuori del suo intervallo consentito o set di enumerazione.
Name
401 - Unauthorized
Description
Autenticazione fallita. Si prega di controllare la tua chiave API.
Name
402 - Payment Required
Description
L'account è sul piano gratuito (è richiesto un piano a pagamento per creare task) o ha crediti insufficienti.
Name
404 - Not Found
Description
Il task di prototipo di riferimento non esiste, appartiene a un altro utente, o è stato creato tramite l'app web (solo i task di prototipo in modalità API si concatenano nella costruzione).
Name
429 - Too Many Requests
Description
Hai superato il tuo limite di frequenza.
Name
500 - Internal Server Error
Description
Si è verificato un errore inaspettato lato server — ad esempio il servizio di moderazione dei contenuti non era disponibile, la messa in scena dell'immagine di input è fallita, o il task non poteva essere creato. Nessun task viene creato in questo caso, quindi riprovare è sicuro.
Richiesta
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 } }'
Recupera un task di prototipo o build dato un valido id del task. Il percorso URL
deve corrispondere alla fase del task — un task di build recuperato tramite
/prototype/:id restituisce 404, e viceversa.
Annulla un'attività keycap. Se l'attività è ancora PENDING, i crediti
consumati al momento della creazione vengono rimborsati. Le attività che sono già
IN_PROGRESS vengono annullate senza rimborso (il lavoratore potrebbe già essere
in fase di utilizzo delle risorse). Le attività che hanno già raggiunto uno stato terminale
(SUCCEEDED, FAILED, CANCELED) non possono essere annullate.
Il percorso URL deve corrispondere alla fase dell'attività — DELETE su
/prototype/:buildId restituisce 404.
Parametri del Percorso
Name
id
Type
path
Description
Identificatore univoco per l'attività keycap da annullare.
Restituisce
Restituisce 204 No Content in caso di successo con un corpo vuoto.
Modalità di Fallimento
Name
400 - Bad Request
Description
L'attività è già in uno stato terminale e non può essere annullata.
Name
404 - Not Found
Description
L'attività non esiste, appartiene a un altro utente o la sua fase non corrisponde al percorso URL.
Name
500 - Internal Server Error
Description
Si è verificato un errore imprevisto lato server durante l'annullamento. L'attività potrebbe essere stata o meno annullata — rileggila per confermare prima di riprovare.
Trasmetti aggiornamenti in tempo reale per un task keycap tramite Server-Sent Events (SSE).
Il percorso URL deve corrispondere alla fase del task — aprire uno stream su
/prototype/:buildId/stream emette un singolo event: error payload con
status_code: 404 e chiude lo stream.
Parametri
Name
id
Type
path
Description
Identificatore univoco per il task keycap da trasmettere.
Restituisce
Restituisce uno stream di oggetti task Keycap Prototype
o Keycap Build come Server-Sent Events. Per i task PENDING o IN_PROGRESS, il flusso di risposta includerà solo i campi necessari progress e status.
Recupera un elenco paginato delle tue attività keycap per una singola fase. Il percorso URL
seleziona la fase — /prototype restituisce le attività di prototipo; /build
restituisce le attività di costruzione. Le attività dell'altra fase non sono incluse in nessuna delle risposte.
Parametri del Percorso
Name
stage
Type
path
Obbligatorio
Description
O prototype o build. La raccolta restituisce solo le attività
la cui fase corrisponde all'URL — il recupero di /prototype non restituisce mai
attività di costruzione e viceversa.
Parametri di Query
Name
page_num
Type
integer
predefinito 1
Description
Numero di pagina per la paginazione.
Name
page_size
Type
integer
predefinito 10
Description
Limite della dimensione della pagina. Il massimo consentito è di 100 elementi.
Name
sort_by
Type
string
predefinito -created_at
Description
Campo per l'ordinamento. Valori disponibili:
+created_at: Ordina per tempo di creazione in ordine crescente.
-created_at: Ordina per tempo di creazione in ordine decrescente.
L'oggetto Task Prototipo Keycap è un'unità di lavoro che Meshy tiene traccia per generare un'immagine di design del keycap finito da una foto sorgente. L'output di questa fase è collegato alla fase di costruzione tramite input_task_id più candidate_id.
Proprietà
Name
id
Type
string
Description
Identificatore univoco per il task. Anche se utilizziamo un UUID ordinabile per k per gli id dei task come dettaglio di implementazione, non dovresti fare alcuna assunzione sul formato dell'id.
Name
type
Type
string
Description
Tipo di task. Il valore è creative-lab-keycap-prototype.
Name
name
Type
string
Description
Il nome del task fornito quando il task è stato creato. Stringa vuota se non è stato fornito alcun nome.
Name
status
Type
string
Description
Stato del task. I valori possibili sono uno di PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Avanzamento del task. Se il task non è ancora iniziato, questa proprietà sarà 0. Una volta che il task è riuscito, questo diventerà 100.
Name
created_at
Type
timestamp
Description
Timestamp di quando il task è stato creato, in millisecondi.
Un timestamp rappresenta il numero di millisecondi trascorsi dal 1 gennaio 1970 UTC, seguendo
lo standard RFC 3339.
Ad esempio, venerdì 1 settembre 2023 12:00:00 PM GMT è rappresentato come 1693569600000. Questo si applica
a tutti i timestamp nella Meshy API.
Name
started_at
Type
timestamp
Description
Timestamp di quando il task è stato avviato, in millisecondi. Se il task non è ancora iniziato, questa proprietà sarà 0.
Name
finished_at
Type
timestamp
Description
Timestamp di quando il task è stato completato, in millisecondi. Se il task non è ancora completato, questa proprietà sarà 0.
Name
expires_at
Type
timestamp
Description
Timestamp di quando il risultato del task scade, in millisecondi.
Name
preceding_tasks
Type
integer
Description
Il conteggio dei task precedenti.
Il valore di questo campo è significativo solo se lo stato del task è PENDING.
Name
task_error
Type
object
Description
Dettagli dell'errore per i task falliti. Vedi Errori per il riferimento completo all'oggetto task_error.
Name
consumed_credits
Type
integer
Description
Il numero di crediti consumati da questo task. Un task che raggiunge SUCCEEDED viene addebitato l'intero importo per la sua fase. Un task che non viene mai creato (un 4xx al momento della richiesta, inclusa una moderazione rifiutata) non viene addebitato affatto. Un task che raggiunge FAILED restituisce 0 — l'addebito viene rimborsato, inclusa una moderazione asincrona bloccata. La cancellazione tramite DELETE rimborsa solo mentre il task è ancora PENDING; un task già IN_PROGRESS rimane addebitato, perché il lavoro è stato speso.
Name
image_urls
Type
array of strings
Description
URL scaricabile del rendering del design del keycap finito — come appare il candidato come keycap finito. Contiene una sola voce; image_urls[i] corrisponde a candidate_ids[i]. Vuoto fino a quando il task non raggiunge SUCCEEDED. L'URL è solo per la visualizzazione; l'endpoint di costruzione consuma candidate_ids, non questi URL. Stesso ciclo di vita degli URL di model_urls: firmato, senza intestazione Authorization, valido fino a expires_at, e stabile quando il task viene riletto.
Name
candidate_ids
Type
array of strings
Description
Identificatori opachi dei candidati, paralleli a image_urls. Passa la voce che corrisponde al design scelto come candidate_id della richiesta di costruzione. Non fare alcuna assunzione sul formato di questi id.
L'oggetto Task di Costruzione Keycap è un'unità di lavoro che Meshy tiene traccia per generare il keycap 3D finale con texture da un task prototipo riuscito e un candidato scelto. Una singola costruzione esegue l'intera pipeline — generazione del modello bianco, posizionamento e taglio automatico, colorazione, assemblaggio ed esportazione.
Proprietà
Name
id
Type
string
Description
Identificatore univoco per il task.
Name
type
Type
string
Description
Tipo di task. Il valore è creative-lab-keycap-build.
Name
name
Type
string
Description
Il nome del task fornito quando il task è stato creato. Stringa vuota se non è stato fornito alcun nome.
Name
status
Type
string
Description
Stato del task. I valori possibili sono uno di PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Avanzamento del task. Se il task non è ancora iniziato, questa proprietà sarà 0. Una volta che il task è riuscito, questo diventerà 100.
Name
created_at
Type
timestamp
Description
Timestamp di quando il task è stato creato, in millisecondi.
Name
started_at
Type
timestamp
Description
Timestamp di quando il task è stato avviato, in millisecondi.
Name
finished_at
Type
timestamp
Description
Timestamp di quando il task è stato completato, in millisecondi.
Name
expires_at
Type
timestamp
Description
Timestamp di quando il risultato del task scade, in millisecondi.
Name
preceding_tasks
Type
integer
Description
Il conteggio dei task precedenti. Significativo solo quando lo stato è PENDING.
Name
task_error
Type
object
Description
Dettagli degli errori per i task falliti. Vedi Errori per il riferimento completo all'oggetto task_error.
Name
consumed_credits
Type
integer
Description
Il numero di crediti consumati da questo task. Un task che raggiunge SUCCEEDED viene addebitato l'intero importo per la sua fase. Un task che non viene mai creato (un 4xx al momento della richiesta, inclusa una moderazione rifiutata) non viene addebitato affatto. Un task che raggiunge FAILED restituisce 0 — l'addebito viene rimborsato, inclusa una moderazione asincrona bloccata. La cancellazione tramite DELETE rimborsa solo mentre il task è ancora PENDING; un task già IN_PROGRESS rimane addebitato, perché il lavoro è stato speso.
Name
model_urls
Type
object
Description
URL scaricabili per gli artefatti del modello generato. Sia il pacchetto GLB che quello OBJ sono esportati a scala millimetrica reale, Y-up, con la parte anteriore del keycap rivolta verso +Z. I mesh sono denominati keycap-head e keycap-base; quando la base ricade su un riempimento a motivo, è presente anche un terzo mesh keycap-base-interior per la cavità dello stelo. Non assumere esattamente due mesh.
Questi sono URL firmati: recuperali senza un'intestazione Authorization. Rimangono validi fino a expires_at, che è 3 giorni dopo finished_at, e rileggendo il task all'interno di quella finestra restituisce l'URL identico piuttosto che uno appena firmato. Scarica e archivia i file prima di allora — non c'è modo di aggiornare un link scaduto.
Name
glb
Type
string
Description
URL scaricabile per il model.glb finale con texture.
Name
obj_zip
Type
string
Description
URL scaricabile per un pacchetto zip contenente model.obj, model.mtl, e i PNG delle texture a cui il suo MTL fa effettivamente riferimento. Una base a colore solido spedisce solo keycap-head.png; una base a motivo spedisce anche keycap-base.png.
Name
process_image_urls
Type
object
Description
URL scaricabili per le immagini di processo intermedie, indicizzate per tipo. Stesso ciclo di vita degli URL come model_urls: firmati, nessuna intestazione Authorization, validi fino a expires_at, e stabili quando il task viene riletto. Tipi attualmente emessi:
head_design — l'immagine del design del candidato scelto che la costruzione ha consumato (sempre presente).
composite — il render di visualizzazione del keycap finito del candidato scelto (presente quando disponibile).
base_canvas — la tela della base del keycap dipinta (presente quando disponibile).
Tratta il set di chiavi come aperto; nuovi tipi possono essere aggiunti senza una modifica di rottura.
Il flusso completo: crea un prototipo da una foto, controlla che sia
SUCCEEDED, scegli un candidato da candidate_ids, crea una build con
quel candidato, controlla che la build sia SUCCEEDED, quindi scarica il GLB e
il pacchetto OBJ da model_urls.
L'esempio sceglie il primo candidato in modo programmatico. In una vera
integrazione, mostreresti l'elemento image_urls all'utente finale e
lasceresti che scelga; l'indice scelto mappa 1:1 su candidate_ids.
Flusso completo
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"