Zet een bronfoto in twee fasen om in een volledig gekleurde, aangepaste mechanische toetsenbord-keycap:
prototype genereert een render van een "afgewerkte keycap"-ontwerp
op basis van je invoerfoto. Zodra je die render hebt bevestigd, zet build deze in één run
om in een 3D-keycapmodel met textuur — het genereren van het witte model, automatisch
plaatsen en snijden op een gekalibreerde standaardpose, kleuring van het volledige model, en
de uiteindelijke assemblage gebeuren allemaal binnen één build-taak. De twee fasen worden 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
accounts met een gratis abonnement worden geweigerd met 402 Payment Required.
Genereer een render van een afgewerkt keycap-ontwerp op basis van de bronfoto. Het taakresultaat bevat een image_urls-array (de weergaverender van de afgewerkte
keycap) en een parallelle candidate_ids-array; beide bevatten één enkel item.
Roep deze endpoint opnieuw aan voor een andere render als het resultaat niet is wat u
wilt — elke aanroep wordt afzonderlijk in rekening gebracht. Geef de candidate_id samen
met de prototype-taak-ID door aan de build-endpoint.
Raadpleeg
Het Keycap Prototype Task Object
voor de vorm van de response.
Parameters
Name
image_url
Type
string
Verplicht
Description
Bronfoto die Meshy omzet in keycap-ontwerpafbeeldingen. We ondersteunen momenteel de indelingen .jpg, .jpeg, .png en .webp.
De indeling wordt gedetecteerd door de afbeeldingsgegevens te decoderen, niet aan de hand van de bestandsextensie van de URL — een URL zonder extensie, of een die doorverwijst, werkt zolang de bytes decoderen naar een ondersteunde indeling. HTTP-redirects worden gevolgd. EXIF-oriëntatie wordt genormaliseerd, zodat een gedraaide telefoonfoto wordt gebruikt zoals hij eruitziet.
Beperkingen: minstens 32 pixels aan elke zijde, hoogstens 178.956.970 pixels in totaal, en hoogstens 20.000.000 bytes na download. 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 relevant is voor uw requestbody, niet voor deze limiet. Een data-URI moet een image/*-content-type en ;base64 bevatten.
Er zijn twee manieren om de afbeelding aan te leveren:
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,<uw 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 waarvan de achtergrond is verwijderd, zodat u deze op elke achtergrond kunt samenvoegen.
Dit geldt alleen voor de weergaverender. De kandidaat die de build-endpoint gebruikt, wordt hierdoor niet beïnvloed, dus het 3D-resultaat is in beide gevallen identiek.
Retourwaarden
De result-eigenschap van de response bevat de taak-id van de nieuw aangemaakte keycap-prototypetaak. Poll de endpoint Een taak ophalen of abonneer u op de stream totdat de taak SUCCEEDED bereikt, en neem vervolgens het item uit candidate_ids en geef dit, samen met de taak-ID, door aan de build-endpoint.
Foutmodi
Name
400 - Bad Request
Description
Het verzoek was onaanvaardbaar. Veelvoorkomende oorzaken:
Ontbrekende parameter: image_url is verplicht.
Ongeldige afbeeldingsindeling: de opgegeven image_url heeft geen ondersteunde indeling (.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-tekenreeks is onjuist opgemaakt.
Content gemarkeerd: de invoerafbeelding is gemarkeerd door NSFW-moderation.
Name
401 - Unauthorized
Description
Authenticatie is mislukt. Controleer uw API-sleutel.
Name
402 - Payment Required
Description
Het account heeft een gratis abonnement (een betaald abonnement is vereist om taken aan te maken) of heeft onvoldoende credits.
Name
403 - Forbidden
Description
De invoerafbeelding is gemarkeerd door moderation voor intellectueel eigendom.
Name
429 - Too Many Requests
Description
U heeft uw rate limit overschreden.
Name
500 - Internal Server Error
Description
Er is een onverwachte serverfout opgetreden — bijvoorbeeld doordat de content-moderationservice niet beschikbaar was, het klaarzetten van de invoerafbeelding mislukte, of de taak niet kon worden aangemaakt. In dit geval wordt er 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 getextureerde 3D keycap-model op basis van een geslaagde prototypetaak en een van de bijbehorende kandidaten. Eén build-taak doorloopt de volledige pijplijn van begin tot eind — het genereren van een wit model op basis van het gekozen ontwerp, automatisch plaatsen en uitsnijden op de keycap-basis met behulp van een gekalibreerde standaardpositie (geen interactieve aanpassing nodig), volledige modelkleuring, en de uiteindelijke assemblage en export. Een build duurt doorgaans 3–7 minuten, richting het bovenste einde wanneer meerdere builds gelijktijdig draaien.
Raadpleeg Het Keycap Build Task-object
voor de vorm van de respons.
Parameters
Name
input_task_id
Type
string
Verplicht
Description
De task-ID van een prototypetaak die via dezelfde OpenAPI-endpoint is aangemaakt. Het prototype moet zijn aangemaakt door hetzelfde Meshy-account, moet SUCCEEDED hebben bereikt, en moet minstens één kandidaat hebben opgeleverd.
Prototypetaken die via de webapp zijn aangemaakt, worden niet geaccepteerd — de build-endpoint accepteert alleen prototypetaken die zijn 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, afkomstig uit de candidate_ids-array van de geslaagde prototypetaak. Moet bij die taak horen; elke andere waarde wordt afgewezen met 400.
Name
name
Type
string
Description
Optionele taaknaam voor weergavedoeleinden. Maximaal 100 tekens.
options
Optionele geometrie-afstemming. Elk veld heeft een gekalibreerde standaardwaarde — stuur alleen degene die u wilt overschrijven.
Name
base_model
Type
string
standaard cherry-mx-1x1-r1
Description
De keycap-basis waarop wordt gebouwd. Momenteel is de enige beschikbare waarde
cherry-mx-1x1-r1 — een standaard Cherry MX-profiel 1u keycap. 3–5
extra mainstream standaardformaten staan gepland; aangepaste formaten worden
niet ondersteund.
Name
head_size_mm
Type
number
standaard 23
Description
Doelgrootte van de gesculptureerde kop, in millimeters: de langste afmeting ervan wordt geschaald naar deze waarde. Bereik: [10, 40]. Waarden boven ongeveer 32.9 kunnen worden verlaagd 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 momenteel niet teruggegeven op het task-object — als u de daadwerkelijk ontvangen grootte wilt bevestigen, 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 task-id van de nieuw aangemaakte keycap build-taak. Poll de Get a Task-endpoint of abonneer u op de stream totdat de taak SUCCEEDED bereikt, en download vervolgens de artefacten van model_urls.glb en model_urls.obj_zip.
Zowel de GLB als het OBJ-bundel worden geëxporteerd op werkelijke
millimeterschaal, met een Y-up-coördinatensysteem en de voorkant van
de keycap gericht op +Z.
Foutmodi
Name
400 - Bad Request
Description
Het verzoek was onaanvaardbaar. Veelvoorkomende oorzaken:
Ontbrekende parameter: input_task_id en candidate_id zijn verplicht.
Ongeldige UUID: De input_task_id is geen geldige UUID.
Ouder niet geslaagd: De verwezen prototypetaak heeft SUCCEEDED nog niet bereikt.
Geen kandidaten: De prototypetaak is geslaagd maar heeft geen kandidaten opgeleverd.
Onbekende kandidaat: candidate_id behoort niet tot de kandidaten van de invoertaak.
Opties buiten bereik: Een van de options-velden viel buiten het toegestane bereik of de toegestane enum-set.
Name
401 - Unauthorized
Description
Authenticatie mislukt. Controleer uw API-sleutel.
Name
402 - Payment Required
Description
Het account gebruikt het gratis plan (een betaald plan is vereist om taken aan te maken) of heeft onvoldoende credits.
Name
404 - Not Found
Description
De verwezen prototypetaak bestaat niet, behoort tot een andere gebruiker, of is aangemaakt via de webapp (alleen prototypetaken in API-mode kunnen doorschakelen naar build).
Name
429 - Too Many Requests
Description
U heeft uw rate limit overschreden.
Name
500 - Internal Server Error
Description
Er is een onverwachte serverzijdige fout opgetreden — bijvoorbeeld de content-moderationservice was niet beschikbaar, het klaarzetten van de invoerafbeelding mislukte, of de taak kon niet worden aangemaakt. In dit geval wordt er geen taak aangemaakt, dus het is veilig om het opnieuw te proberen.
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 build-taak op aan de hand van een geldige taak-id. Het URL-pad
moet overeenkomen met de fase van de taak — een build-taak die wordt opgehaald via
/prototype/:id retourneert 404, en omgekeerd.
Annuleer een keycap-taak. Als de taak nog PENDING is, worden de bij het
aanmaken verbruikte credits terugbetaald. Taken die al IN_PROGRESS
zijn, worden geannuleerd zonder terugbetaling (de worker 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 retourneert 404.
Padparameters
Name
id
Type
path
Description
Unieke identifier voor de te annuleren keycap-taak.
Retourneert
Retourneert 204 No Content bij succes met een lege body.
Faalmodi
Name
400 - Bad Request
Description
De taak bevindt zich al in een eindstatus en kan niet worden geannuleerd.
Name
404 - Not Found
Description
De taak bestaat niet, behoort toe aan 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 is mogelijk wel of niet geannuleerd — lees deze opnieuw om dit te bevestigen voordat u het opnieuw probeert.
Stream realtime 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 op
/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 die gestreamd moet worden.
Retourwaarden
Retourneert een stream van Keycap Prototype
of Keycap Build taakobjecten als
Server-Sent Events. Elk frame bevat het volledige taakobject voor die fase — dezelfde vorm die het
Get-endpoint retourneert — dus zolang de taak PENDING of IN_PROGRESS is, zijn de
outputvelden simpelweg nog niet ingevuld (null, [] of {}) en
is finished_atnull.
// Error event example (wrong stage or task not found)event: errordata: {"status_code": 404,"message": "Task not found"}// Message event examples illustrate task progress.// Every frame is the full task object; fields not yet populated are null / empty.// The PENDING frame below is abbreviated to the fields that change.event: messagedata: {"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af","progress": 0,"status": "PENDING"}event: messagedata: {"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af","type": "creative-lab-keycap-build","status": "SUCCEEDED","progress": 100,"created_at": 1753142600000,"started_at": 1753142610000,"finished_at": 1753143050000,"expires_at": 1753402250000,"task_error": null,"consumed_credits": 50,"model_urls": {"glb":"https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***","obj_zip":"https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***" },"process_image_urls": {"head_design":"https://assets.meshy.ai/***/head-design.png?Expires=***" }}
Haal een gepagineerde lijst op van je keycap-taken voor één fase. Het URL-
pad selecteert de fase — /prototype retourneert prototype-taken; /build
retourneert build-taken. Taken uit de andere fase worden in geen van beide
antwoorden opgenomen.
Padparameters
Name
stage
Type
path
Verplicht
Description
Ofwel prototype ofwel build. De collectie retourneert alleen taken
waarvan de fase overeenkomt met de URL — het ophalen van /prototype
retourneert nooit build-taken en omgekeerd.
Queryparameters
Name
page_num
Type
integer
standaard 1
Description
Paginanummer voor paginering.
Name
page_size
Type
integer
standaard 10
Description
Paginagrootte-limiet. Maximaal toegestaan is 100 items.
Name
sort_by
Type
string
standaard -created_at
Description
Veld om op te sorteren. Beschikbare waarden:
+created_at: Sorteer op aanmaaktijd in oplopende volgorde.
-created_at: Sorteer op aanmaaktijd in aflopende volgorde.
Het Keycap Prototype Task-object is een werkeenheid die Meshy bijhoudt om
één afbeelding van het uiteindelijke keycap-ontwerp te genereren op basis van een
bronfoto. De output van deze fase wordt gekoppeld aan
de buildfase via input_task_id plus candidate_id.
Eigenschappen
Name
id
Type
string
Description
Unieke identifier voor de taak. Hoewel we als implementatiedetail een k-sorteerbare UUID gebruiken voor taak-id's, mag je geen aannames doen 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 is opgegeven bij het aanmaken van de taak. Lege string als er geen naam is 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
Progress van de taak. Als de taak nog niet is gestart, is deze eigenschap 0. 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 geeft het aantal milliseconden weer dat is verstreken sinds 1 januari 1970 UTC, volgens
de RFC 3339-standaard.
Zo wordt vrijdag 1 september 2023 12:00:00 PM GMT weergegeven als 1693569600000. Dit geldt
voor alle tijdstempels in de Meshy API.
Name
started_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is gestart, in milliseconden. Als de taak nog niet is gestart, is deze eigenschap 0.
Name
finished_at
Type
timestamp
Description
Tijdstempel van wanneer de taak is afgerond, in milliseconden. Als de taak nog niet is afgerond, is deze eigenschap 0.
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 relevant als de taakstatus PENDING is.
Name
task_error
Type
object
Description
Foutdetails voor mislukte taken. Zie Fouten voor de volledige referentie van het task_error-object.
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 die fase in rekening gebracht. Een taak die nooit wordt aangemaakt (een 4xx op het moment van het verzoek, inclusief een afwijzing door moderation) wordt helemaal niet in rekening gebracht. Een taak die FAILED bereikt, geeft 0 terug — het bedrag wordt terugbetaald, ook bij een asynchrone blokkade door moderation. Annuleren via DELETE betaalt alleen terug zolang de taak nog PENDING is; een taak die al IN_PROGRESS is, blijft in rekening gebracht, omdat het werk al is verricht.
Name
image_urls
Type
array of strings
Description
Downloadbare URL van de render van het uiteindelijke keycap-ontwerp — hoe de kandidaat eruitziet als 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 build-endpoint gebruikt candidate_ids, niet deze URL's. Dezelfde URL-levenscyclus als model_urls: ondertekend, geen Authorization-header, geldig tot expires_at, en stabiel wanneer de taak opnieuw wordt uitgelezen.
Name
candidate_ids
Type
array of strings
Description
Ondoorzichtige kandidaat-identifiers, parallel aan image_urls. Geef het item dat overeenkomt met je gekozen ontwerp door als candidate_id van het buildverzoek. Doe 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 op basis van een geslaagde prototype-task en een
gekozen kandidaat. Eén build doorloopt de volledige pipeline — white-model-
generatie, automatisch passend maken en snijden, kleuring, assemblage en export.
Eigenschappen
Name
id
Type
string
Description
Unieke identifier voor de task.
Name
type
Type
string
Description
Type van de task. De waarde is creative-lab-keycap-build.
Name
name
Type
string
Description
De taaknaam die is opgegeven bij het aanmaken van de task. Lege string als er geen naam is opgegeven.
Name
status
Type
string
Description
Status van de task. Mogelijke waarden zijn een van PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Voortgang van de task. Als de task nog niet is gestart, is deze eigenschap 0. Zodra de task is geslaagd, wordt dit 100.
Name
created_at
Type
timestamp
Description
Tijdstempel van het moment waarop de task is aangemaakt, in milliseconden.
Name
started_at
Type
timestamp
Description
Tijdstempel van het moment waarop de task is gestart, in milliseconden.
Name
finished_at
Type
timestamp
Description
Tijdstempel van het moment waarop de task is voltooid, in milliseconden.
Name
expires_at
Type
timestamp
Description
Tijdstempel van het moment waarop het taakresultaat verloopt, in milliseconden.
Name
preceding_tasks
Type
integer
Description
Het aantal voorgaande tasks. Alleen relevant wanneer status PENDING is.
Name
task_error
Type
object
Description
Foutdetails voor mislukte tasks. Zie Fouten voor de volledige referentie van het task_error-object.
Name
consumed_credits
Type
integer
Description
Het aantal credits dat door deze task is verbruikt. Een task die SUCCEEDED bereikt, wordt volledig in rekening gebracht voor de betreffende fase. Een task die nooit wordt aangemaakt (een 4xx op het moment van de aanvraag, inclusief een afwijzing door moderation) wordt helemaal niet in rekening gebracht. Een task die FAILED bereikt, geeft 0 terug — de kosten worden terugbetaald, ook bij een asynchrone blokkering door moderation. Annuleren via DELETE betaalt de kosten alleen terug zolang de task nog PENDING is; een task die al IN_PROGRESS is, blijft in rekening gebracht, omdat het werk al is uitgevoerd.
Name
model_urls
Type
object
Description
Downloadbare URL's voor de gegenereerde modelbestanden. Zowel de GLB- als de OBJ-bundel worden geëxporteerd op schaal van echte millimeters, Y-omhoog, met de voorkant van de keycap gericht op +Z. Meshes hebben de namen 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 er niet van uit dat er precies twee meshes zijn.
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 uitlezen van de task binnen dat venster levert dezelfde URL op in plaats van een nieuw ondertekende. Download en bewaar de bestanden zelf vóór die tijd — er is geen manier om een verlopen link te vernieuwen.
Name
glb
Type
string
Description
Downloadbare URL naar de definitieve getextureerde model.glb.
Name
obj_zip
Type
string
Description
Downloadbare URL naar een zip-bundel met model.obj, model.mtl, en de textuur-PNG's waarnaar de MTL daadwerkelijk verwijst. Een egaalgekleurde basis bevat alleen keycap-head.png; een basis met een patroon bevat ook keycap-base.png.
Name
process_image_urls
Type
object
Description
Downloadbare URL's voor tussentijdse procesafbeeldingen, gesleuteld op type. Dezelfde URL-levenscyclus als model_urls: ondertekend, geen Authorization-header, geldig tot expires_at, en stabiel wanneer de task opnieuw wordt uitgelezen. Momenteel geleverde typen:
head_design — de door de build gebruikte ontwerpafbeelding van de gekozen kandidaat (altijd aanwezig).
composite — de render van de afgewerkte keycap van de gekozen kandidaat (aanwezig indien beschikbaar).
base_canvas — het beschilderde canvas van de keycap-basis (aanwezig indien beschikbaar).
Beschouw de set sleutels als open voor uitbreiding; er kunnen nieuwe typen worden toegevoegd zonder een breaking change.
De volledige flow: maak een prototype van een foto, poll deze tot
SUCCEEDED, kies een kandidaat uit candidate_ids, maak een build met
die kandidaat, poll de build tot SUCCEEDED, en download vervolgens de GLB- en
de OBJ-bundel via model_urls.
Het voorbeeld kiest programmatisch de eerste kandidaat. In een echte
integratie zou je de image_urls-vermelding aan de eindgebruiker tonen en
hen laten kiezen; de gekozen index komt 1-op-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"