Förvandla ett källfoto till en fullfärgad anpassad mekanisk tangentbordstangent i
två steg: prototyp genererar en "färdig tangent"-designrendering
från ditt inmatningsfoto. När du har bekräftat den renderingen, bygg omvandlar den till en
texturerad 3D-tangentmodell i en enda körning — vitmodellgenerering, automatisk
placering och skärning i en kalibrerad standardposition, fullmodellfärgning och
slutmontering sker alla inom en byggtask. De två stegen är länkade
via input_task_id plus candidate_id.
POST /openapi/creative-lab/keycap/v1/prototype
POST /openapi/creative-lab/keycap/v1/build
Båda POST endpoints kräver en betald prenumerationsplan. Förfrågningar från
gratisplan-konton avvisas med 402 Payment Required.
Generera en färdig keycap-designrendering från källfotot. Uppgiftsresultatet innehåller en image_urls-array (visningsrenderingen av den färdiga keycapen) och en parallell candidate_ids-array; båda innehåller en enda post. Anropa denna endpoint igen för en annan rendering om resultatet inte är vad du vill ha — varje anrop debiteras separat. Skicka candidate_id tillsammans med prototypuppgiftens ID till bygg-endpointen. Se
Keycap Prototypuppgiftsobjektet
för svarformatet.
Parametrar
Name
image_url
Type
string
Obligatorisk
Description
Källfoto för Meshy att omvandla till keycap-designbilder. Vi stöder för närvarande formaten .jpg, .jpeg, .png och .webp.
Formatet detekteras genom att avkoda bilddata, inte från URL:ens filändelse — en URL utan ändelse, eller en som omdirigerar, fungerar så länge bytena avkodas till ett stödt format. HTTP-omdirigeringar följs. EXIF-orientering normaliseras, så ett roterat telefonfoto används som det ser ut.
Begränsningar: minst 32 pixlar på varje sida, högst 178,956,970 pixlar totalt, och högst 20,000,000 byte när det har laddats ner. För en data URI gäller gränsen för de avkodade bytena, så källfilen själv kan vara upp till den storleken — det är base64-texten som är ungefär en tredjedel större, vilket är viktigt för din begäran, inte för denna gräns. En data URI måste deklarera en image/* innehållstyp och ;base64.
Det finns två sätt att tillhandahålla bilden:
Offentligt tillgänglig URL: En URL som är tillgänglig från det offentliga internet.
Data URI: En base64-kodad data URI av bilden. Exempel på en data URI: data:image/jpeg;base64,<din base64-kodade bilddata>.
Name
name
Type
string
Description
Valfritt uppgiftsnamn för visningsändamål. Max 100 tecken.
Name
remove_background
Type
boolean
standard false
Description
När den är inställd på true, är visningsrenderingen som returneras i image_urls en transparent RGBA PNG med bakgrunden borttagen, så att du kan komponera den på vilken bakgrund som helst.
Detta gäller endast visningsrenderingen. Kandidaten som bygg-endpointen använder påverkas inte, så 3D-resultatet är identiskt oavsett.
Returnerar
result-egenskapen i svaret innehåller uppgiftens id för den nyligen skapade keycap-prototypuppgiften. Poll Hämta en uppgift endpointen eller prenumerera på strömmen tills uppgiften når SUCCEEDED, ta sedan posten från candidate_ids och skicka den, tillsammans med uppgifts-ID, till bygg-endpointen.
Felmod
Name
400 - Bad Request
Description
Begäran var oacceptabel. Vanliga orsaker:
Saknad parameter: image_url krävs.
Ogiltigt bildformat: Den angivna image_url är inte ett stödt format (.jpg, .jpeg, .png, .webp).
Bilddimensioner utanför räckvidd: Bilden är för liten, överskrider max filstorlek eller överskrider max antal pixlar.
Oåtkomlig URL: image_url kunde inte laddas ner (404 eller timeout).
Ogiltig Data URI: Base64-strängen är felaktig.
Innehåll flaggat: Ingångsbilden flaggades av NSFW-moderation.
Name
401 - Unauthorized
Description
Autentisering misslyckades. Kontrollera din API-nyckel.
Name
402 - Payment Required
Description
Kontot är på gratisplanen (en betald plan krävs för att skapa uppgifter) eller har otillräckliga credits.
Name
403 - Forbidden
Description
Ingångsbilden flaggades av immaterialrättslig moderation.
Name
429 - Too Many Requests
Description
Du har överskridit din hastighetsgräns.
Name
500 - Internal Server Error
Description
Ett oväntat serverfel inträffade — till exempel var innehållsmoderationstjänsten otillgänglig, staging av ingångsbilden misslyckades, eller uppgiften kunde inte skapas. Ingen uppgift skapas i detta fall, så att försöka igen är säkert.
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>" }'
Generera den slutliga texturerade 3D keycap-modellen från en lyckad prototyptask och en av dess kandidater. En enda byggtask kör hela processen från början till slut — vitmodellgenerering från den valda designen, automatisk placering och skärning på keycap-basen med en kalibrerad standardposition (ingen interaktiv justering behövs), full modellfärgning och slutlig montering och export. En byggprocess tar vanligtvis 3–7 minuter, mot den övre gränsen när flera byggprocesser körs samtidigt.
Se The Keycap Build Task Object för svarformatet.
Parametrar
Name
input_task_id
Type
string
Obligatorisk
Description
Task-ID för en prototyptask skapad via denna samma OpenAPI endpoint. Prototypen måste ha skapats av samma Meshy-konto, måste ha nått SUCCEEDED och måste ha producerat minst en kandidat.
Prototyptasks skapade genom webappen accepteras inte — bygg-endpointen accepterar endast prototyptasks producerade av POST /openapi/creative-lab/keycap/v1/prototype och avvisar alla andra källor med 404.
Name
candidate_id
Type
string
Obligatorisk
Description
Kandidaten att bygga, tagen från candidate_ids-arrayen av den lyckade prototyptasken. Måste tillhöra den tasken; alla andra värden avvisas med 400.
Name
name
Type
string
Description
Valfritt tasknamn för visningsändamål. Max 100 tecken.
options
Valfri geometriinställning. Varje fält har ett kalibrerat standardvärde — skicka endast de du vill åsidosätta.
Name
base_model
Type
string
standard cherry-mx-1x1-r1
Description
Keycap-basen att bygga på. För närvarande är det enda tillgängliga värdet cherry-mx-1x1-r1 — en standard Cherry MX profil 1u keycap. 3–5 ytterligare vanliga standardstorlekar planeras; anpassade storlekar stöds inte.
Name
head_size_mm
Type
number
standard 23
Description
Målstorlek för det skulpterade huvudet, i millimeter: dess längsta dimension skalas till detta värde. Område: [10, 40]. Värden över ungefär 32.9 kan reduceras så att huvudet fortfarande passar basens skyddande fotavtrycksgräns, så den levererade längsta dimensionen kan vara mindre än begärd. Det tillämpade värdet återges inte i task-objektet idag — om du behöver bekräfta storleken du faktiskt fick, mät begränsningsrutan för keycap-head nätet i den nedladdade modellen.
Name
vertical_offset_mm
Type
number
standard 0
Description
Vertikal förskjutning applicerad på huvudet innan det placeras på basen, i millimeter. Område: [-5, 5].
Returnerar
result-egenskapen i svaret innehåller taskens id för den nyligen skapade keycap build tasken. Poll Get a Task endpointen eller prenumerera på stream tills tasken når SUCCEEDED, ladda sedan ner artefakterna från model_urls.glb och model_urls.obj_zip.
Både GLB och OBJ-paketet exporteras i verklig värld millimeterskala, med ett Y-upp koordinatsystem och framsidan av keycapen vänd mot +Z.
Felmod
Name
400 - Bad Request
Description
Begäran var oacceptabel. Vanliga orsaker:
Saknad parameter: input_task_id och candidate_id är obligatoriska.
Ogiltig UUID: input_task_id är inte en giltig UUID.
Förälder inte lyckad: Den refererade prototyptasken har inte nått SUCCEEDED än.
Inga kandidater: Prototyptasken lyckades men producerade inga kandidater.
Okänd kandidat: candidate_id är inte en av input taskens kandidater.
Alternativ utanför räckvidd: Ett av options-fälten föll utanför dess tillåtna räckvidd eller enum-set.
Name
401 - Unauthorized
Description
Autentisering misslyckades. Kontrollera din API-nyckel.
Name
402 - Payment Required
Description
Kontot är på gratisplanen (en betald plan krävs för att skapa tasks) eller har otillräckliga credits.
Name
404 - Not Found
Description
Den refererade prototyptasken existerar inte, tillhör en annan användare eller skapades genom webappen (endast API-mode prototyptasks kedjas till bygg).
Name
429 - Too Many Requests
Description
Du har överskridit din hastighetsgräns.
Name
500 - Internal Server Error
Description
Ett oväntat serverfel inträffade — till exempel var innehållsmodereringstjänsten otillgänglig, staging av inmatningsbilden misslyckades, eller tasken kunde inte skapas. Ingen task skapas i detta fall, så det är säkert att försöka igen.
Request
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 } }'
Hämta en prototyp- eller bygguppgift med ett giltigt uppgifts-id. URL-sökvägen
måste matcha uppgiftens steg — en bygguppgift hämtad genom
/prototype/:id returnerar 404, och vice versa.
Avbryt en keycap-uppgift. Om uppgiften fortfarande är PENDING, återbetalas de credits som förbrukades vid skapandet. Uppgifter som redan är IN_PROGRESS avbryts utan återbetalning (arbetaren kan redan använda resurser). Uppgifter som redan har nått ett slutligt tillstånd (SUCCEEDED, FAILED, CANCELED) kan inte avbrytas.
URL-sökvägen måste matcha uppgiftens stadium — DELETE på
/prototype/:buildId returnerar 404.
Sökvägsparametrar
Name
id
Type
path
Description
Unik identifierare för keycap-uppgiften som ska avbrytas.
Returnerar
Returnerar 204 No Content vid framgång med en tom kropp.
Feltyper
Name
400 - Bad Request
Description
Uppgiften är redan i ett slutligt tillstånd och kan inte avbrytas.
Name
404 - Not Found
Description
Uppgiften existerar inte, tillhör en annan användare, eller dess stadium matchar inte URL-sökvägen.
Name
500 - Internal Server Error
Description
Ett oväntat serverfel inträffade vid avbrytandet. Uppgiften kan ha avbrutits eller inte — läs om den för att bekräfta innan du försöker igen.
Strömma realtidsuppdateringar för en keycap-uppgift via Server-Sent Events (SSE).
URL-sökvägen måste matcha uppgiftens steg — att öppna en ström vid
/prototype/:buildId/stream sänder en enda event: error payload med
status_code: 404 och stänger strömmen.
Parametrar
Name
id
Type
path
Description
Unik identifierare för keycap-uppgiften att strömma.
Returnerar
Returnerar en ström av Keycap Prototype
eller Keycap Build uppgiftsobjekt som
Server-Sent Events. För PENDING eller IN_PROGRESS uppgifter kommer
svarströmmen endast att inkludera de nödvändiga progress och status fälten.
Hämta en paginerad lista över dina keycap-uppgifter för ett enskilt stadium. URL-sökvägen väljer stadiet — /prototype returnerar prototypuppgifter; /build returnerar bygguppgifter. Uppgifter från det andra stadiet ingår inte i något av svaren.
Sökvägsparametrar
Name
stage
Type
path
Obligatorisk
Description
Antingen prototype eller build. Samlingen returnerar endast uppgifter
vars stadium matchar URL:en — hämtning av /prototype returnerar aldrig
bygguppgifter och vice versa.
Frågeparametrar
Name
page_num
Type
integer
standard 1
Description
Sidnummer för paginering.
Name
page_size
Type
integer
standard 10
Description
Sidstorleksgräns. Max tillåtet är 100 objekt.
Name
sort_by
Type
string
standard -created_at
Description
Fält att sortera efter. Tillgängliga värden:
+created_at: Sortera efter skapandetid i stigande ordning.
-created_at: Sortera efter skapandetid i fallande ordning.
Keycap-prototypsuppgiftens objekt är en arbetsenhet som Meshy håller reda på för att generera en färdig nyckelkapseldesignbild från ett källfoto. Utdata från detta steg är kopplat till byggsteget via input_task_id plus candidate_id.
Egenskaper
Name
id
Type
string
Description
Unik identifierare för uppgiften. Även om vi använder en k-sorterbar UUID för uppgifts-id som implementeringsdetalj, bör du inte göra några antaganden om formatet på id.
Name
type
Type
string
Description
Typ av uppgift. Värdet är creative-lab-keycap-prototype.
Name
name
Type
string
Description
Det uppgiftsnamn som angavs när uppgiften skapades. Tom sträng om inget namn angavs.
Name
status
Type
string
Description
Status för uppgiften. Möjliga värden är en av PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Framsteg för uppgiften. Om uppgiften inte har startat än, kommer denna egenskap att vara 0. När uppgiften har lyckats, kommer detta att bli 100.
Name
created_at
Type
timestamp
Description
Tidsstämpel för när uppgiften skapades, i millisekunder.
En tidsstämpel representerar antalet millisekunder som har förflutit sedan 1 januari 1970 UTC, enligt
RFC 3339-standarden.
Till exempel, fredag 1 september 2023 12:00:00 PM GMT representeras som 1693569600000. Detta gäller
för alla tidsstämplar i Meshy API.
Name
started_at
Type
timestamp
Description
Tidsstämpel för när uppgiften startades, i millisekunder. Om uppgiften inte har startat än, kommer denna egenskap att vara 0.
Name
finished_at
Type
timestamp
Description
Tidsstämpel för när uppgiften avslutades, i millisekunder. Om uppgiften inte har avslutats än, kommer denna egenskap att vara 0.
Name
expires_at
Type
timestamp
Description
Tidsstämpel för när uppgiftsresultatet går ut, i millisekunder.
Name
preceding_tasks
Type
integer
Description
Antalet föregående uppgifter.
Värdet av detta fält är meningsfullt endast om uppgiftens status är PENDING.
Name
task_error
Type
object
Description
Felformationer för misslyckade uppgifter. Se Fel för fullständig referens av task_error-objektet.
Name
consumed_credits
Type
integer
Description
Antalet credits som förbrukats av denna uppgift. En uppgift som når SUCCEEDED debiteras hela beloppet för sitt steg. En uppgift som aldrig skapas (en 4xx vid begäran, inklusive en moderation avslag) debiteras inte alls. En uppgift som når FAILED returnerar 0 — avgiften återbetalas, inklusive en asynkron moderation block. Avbokning via DELETE återbetalar endast medan uppgiften fortfarande är PENDING; en uppgift som redan är IN_PROGRESS förblir debiterad, eftersom arbetet har utförts.
Name
image_urls
Type
array of strings
Description
Nedladdningsbar URL för den färdiga nyckelkapseldesignens rendering — hur kandidaten ser ut som en färdig nyckelkapsel. Innehåller en enda post; image_urls[i] motsvarar candidate_ids[i]. Tom tills uppgiften når SUCCEEDED. URL:en är endast för visning; bygg-endpointen förbrukar candidate_ids, inte dessa URL:er. Samma URL-livscykel som model_urls: signerad, ingen Authorization-header, giltig tills expires_at, och stabil när uppgiften läses om.
Name
candidate_ids
Type
array of strings
Description
Ogenomskinliga kandidatidentifierare, parallella med image_urls. Skicka posten som matchar din valda design som byggbegärans candidate_id. Gör inga antaganden om formatet på dessa id.
Objektet för Keycap Build-uppgift är en arbetsenhet som Meshy håller reda på för att generera den slutliga texturerade 3D-tangentkappen från en lyckad prototypuppgift och en vald kandidat. En enda byggprocess kör hela pipeline — generering av vit modell, automatisk placering och skärning, färgläggning, montering och export.
Egenskaper
Name
id
Type
string
Description
Unik identifierare för uppgiften.
Name
type
Type
string
Description
Typ av uppgift. Värdet är creative-lab-keycap-build.
Name
name
Type
string
Description
Uppgiftsnamnet som angavs när uppgiften skapades. Tom sträng om inget namn angavs.
Name
status
Type
string
Description
Status för uppgiften. Möjliga värden är en av PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Framsteg för uppgiften. Om uppgiften inte har startat ännu kommer denna egenskap att vara 0. När uppgiften har lyckats kommer detta att bli 100.
Name
created_at
Type
timestamp
Description
Tidsstämpel för när uppgiften skapades, i millisekunder.
Name
started_at
Type
timestamp
Description
Tidsstämpel för när uppgiften startades, i millisekunder.
Name
finished_at
Type
timestamp
Description
Tidsstämpel för när uppgiften avslutades, i millisekunder.
Name
expires_at
Type
timestamp
Description
Tidsstämpel för när uppgiftsresultatet går ut, i millisekunder.
Name
preceding_tasks
Type
integer
Description
Antalet föregående uppgifter. Meningsfullt endast när status är PENDING.
Name
task_error
Type
object
Description
Felformationer för misslyckade uppgifter. Se Fel för fullständig referens av task_error-objektet.
Name
consumed_credits
Type
integer
Description
Antalet credits som förbrukats av denna uppgift. En uppgift som når SUCCEEDED debiteras fullt belopp för sin fas. En uppgift som aldrig skapas (en 4xx vid begäran, inklusive en moderation avslag) debiteras inte alls. En uppgift som når FAILED returnerar 0 — avgiften återbetalas, inklusive en asynkron moderation blockering. Avbrytande via DELETE återbetalar endast medan uppgiften fortfarande är PENDING; en uppgift som redan är IN_PROGRESS förblir debiterad, eftersom arbetet har utförts.
Name
model_urls
Type
object
Description
Nedladdningsbara URL:er för de genererade modellartefakterna. Både GLB och OBJ-paketet exporteras i verklig värld millimeterskala, Y-up, med framsidan av tangentkappen vänd mot +Z. Nät är namngivna keycap-head och keycap-base; när basen faller tillbaka till ett mönsterfyllning finns också ett tredje nät keycap-base-interior för stamkaviteten. Förutsätt inte exakt två nät.
Dessa är signerade URL:er: hämta dem utan en Authorization-header. De förblir giltiga till expires_at, vilket är 3 dagar efter finished_at, och att läsa om uppgiften inom det fönstret returnerar samma URL istället för en ny signerad. Ladda ner och lagra filerna själv innan dess — det finns inget sätt att förnya en utgången länk.
Name
glb
Type
string
Description
Nedladdningsbar URL till den slutliga texturerade model.glb.
Name
obj_zip
Type
string
Description
Nedladdningsbar URL till ett zip-paket som innehåller model.obj, model.mtl, och de textur PNG:er som dess MTL faktiskt refererar till. En enfärgad bas levererar endast keycap-head.png; en mönstrad bas levererar också keycap-base.png.
Name
process_image_urls
Type
object
Description
Nedladdningsbara URL:er för mellanliggande processbilder, nycklade efter typ. Samma URL-livscykel som model_urls: signerade, ingen Authorization-header, giltiga till expires_at, och stabila när uppgiften läses om. För närvarande emitterade typer:
head_design — den valda kandidatens designbild som byggprocessen förbrukade (alltid närvarande).
composite — den färdiga tangentkappens visningsrendering av den valda kandidaten (närvarande när tillgänglig).
base_canvas — den målade tangentkappens basduk (närvarande när tillgänglig).
Behandla nyckeluppsättningen som öppen; nya typer kan läggas till utan en brytande förändring.
Det kompletta flödet: skapa en prototyp från ett foto, poll:a den till
SUCCEEDED, välj en kandidat från candidate_ids, skapa en build med
den kandidaten, poll:a builden till SUCCEEDED, och ladda sedan ner GLB och
OBJ-paketet från model_urls.
Exemplet väljer den första kandidaten programmatiskt. I en verklig
integration skulle du visa image_urls-posten för slutanvändaren och
låta dem välja; det valda indexet mappar 1:1 till candidate_ids.
Komplett flöde
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"