Przekształć zdjęcie źródłowe w pełnokolorowy, niestandardowy klawisz mechanicznej klawiatury w dwóch etapach: prototyp generuje render projektu "gotowego klawisza" z Twojego zdjęcia wejściowego. Po potwierdzeniu tego renderu, budowa przekształca go w teksturowany model 3D klawisza w jednym przebiegu — generowanie modelu białego, automatyczne osadzanie i cięcie na skalibrowanej domyślnej pozycji, kolorowanie pełnego modelu i ostateczny montaż odbywają się w ramach jednego zadania budowy. Oba etapy są połączone za pomocą input_task_id oraz candidate_id.
POST /openapi/creative-lab/keycap/v1/prototype
POST /openapi/creative-lab/keycap/v1/build
Oba POST endpointy wymagają płatnego planu subskrypcji. Żądania z kont na darmowym planie są odrzucane z 402 Payment Required.
Wygeneruj render projektu gotowego keycapu z oryginalnego zdjęcia. Wynik zadania zawiera tablicę image_urls (render wyświetlania gotowego keycapu) oraz równoległą tablicę candidate_ids; obie zawierają pojedynczy wpis. Wywołaj ten punkt końcowy ponownie dla innego renderu, jeśli wynik nie jest tym, czego oczekujesz — każde wywołanie jest rozliczane osobno. Przekaż candidate_id razem z identyfikatorem zadania prototypu do punktu końcowego budowy. Odnieś się do Obiektu zadania prototypu Keycap dla kształtu odpowiedzi.
Parametry
Name
image_url
Type
string
Wymagane
Description
Oryginalne zdjęcie, które Meshy przekształci w obrazy projektu keycapu. Obecnie obsługujemy formaty .jpg, .jpeg, .png i .webp.
Format jest wykrywany przez dekodowanie danych obrazu, nie na podstawie rozszerzenia pliku URL — URL bez rozszerzenia lub taki, który przekierowuje, działa, o ile bajty dekodują się do obsługiwanego formatu. Przekierowania HTTP są śledzone. Orientacja EXIF jest normalizowana, więc obrócone zdjęcie z telefonu jest używane tak, jak wygląda.
Limity: co najmniej 32 piksele z każdej strony, maksymalnie 178,956,970 pikseli w sumie i maksymalnie 20,000,000 bajtów po pobraniu. Dla Data URI limit dotyczy zdekodowanych bajtów, więc sam plik źródłowy może mieć do tej wielkości — jest to tekst base64, który jest około jednej trzeciej większy, co ma znaczenie dla treści żądania, a nie dla tego limitu. Data URI musi deklarować typ zawartości image/* i ;base64.
Istnieją dwa sposoby dostarczenia obrazu:
Publicznie dostępny URL: URL, który jest dostępny z publicznego internetu.
Data URI: zakodowany w base64 Data URI obrazu. Przykład Data URI: data:image/jpeg;base64,<twoje dane obrazu zakodowane w base64>.
Name
name
Type
string
Description
Opcjonalna nazwa zadania do celów wyświetlania. Maksymalnie 100 znaków.
Name
remove_background
Type
boolean
domyślne false
Description
Gdy ustawione na true, render wyświetlania zwrócony w image_urls jest przezroczystym RGBA PNG z usuniętym tłem, dzięki czemu można go skomponować na dowolnym tle.
Dotyczy to tylko renderu wyświetlania. Kandydat konsumowany przez punkt końcowy budowy pozostaje niezmieniony, więc wynik 3D jest identyczny w obu przypadkach.
Zwraca
Właściwość result odpowiedzi zawiera identyfikator zadania id nowo utworzonego zadania prototypu keycapu. Sprawdź punkt końcowy Pobierz zadanie lub subskrybuj strumień aż zadanie osiągnie SUCCEEDED, a następnie weź wpis z candidate_ids i przekaż go, razem z identyfikatorem zadania, do punktu końcowego budowy.
Tryby awarii
Name
400 - Bad Request
Description
Żądanie było nieakceptowalne. Typowe przyczyny:
Brakujący parametr: image_url jest wymagany.
Nieprawidłowy format obrazu: Podany image_url nie jest obsługiwanym formatem (.jpg, .jpeg, .png, .webp).
Wymiary obrazu poza zakresem: Obraz jest zbyt mały, przekracza maksymalny rozmiar pliku lub przekracza maksymalną liczbę pikseli.
Nieosiągalny URL: image_url nie mógł zostać pobrany (404 lub timeout).
Nieprawidłowy Data URI: Ciąg base64 jest niepoprawny.
Treść oznaczona: Obraz wejściowy został oznaczony przez moderation NSFW.
Name
401 - Unauthorized
Description
Uwierzytelnianie nie powiodło się. Proszę sprawdzić swój klucz API.
Name
402 - Payment Required
Description
Konto jest na darmowym planie (wymagany jest płatny plan do tworzenia zadań) lub nie ma wystarczających kredytów.
Name
403 - Forbidden
Description
Obraz wejściowy został oznaczony przez moderation własności intelektualnej.
Name
429 - Too Many Requests
Description
Przekroczyłeś swój limit szybkości.
Name
500 - Internal Server Error
Description
Wystąpił nieoczekiwany błąd po stronie serwera — na przykład usługa moderation treści była niedostępna, przygotowanie obrazu wejściowego nie powiodło się lub zadanie nie mogło zostać utworzone. W takim przypadku nie jest tworzone żadne zadanie, więc ponowne próby są bezpieczne.
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>" }'
Wygeneruj ostateczny model 3D nakładki na klawisz z teksturą z zakończonego sukcesem zadania prototypowego i jednego z jego kandydatów. Pojedyncze zadanie budowy uruchamia cały proces od początku do końca — generowanie modelu białego z wybranego projektu, automatyczne osadzenie i wycięcie na podstawie nakładki przy użyciu skalibrowanej domyślnej pozy (bez potrzeby interaktywnej regulacji), kolorowanie pełnego modelu oraz ostateczny montaż i eksport. Budowa zazwyczaj trwa 3–7 minut, bliżej górnej granicy, gdy kilka budów jest uruchomionych jednocześnie. Odwołaj się do Obiekt zadania budowy nakładki na klawisz dla kształtu odpowiedzi.
Parametry
Name
input_task_id
Type
string
Wymagane
Description
Identyfikator zadania prototypowego utworzonego za pośrednictwem tego samego punktu końcowego OpenAPI. Prototyp musi być utworzony przez to samo konto Meshy, musi osiągnąć SUCCEEDED i musi wygenerować co najmniej jednego kandydata.
Zadania prototypowe utworzone za pośrednictwem aplikacji webowej nie są akceptowane — punkt końcowy budowy akceptuje tylko zadania prototypowe wygenerowane przez POST /openapi/creative-lab/keycap/v1/prototype i odrzuca każde inne źródło z 404.
Name
candidate_id
Type
string
Wymagane
Description
Kandydat do budowy, wybrany z tablicy candidate_ids zakończonego sukcesem zadania prototypowego. Musi należeć do tego zadania; każda inna wartość jest odrzucana z 400.
Name
name
Type
string
Description
Opcjonalna nazwa zadania do celów wyświetlania. Maksymalnie 100 znaków.
options
Opcjonalne dostrojenie geometrii. Każde pole ma skalibrowaną wartość domyślną — wysyłaj tylko te, które chcesz nadpisać.
Name
base_model
Type
string
domyślne cherry-mx-1x1-r1
Description
Podstawa nakładki, na której ma być zbudowana. Obecnie jedyną dostępną wartością jest cherry-mx-1x1-r1 — standardowy profil nakładki Cherry MX 1u. Planowane są 3–5 dodatkowych standardowych rozmiarów; niestandardowe rozmiary nie są obsługiwane.
Name
head_size_mm
Type
number
domyślne 23
Description
Docelowy rozmiar rzeźbionej głowy, w milimetrach: jej najdłuższy wymiar jest skalowany do tej wartości. Zakres: [10, 40]. Wartości powyżej około 32.9 mogą zostać zmniejszone, aby głowa nadal mieściła się w ograniczeniu ochronnej powierzchni podstawy, więc dostarczony najdłuższy wymiar może być mniejszy niż żądany. Zastosowana wartość nie jest obecnie zwracana w obiekcie zadania — jeśli potrzebujesz potwierdzić otrzymany rozmiar, zmierz prostopadłościan ograniczający siatki keycap-head w pobranym modelu.
Name
vertical_offset_mm
Type
number
domyślne 0
Description
Pionowe przesunięcie zastosowane do głowy przed jej osadzeniem na podstawie, w milimetrach. Zakres: [-5, 5].
Zwraca
Właściwość result odpowiedzi zawiera identyfikator zadania id nowo utworzonego zadania budowy nakładki na klawisz. Odpytywanie punktu końcowego Pobierz zadanie lub subskrybuj strumień aż zadanie osiągnie SUCCEEDED, a następnie pobierz artefakty z model_urls.glb i model_urls.obj_zip.
Zarówno pakiet GLB, jak i OBJ są eksportowane w rzeczywistej skali milimetrowej, z układem współrzędnych Y-up i przodem nakładki skierowanym na +Z.
Tryby awarii
Name
400 - Bad Request
Description
Żądanie było nieakceptowalne. Typowe przyczyny:
Brakujący parametr: input_task_id i candidate_id są wymagane.
Nieprawidłowy UUID: input_task_id nie jest prawidłowym UUID.
Rodzic nie zakończony sukcesem: Odwołane zadanie prototypowe nie osiągnęło jeszcze SUCCEEDED.
Brak kandydatów: Zadanie prototypowe zakończyło się sukcesem, ale nie wygenerowało żadnych kandydatów.
Nieznany kandydat: candidate_id nie jest jednym z kandydatów zadania wejściowego.
Opcje poza zakresem: Jedno z pól options wykraczało poza dozwolony zakres lub zestaw enum.
Name
401 - Unauthorized
Description
Uwierzytelnianie nie powiodło się. Proszę sprawdzić swój klucz API.
Name
402 - Payment Required
Description
Konto jest na darmowym planie (wymagany jest płatny plan do tworzenia zadań) lub ma niewystarczającą ilość kredytów.
Name
404 - Not Found
Description
Odwołane zadanie prototypowe nie istnieje, należy do innego użytkownika lub zostało utworzone za pośrednictwem aplikacji webowej (tylko zadania prototypowe w trybie API łączą się w budowę).
Name
429 - Too Many Requests
Description
Przekroczyłeś swój limit szybkości.
Name
500 - Internal Server Error
Description
Wystąpił nieoczekiwany błąd po stronie serwera — na przykład usługa moderacji treści była niedostępna, nie udało się przygotować obrazu wejściowego lub nie można było utworzyć zadania. W takim przypadku nie jest tworzone żadne zadanie, więc ponowienie jest bezpieczne.
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 } }'
Pobierz zadanie prototypu lub budowy, podając prawidłowy id zadania. Ścieżka URL
musi odpowiadać etapowi zadania — zadanie budowy pobrane przez
/prototype/:id zwróci 404, i odwrotnie.
Anuluj zadanie keycap. Jeśli zadanie jest nadal PENDING, kredyty
zużyte podczas tworzenia są zwracane. Zadania, które są już
IN_PROGRESS, są anulowane bez zwrotu (pracownik może już
zużywać zasoby). Zadania, które osiągnęły już stan końcowy
(SUCCEEDED, FAILED, CANCELED) nie mogą być anulowane.
Ścieżka URL musi odpowiadać etapowi zadania — DELETE na
/prototype/:buildId zwraca 404.
Parametry ścieżki
Name
id
Type
path
Description
Unikalny identyfikator zadania keycap do anulowania.
Zwraca
Zwraca 204 No Content w przypadku sukcesu z pustym ciałem.
Tryby błędów
Name
400 - Bad Request
Description
Zadanie jest już w stanie końcowym i nie może być anulowane.
Name
404 - Not Found
Description
Zadanie nie istnieje, należy do innego użytkownika lub jego etap nie odpowiada ścieżce URL.
Name
500 - Internal Server Error
Description
Wystąpił nieoczekiwany błąd po stronie serwera podczas anulowania. Zadanie mogło zostać anulowane lub nie — odczytaj je ponownie, aby potwierdzić przed ponowną próbą.
Transmituj aktualizacje w czasie rzeczywistym dla zadania keycap za pomocą Server-Sent Events (SSE).
Ścieżka URL musi odpowiadać etapowi zadania — otwarcie transmisji na
/prototype/:buildId/stream emituje pojedynczy ładunek event: error z
status_code: 404 i zamyka transmisję.
Parametry
Name
id
Type
path
Description
Unikalny identyfikator dla zadania keycap do transmisji.
Zwraca
Zwraca strumień obiektów zadań Keycap Prototype
lub Keycap Build jako Server-Sent Events. Dla zadań PENDING lub IN_PROGRESS, strumień odpowiedzi będzie zawierał tylko niezbędne pola progress i status.
Pobierz stronicowaną listę swoich zadań keycap dla jednego etapu. Ścieżka URL
wybiera etap — /prototype zwraca zadania prototypowe; /build
zwraca zadania budowlane. Zadania z innego etapu nie są uwzględniane w żadnej z odpowiedzi.
Parametry ścieżki
Name
stage
Type
path
Wymagane
Description
Albo prototype, albo build. Kolekcja zwraca tylko zadania,
których etap odpowiada ścieżce URL — pobieranie /prototype nigdy nie zwraca
zadań budowlanych i odwrotnie.
Parametry zapytania
Name
page_num
Type
integer
domyślne 1
Description
Numer strony dla stronicowania.
Name
page_size
Type
integer
domyślne 10
Description
Limit rozmiaru strony. Maksymalnie dozwolone to 100 elementów.
Name
sort_by
Type
string
domyślne -created_at
Description
Pole do sortowania. Dostępne wartości:
+created_at: Sortuj według czasu utworzenia w porządku rosnącym.
-created_at: Sortuj według czasu utworzenia w porządku malejącym.
Obiekt Zadania Prototypu Klawisza to jednostka pracy, którą Meshy śledzi, aby wygenerować jeden obraz projektu klawisza z zdjęcia źródłowego. Wynik tego etapu jest łączony z etapem budowy za pomocą input_task_id oraz candidate_id.
Właściwości
Name
id
Type
string
Description
Unikalny identyfikator zadania. Chociaż używamy k-sortowalnego UUID dla identyfikatorów zadań jako szczegółu implementacji, nie powinieneś zakładać żadnych założeń dotyczących formatu tego identyfikatora.
Name
type
Type
string
Description
Typ zadania. Wartość to creative-lab-keycap-prototype.
Name
name
Type
string
Description
Nazwa zadania podana podczas tworzenia zadania. Pusty ciąg, jeśli nie podano nazwy.
Name
status
Type
string
Description
Status zadania. Możliwe wartości to PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Postęp zadania. Jeśli zadanie jeszcze się nie rozpoczęło, ta właściwość będzie miała wartość 0. Gdy zadanie zakończy się sukcesem, wartość ta wyniesie 100.
Name
created_at
Type
timestamp
Description
Znacznik czasu utworzenia zadania, w milisekundach.
Znacznik czasu reprezentuje liczbę milisekund, które upłynęły od 1 stycznia 1970 UTC, zgodnie ze standardem RFC 3339. Na przykład, piątek, 1 września 2023, 12:00:00 GMT jest reprezentowany jako 1693569600000. Dotyczy to wszystkich znaczników czasu w Meshy API.
Name
started_at
Type
timestamp
Description
Znacznik czasu rozpoczęcia zadania, w milisekundach. Jeśli zadanie jeszcze się nie rozpoczęło, ta właściwość będzie miała wartość 0.
Name
finished_at
Type
timestamp
Description
Znacznik czasu zakończenia zadania, w milisekundach. Jeśli zadanie jeszcze się nie zakończyło, ta właściwość będzie miała wartość 0.
Name
expires_at
Type
timestamp
Description
Znacznik czasu wygaśnięcia wyniku zadania, w milisekundach.
Name
preceding_tasks
Type
integer
Description
Liczba poprzedzających zadań.
Wartość tego pola ma znaczenie tylko wtedy, gdy status zadania to PENDING.
Name
task_error
Type
object
Description
Szczegóły błędu dla nieudanych zadań. Zobacz Błędy dla pełnej referencji obiektu task_error.
Name
consumed_credits
Type
integer
Description
Liczba kredytów zużytych przez to zadanie. Zadanie, które osiąga SUCCEEDED, jest obciążane pełną kwotą za swój etap. Zadanie, które nigdy nie zostaje utworzone (błąd 4xx w czasie żądania, w tym odrzucenie moderacji) nie jest w ogóle obciążane. Zadanie, które osiąga FAILED, zwraca 0 — opłata jest zwracana, w tym asynchroniczny blok moderacji. Anulowanie za pomocą DELETE zwraca opłatę tylko wtedy, gdy zadanie jest jeszcze PENDING; zadanie już IN_PROGRESS pozostaje obciążone, ponieważ praca została wykonana.
Name
image_urls
Type
array of strings
Description
Pobieralny URL renderu projektu klawisza — jak wygląda kandydat jako gotowy klawisz. Zawiera jeden wpis; image_urls[i] odpowiada candidate_ids[i]. Pusty, dopóki zadanie nie osiągnie SUCCEEDED. URL jest tylko do wyświetlania; punkt końcowy budowy zużywa candidate_ids, a nie te URL-e. Ten sam cykl życia URL jak model_urls: podpisany, bez nagłówka Authorization, ważny do expires_at i stabilny, gdy zadanie jest ponownie odczytywane.
Name
candidate_ids
Type
array of strings
Description
Nieprzejrzyste identyfikatory kandydatów, równoległe do image_urls. Przekaż wpis odpowiadający wybranemu projektowi jako candidate_id w żądaniu budowy. Nie zakładaj żadnych założeń dotyczących formatu tych identyfikatorów.
Obiekt Zadania Budowy Keycap jest jednostką pracy, którą Meshy śledzi, aby wygenerować ostateczny teksturowany trójwymiarowy keycap z zakończonego sukcesem zadania prototypowego i wybranego kandydata. Pojedyncza budowa uruchamia pełny proces — generowanie modelu białego, automatyczne osadzanie i cięcie, kolorowanie, montaż i eksport.
Właściwości
Name
id
Type
string
Description
Unikalny identyfikator zadania.
Name
type
Type
string
Description
Typ zadania. Wartość to creative-lab-keycap-build.
Name
name
Type
string
Description
Nazwa zadania podana podczas tworzenia zadania. Pusty ciąg znaków, jeśli nie podano nazwy.
Name
status
Type
string
Description
Status zadania. Możliwe wartości to PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Postęp zadania. Jeśli zadanie jeszcze się nie rozpoczęło, ta właściwość będzie miała wartość 0. Po zakończeniu zadania sukcesem, wartość ta wyniesie 100.
Name
created_at
Type
timestamp
Description
Znacznik czasu utworzenia zadania, w milisekundach.
Name
started_at
Type
timestamp
Description
Znacznik czasu rozpoczęcia zadania, w milisekundach.
Name
finished_at
Type
timestamp
Description
Znacznik czasu zakończenia zadania, w milisekundach.
Name
expires_at
Type
timestamp
Description
Znacznik czasu wygaśnięcia wyniku zadania, w milisekundach.
Name
preceding_tasks
Type
integer
Description
Liczba poprzedzających zadań. Znaczące tylko, gdy status to PENDING.
Name
task_error
Type
object
Description
Szczegóły błędu dla nieudanych zadań. Zobacz Błędy dla pełnej referencji obiektu task_error.
Name
consumed_credits
Type
integer
Description
Liczba kredytów zużytych przez to zadanie. Zadanie, które osiąga SUCCEEDED, jest obciążane pełną kwotą za swój etap. Zadanie, które nigdy nie zostaje utworzone (błąd 4xx w czasie żądania, w tym odrzucenie moderacji) nie jest w ogóle obciążane. Zadanie, które osiąga FAILED, zwraca 0 — opłata jest zwracana, w tym asynchroniczna blokada moderacji. Anulowanie za pomocą DELETE zwraca opłatę tylko wtedy, gdy zadanie jest nadal PENDING; zadanie już IN_PROGRESS pozostaje obciążone, ponieważ praca została wykonana.
Name
model_urls
Type
object
Description
Pobieralne URL-e dla wygenerowanych artefaktów modelu. Zarówno pakiet GLB, jak i OBJ są eksportowane w rzeczywistej skali milimetrowej, Y-up, z przodem keycapu skierowanym na +Z. Siatki są nazwane keycap-head i keycap-base; gdy podstawa wraca do wypełnienia wzorem, obecna jest również trzecia siatka keycap-base-interior dla wnęki trzonu. Nie zakładaj dokładnie dwóch siatek.
Są to podpisane URL-e: pobieraj je bez nagłówka Authorization. Pozostają ważne do expires_at, co jest 3 dni po finished_at, a ponowne odczytanie zadania w tym oknie zwraca identyczny URL zamiast nowo podpisanego. Pobierz i przechowaj pliki samodzielnie przed tym czasem — nie ma możliwości odświeżenia wygasłego linku.
Name
glb
Type
string
Description
Pobieralny URL do ostatecznego teksturowanego model.glb.
Name
obj_zip
Type
string
Description
Pobieralny URL do pakietu zip zawierającego model.obj, model.mtl i tekstury PNG, do których odnosi się jego MTL. Podstawa w jednolitym kolorze zawiera tylko keycap-head.png; podstawa wzorzysta zawiera również keycap-base.png.
Name
process_image_urls
Type
object
Description
Pobieralne URL-e dla pośrednich obrazów procesu, kluczowane według rodzaju. Taki sam cykl życia URL-i jak model_urls: podpisane, bez nagłówka Authorization, ważne do expires_at, i stabilne przy ponownym odczycie zadania. Obecnie emitowane rodzaje:
head_design — obraz projektu wybranego kandydata, który budowa zużyła (zawsze obecny).
Kompletny przepływ: utwórz prototyp ze zdjęcia, sprawdź jego status do
SUCCEEDED, wybierz kandydata z candidate_ids, utwórz build z
tym kandydatem, sprawdź status build do SUCCEEDED, a następnie pobierz pakiet GLB i
OBJ z model_urls.
Przykład wybiera pierwszego kandydata programowo. W rzeczywistej
integracji wyświetlisz wpis image_urls użytkownikowi końcowemu i
pozwolisz mu wybrać; wybrany indeks mapuje się 1:1 na candidate_ids.
Kompletny przepływ
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"