Verander een bronfoto in een full-color aangepaste mechanische toetsenbordtoets in
twee fasen: prototype genereert een "voltooide toets" ontwerpweergave
van uw invoerfoto. Zodra u die weergave hebt bevestigd, verandert build het in een
getextureerd 3D-toetsmodel in één enkele run — wit-model generatie, automatische
plaatsing en snijden op een gekalibreerde standaardhouding, volledige modelkleuring, en
eindassemblage gebeuren allemaal binnen één bouwtaak. De twee fasen zijn gekoppeld
via input_task_id plus candidate_id.
POST /openapi/creative-lab/keycap/v1/prototype
POST /openapi/creative-lab/keycap/v1/build
Beide POST endpoints vereisen een betaald abonnement. Verzoeken van
gratis-abonnement accounts worden afgewezen met 402 Payment Required.
Genereer een render van een afgewerkte keycap-ontwerp vanuit de bronfoto. Het resultaat van de taak bevat een image_urls array (de weergaverender van de afgewerkte keycap) en een parallelle candidate_ids array; beide bevatten één item. Roep deze endpoint opnieuw aan voor een andere render als het resultaat niet is wat je wilt — elke oproep wordt afzonderlijk gefactureerd. Geef de candidate_id samen met de prototype taak-ID door aan de build endpoint. Raadpleeg
Het Keycap Prototype Taak Object
voor de vorm van de respons.
Parameters
Name
image_url
Type
string
Verplicht
Description
Bronfoto voor Meshy om om te zetten in keycap-ontwerpafbeeldingen. We ondersteunen momenteel de formaten .jpg, .jpeg, .png en .webp.
Het formaat wordt gedetecteerd door het decoderen van de afbeeldingsgegevens, niet van de bestandsextensie van de URL — een URL zonder extensie, of een die omleidt, werkt zolang de bytes decoderen naar een ondersteund formaat. HTTP-omleidingen worden gevolgd. EXIF-oriëntatie wordt genormaliseerd, dus een gedraaide telefoonfoto wordt gebruikt zoals deze eruitziet.
Limieten: minimaal 32 pixels aan elke zijde, maximaal 178,956,970 pixels in totaal, en maximaal 20,000,000 bytes eenmaal gedownload. Voor een data URI geldt de limiet voor de gedecodeerde bytes, dus het bronbestand zelf mag tot die grootte zijn — het is de base64-tekst die ongeveer een derde groter is, wat van belang is voor je request body, niet voor deze limiet. Een data URI moet een image/* contenttype en ;base64 declareren.
Er zijn twee manieren om de afbeelding te verstrekken:
Publiek toegankelijke URL: Een URL die toegankelijk is vanaf het openbare internet.
Data URI: Een base64-gecodeerde data URI van de afbeelding. Voorbeeld van een data URI: data:image/jpeg;base64,<je base64-gecodeerde afbeeldingsgegevens>.
Name
name
Type
string
Description
Optionele taaknaam voor weergavedoeleinden. Maximaal 100 tekens.
Name
remove_background
Type
boolean
standaard false
Description
Wanneer ingesteld op true, is de weergaverender die wordt geretourneerd in image_urls een transparante RGBA PNG met de achtergrond verwijderd, zodat je deze op elke achtergrond kunt samenstellen.
Dit geldt alleen voor de weergaverender. De kandidaat die de build endpoint verbruikt, blijft onaangetast, dus het 3D-resultaat is in beide gevallen identiek.
Retourneert
De result eigenschap van de respons bevat de taak id van de nieuw aangemaakte keycap prototype taak. Poll de Get a Task endpoint of abonneer je op de stream totdat de taak SUCCEEDED bereikt, neem dan het item uit candidate_ids en geef het, samen met de taak-ID, door aan de build endpoint.
Foutmodi
Name
400 - Bad Request
Description
Het verzoek was onacceptabel. Veelvoorkomende oorzaken:
Ontbrekende parameter: image_url is vereist.
Ongeldig afbeeldingsformaat: De verstrekte image_url is geen ondersteund formaat (.jpg, .jpeg, .png, .webp).
Afbeeldingsafmetingen buiten bereik: De afbeelding is te klein, overschrijdt de maximale bestandsgrootte of overschrijdt het maximale aantal pixels.
Onbereikbare URL: De image_url kon niet worden gedownload (404 of timeout).
Ongeldige Data URI: De base64-string is ongeldig.
Inhoud gemarkeerd: De invoerafbeelding is gemarkeerd door NSFW moderation.
Name
401 - Unauthorized
Description
Authenticatie mislukt. Controleer je API-sleutel.
Name
402 - Payment Required
Description
Het account bevindt zich op het gratis plan (een betaald plan is vereist om taken te maken) of heeft onvoldoende credits.
Name
403 - Forbidden
Description
De invoerafbeelding is gemarkeerd door intellectuele eigendom moderation.
Name
429 - Too Many Requests
Description
Je hebt je rate limit overschreden.
Name
500 - Internal Server Error
Description
Er is een onverwachte server-side fout opgetreden — bijvoorbeeld de content-moderation service was niet beschikbaar, het voorbereiden van de invoerafbeelding is mislukt, of de taak kon niet worden aangemaakt. Er wordt in dit geval geen taak aangemaakt, dus opnieuw proberen is veilig.
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>" }'
Genereer het uiteindelijke 3D keycap model met textuur van een geslaagde prototype taak en een van zijn kandidaten. Een enkele build taak voert de hele pijplijn van begin tot eind uit — wit-model generatie van het gekozen ontwerp, automatische plaatsing en uitsnijding op de keycap basis met behulp van een gekalibreerde standaardpositie (geen interactieve aanpassing nodig), volledige modelkleuring, en uiteindelijke assemblage en export. Een build duurt doorgaans 3–7 minuten, aan de hogere kant wanneer meerdere builds gelijktijdig worden uitgevoerd. Raadpleeg Het Keycap Build Taak Object voor de vorm van de respons.
Parameters
Name
input_task_id
Type
string
Verplicht
Description
De taak-ID van een prototype taak die via dezezelfde OpenAPI endpoint is aangemaakt. Het prototype moet zijn aangemaakt door hetzelfde Meshy-account, moet SUCCEEDED hebben bereikt, en moet ten minste één kandidaat hebben geproduceerd.
Prototype taken die via de webapp zijn aangemaakt worden niet geaccepteerd — de build endpoint accepteert alleen prototype taken geproduceerd door POST /openapi/creative-lab/keycap/v1/prototype en weigert elke andere bron met 404.
Name
candidate_id
Type
string
Verplicht
Description
De kandidaat om te bouwen, genomen uit de candidate_ids array van de geslaagde prototype taak. Moet tot die taak behoren; elke andere waarde wordt afgewezen met 400.
Name
name
Type
string
Description
Optionele taaknaam voor weergavedoeleinden. Maximum 100 tekens.
options
Optionele geometrie-aanpassing. Elk veld heeft een gekalibreerde standaardwaarde — stuur alleen de velden die je wilt overschrijven.
Name
base_model
Type
string
standaard cherry-mx-1x1-r1
Description
De keycap basis om op te bouwen. Momenteel is de enige beschikbare waarde cherry-mx-1x1-r1 — een standaard Cherry MX profiel 1u keycap. 3–5 extra gangbare standaardmaten zijn gepland; aangepaste maten worden niet ondersteund.
Name
head_size_mm
Type
number
standaard 23
Description
Doelgrootte van de gebeeldhouwde kop, in millimeters: de langste afmeting wordt geschaald naar deze waarde. Bereik: [10, 40]. Waarden boven ongeveer 32.9 kunnen worden verkleind zodat de kop nog steeds binnen de beschermende voetafdruklimiet van de basis past, waardoor de geleverde langste afmeting kleiner kan zijn dan gevraagd. De toegepaste waarde wordt vandaag niet teruggegeven op het taakobject — als je de grootte wilt bevestigen die je daadwerkelijk hebt ontvangen, meet dan de bounding box van de keycap-head mesh in het gedownloade model.
Name
vertical_offset_mm
Type
number
standaard 0
Description
Verticale offset toegepast op de kop voordat deze op de basis wordt geplaatst, in millimeters. Bereik: [-5, 5].
Retourneert
De result eigenschap van de respons bevat de taak id van de nieuw aangemaakte keycap build taak. Poll de Get a Task endpoint of abonneer je op de stream totdat de taak SUCCEEDED bereikt, en download dan de artefacten van model_urls.glb en model_urls.obj_zip.
Zowel de GLB als de OBJ bundel worden geëxporteerd op echte wereld millimeterschaal, met een Y-up coördinatensysteem en de voorkant van de keycap gericht naar +Z.
Foutmodi
Name
400 - Bad Request
Description
Het verzoek was onacceptabel. Veelvoorkomende oorzaken:
Ontbrekende parameter: input_task_id en candidate_id zijn vereist.
Ongeldige UUID: De input_task_id is geen geldige UUID.
Ouder niet geslaagd: De genoemde prototype taak heeft SUCCEEDED nog niet bereikt.
Geen kandidaten: De prototype taak is geslaagd maar heeft geen kandidaten geproduceerd.
Onbekende kandidaat: candidate_id is niet een van de kandidaten van de input taak.
Opties buiten bereik: Een van de options velden viel buiten het toegestane bereik of enum set.
Name
401 - Unauthorized
Description
Authenticatie mislukt. Controleer je API-sleutel.
Name
402 - Payment Required
Description
Het account is op het gratis plan (een betaald plan is vereist om taken te maken) of heeft onvoldoende credits.
Name
404 - Not Found
Description
De genoemde prototype taak bestaat niet, behoort tot een andere gebruiker, of is aangemaakt via de webapp (alleen API-modus prototype taken schakelen over naar build).
Name
429 - Too Many Requests
Description
Je hebt je rate limit overschreden.
Name
500 - Internal Server Error
Description
Er is een onverwachte server-side fout opgetreden — bijvoorbeeld de content-moderation service was niet beschikbaar, het voorbereiden van de input afbeelding is mislukt, of de taak kon niet worden aangemaakt. Er wordt in dit geval geen taak aangemaakt, dus opnieuw proberen is veilig.
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 } }'
Haal een prototype- of bouwtaak op met een geldig taak id. Het URL-pad
moet overeenkomen met de fase van de taak — een bouwtaak opgehaald via
/prototype/:id geeft 404, en vice versa.
Annuleer een keycap taak. Als de taak nog PENDING is, worden de credits
die bij het aanmaken zijn verbruikt, teruggestort. Taken die al
IN_PROGRESS zijn, worden geannuleerd zonder terugbetaling (de werknemer kan al bezig zijn met het verbruiken van bronnen). Taken die al een eindstatus hebben bereikt
(SUCCEEDED, FAILED, CANCELED) kunnen niet worden geannuleerd.
Het URL-pad moet overeenkomen met de fase van de taak — DELETE op
/prototype/:buildId geeft 404 terug.
Pad Parameters
Name
id
Type
path
Description
Unieke identificatie voor de keycap taak om te annuleren.
Retourneert
Retourneert 204 No Content bij succes met een lege body.
Foutmodi
Name
400 - Bad Request
Description
De taak is al in een eindstatus en kan niet worden geannuleerd.
Name
404 - Not Found
Description
De taak bestaat niet, behoort tot een andere gebruiker, of de fase komt niet overeen met het URL-pad.
Name
500 - Internal Server Error
Description
Er is een onverwachte serverfout opgetreden tijdens het annuleren. De taak kan al dan niet zijn geannuleerd — lees deze opnieuw om te bevestigen voordat u het opnieuw probeert.
Stream real-time updates voor een keycap taak via Server-Sent Events (SSE).
Het URL-pad moet overeenkomen met de fase van de taak — het openen van een stream bij
/prototype/:buildId/stream geeft een enkele event: error payload met
status_code: 404 en sluit de stream.
Parameters
Name
id
Type
path
Description
Unieke identificatie voor de keycap taak om te streamen.
Retourneert
Retourneert een stream van Keycap Prototype
of Keycap Build taakobjecten als
Server-Sent Events. Voor PENDING of IN_PROGRESS taken, zal de responsstream
alleen de noodzakelijke progress en status velden bevatten.
Haal een gepagineerde lijst op van je keycap-taken voor een enkele fase. Het URL-pad selecteert de fase — /prototype retourneert prototype-taken; /build retourneert build-taken. Taken van de andere fase zijn in geen van beide antwoorden inbegrepen.
Padparameters
Name
stage
Type
path
Verplicht
Description
Ofwel prototype of build. De collectie retourneert alleen taken
waarvan de fase overeenkomt met de URL — het ophalen van /prototype retourneert nooit build-taken en vice versa.
Queryparameters
Name
page_num
Type
integer
standaard 1
Description
Paginanummer voor paginering.
Name
page_size
Type
integer
standaard 10
Description
Limiet voor paginagrootte. Maximum toegestaan is 100 items.
Name
sort_by
Type
string
standaard -created_at
Description
Veld om op te sorteren. Beschikbare waarden:
+created_at: Sorteren op creatietijd in oplopende volgorde.
-created_at: Sorteren op creatietijd in aflopende volgorde.
Het Keycap Prototype Taakobject is een werkunit die Meshy bijhoudt om één afgewerkt keycap-ontwerpafbeelding te genereren vanuit een bronfoto. De output van deze fase wordt gekoppeld aan de bouwfase via input_task_id plus candidate_id.
Eigenschappen
Name
id
Type
string
Description
Unieke identificatie voor de taak. Hoewel we een k-sorteerbare UUID gebruiken voor taak-id's als implementatiedetail, moet je geen aannames maken over het formaat van de id.
Name
type
Type
string
Description
Type van de taak. De waarde is creative-lab-keycap-prototype.
Name
name
Type
string
Description
De taaknaam die werd opgegeven toen de taak werd aangemaakt. Lege string als er geen naam werd opgegeven.
Name
status
Type
string
Description
Status van de taak. Mogelijke waarden zijn een van PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Voortgang van de taak. Als de taak nog niet is gestart, zal deze eigenschap 0 zijn. Zodra de taak is geslaagd, wordt dit 100.
Name
created_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is aangemaakt, in milliseconden.
Een tijdstempel vertegenwoordigt het aantal milliseconden dat is verstreken sinds 1 januari 1970 UTC, volgens de RFC 3339 standaard. Bijvoorbeeld, vrijdag 1 september 2023 12:00:00 PM GMT wordt weergegeven als 1693569600000. Dit geldt voor alle tijdstempels in Meshy API.
Name
started_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is gestart, in milliseconden. Als de taak nog niet is gestart, zal deze eigenschap 0 zijn.
Name
finished_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is voltooid, in milliseconden. Als de taak nog niet is voltooid, zal deze eigenschap 0 zijn.
Name
expires_at
Type
timestamp
Description
Tijdstempel van wanneer het taakresultaat verloopt, in milliseconden.
Name
preceding_tasks
Type
integer
Description
Het aantal voorafgaande taken.
De waarde van dit veld is alleen betekenisvol als de taakstatus PENDING is.
Name
task_error
Type
object
Description
Foutdetails voor mislukte taken. Zie Fouten voor de volledige task_error objectreferentie.
Name
consumed_credits
Type
integer
Description
Het aantal credits dat door deze taak is verbruikt. Een taak die SUCCEEDED bereikt, wordt het volledige bedrag voor zijn fase in rekening gebracht. Een taak die nooit wordt aangemaakt (een 4xx op het moment van de aanvraag, inclusief een moderatieafwijzing) wordt helemaal niet in rekening gebracht. Een taak die FAILED bereikt, retourneert 0 — de kosten worden terugbetaald, inclusief een asynchrone moderatieblokkering. Annuleren via DELETE wordt alleen terugbetaald zolang de taak nog PENDING is; een taak die al IN_PROGRESS is, blijft in rekening gebracht, omdat het werk is besteed.
Name
image_urls
Type
array of strings
Description
Downloadbare URL van de afgewerkte keycap-ontwerprender — hoe de kandidaat eruitziet als een afgewerkte keycap. Bevat één item; image_urls[i] komt overeen met candidate_ids[i]. Leeg totdat de taak SUCCEEDED bereikt. De URL is alleen voor weergave; de bouwendpoint verbruikt candidate_ids, niet deze URL's. Zelfde URL-levenscyclus als model_urls: ondertekend, geen Authorization header, geldig tot expires_at, en stabiel wanneer de taak opnieuw wordt gelezen.
Name
candidate_ids
Type
array of strings
Description
Ondoorzichtige kandidaat-identificaties, parallel aan image_urls. Geef de vermelding die overeenkomt met je gekozen ontwerp door als de candidate_id van het bouwverzoek. Maak geen aannames over het formaat van deze id's.
Het Keycap Build Task-object is een werkeenheid die Meshy bijhoudt om de uiteindelijke getextureerde 3D-keycap te genereren vanuit een geslaagde prototype-taak en een gekozen kandidaat. Een enkele build doorloopt de volledige pijplijn — wit-model generatie, automatische plaatsing en snijden, inkleuren, assemblage en export.
Eigenschappen
Name
id
Type
string
Description
Unieke identificatie voor de taak.
Name
type
Type
string
Description
Type van de taak. De waarde is creative-lab-keycap-build.
Name
name
Type
string
Description
De taaknaam die werd opgegeven bij het aanmaken van de taak. Lege string als er geen naam werd opgegeven.
Name
status
Type
string
Description
Status van de taak. Mogelijke waarden zijn een van PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Voortgang van de taak. Als de taak nog niet is gestart, zal deze eigenschap 0 zijn. Zodra de taak is geslaagd, wordt dit 100.
Name
created_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is aangemaakt, in milliseconden.
Name
started_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is gestart, in milliseconden.
Name
finished_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is voltooid, in milliseconden.
Name
expires_at
Type
timestamp
Description
Tijdstempel van wanneer het taakresultaat verloopt, in milliseconden.
Name
preceding_tasks
Type
integer
Description
Het aantal voorafgaande taken. Betekenisvol alleen wanneer de status PENDING is.
Name
task_error
Type
object
Description
Foutdetails voor mislukte taken. Zie Fouten voor de volledige task_error objectreferentie.
Name
consumed_credits
Type
integer
Description
Het aantal credits dat door deze taak is verbruikt. Een taak die SUCCEEDED bereikt, wordt het volledige bedrag voor zijn fase in rekening gebracht. Een taak die nooit wordt aangemaakt (een 4xx op het moment van aanvraag, inclusief een moderatie-afwijzing) wordt helemaal niet in rekening gebracht. Een taak die FAILED bereikt, retourneert 0 — de kosten worden terugbetaald, inclusief een asynchrone moderatieblokkering. Annuleren via DELETE geeft alleen een terugbetaling zolang de taak nog PENDING is; een taak die al IN_PROGRESS is, blijft in rekening gebracht, omdat het werk al is verricht.
Name
model_urls
Type
object
Description
Downloadbare URL's voor de gegenereerde modelartefacten. Zowel de GLB als de OBJ-bundel worden geëxporteerd op echte wereld millimeterschaal, Y-up, met de voorkant van de keycap gericht naar +Z. Meshes worden genoemd keycap-head en keycap-base; wanneer de basis terugvalt op een patroonvulling, is er ook een derde mesh keycap-base-interior aanwezig voor de steelholte. Ga niet uit van precies twee meshes.
Dit zijn ondertekende URL's: haal ze op zonder een Authorization-header. Ze blijven geldig tot expires_at, wat 3 dagen na finished_at is, en het opnieuw lezen van de taak binnen dat venster retourneert dezelfde URL in plaats van een vers ondertekende. Download en sla de bestanden zelf op voor die tijd — er is geen manier om een verlopen link te vernieuwen.
Name
glb
Type
string
Description
Downloadbare URL naar de uiteindelijke getextureerde model.glb.
Name
obj_zip
Type
string
Description
Downloadbare URL naar een zip-bundel die model.obj, model.mtl en de textuur PNG's bevat waarnaar de MTL daadwerkelijk verwijst. Een effen kleur basis levert alleen keycap-head.png; een gepatroonde basis levert ook keycap-base.png.
Name
process_image_urls
Type
object
Description
Downloadbare URL's voor tussentijdse procesafbeeldingen, gesorteerd op soort. Zelfde URL-levenscyclus als model_urls: ondertekend, geen Authorization-header, geldig tot expires_at, en stabiel wanneer de taak opnieuw wordt gelezen. Momenteel uitgegeven soorten:
head_design — het ontwerpbeeld van de gekozen kandidaat dat de build heeft verbruikt (altijd aanwezig).
composite — de weergave van de afgewerkte keycap van de gekozen kandidaat (aanwezig wanneer beschikbaar).
base_canvas — het geschilderde keycap-basis canvas (aanwezig wanneer beschikbaar).
Behandel de sleutelset als open-eindig; nieuwe soorten kunnen worden toegevoegd zonder een brekende verandering.
De complete flow: maak een prototype van een foto, poll het naar
SUCCEEDED, kies een kandidaat uit candidate_ids, maak een build met
die kandidaat, poll de build naar SUCCEEDED, en download vervolgens de GLB en
de OBJ bundel van model_urls.
Het voorbeeld kiest de eerste kandidaat programmatisch. In een echte
integratie zou je de image_urls vermelding aan de eindgebruiker tonen en
hen laten kiezen; de gekozen index komt 1:1 overeen met candidate_ids.
Complete flow
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"