Transformez une photo source en une touche de clavier mécanique personnalisée en couleur
en deux étapes : prototype génère un rendu de conception de "touche finie"
à partir de votre photo d'entrée. Une fois que vous avez confirmé ce rendu, build le transforme en un
modèle 3D de touche texturée en une seule exécution — génération de modèle blanc, positionnement et découpe automatiques sur une pose par défaut calibrée, coloration complète du modèle, et
assemblage final se produisent tous dans une seule tâche de construction. Les deux étapes sont liées
via input_task_id plus candidate_id.
POST /openapi/creative-lab/keycap/v1/prototype
POST /openapi/creative-lab/keycap/v1/build
Les deux endpoints POST nécessitent un plan d'abonnement payant. Les requêtes provenant
de comptes avec plan gratuit sont rejetées avec 402 Payment Required.
Générer un rendu de design de keycap fini à partir de la photo source. Le résultat de la tâche contient un tableau image_urls (le rendu d'affichage du keycap fini) et un tableau parallèle candidate_ids; les deux contiennent une seule entrée. Appelez à nouveau ce point de terminaison pour un autre rendu si le résultat ne vous convient pas — chaque appel est facturé séparément. Passez le candidate_id avec l'ID de la tâche prototype au point de terminaison de construction. Référez-vous à L'Objet de Tâche Prototype de Keycap pour la forme de la réponse.
Paramètres
Name
image_url
Type
string
Requis
Description
Photo source pour que Meshy la transforme en images de design de keycap. Nous supportons actuellement les formats .jpg, .jpeg, .png, et .webp.
Le format est détecté en décodant les données de l'image, pas à partir de l'extension de fichier de l'URL — une URL sans extension, ou une qui redirige, fonctionne tant que les octets se décodent dans un format supporté. Les redirections HTTP sont suivies. L'orientation EXIF est normalisée, donc une photo de téléphone tournée est utilisée telle qu'elle apparaît.
Limites : au moins 32 pixels de chaque côté, au maximum 178,956,970 pixels au total, et au maximum 20,000,000 octets une fois téléchargée. Pour un Data URI, la limite s'applique aux octets décodés, donc le fichier source lui-même peut atteindre cette taille — c'est le texte base64 qui est environ un tiers plus grand, ce qui compte pour votre corps de requête, pas pour cette limite. Un Data URI doit déclarer un type de contenu image/* et ;base64.
Il y a deux façons de fournir l'image :
URL accessible publiquement : Une URL accessible depuis l'internet public.
Data URI : Un Data URI encodé en base64 de l'image. Exemple de Data URI : data:image/jpeg;base64,<vos données d'image encodées en base64>.
Name
name
Type
string
Description
Nom de tâche optionnel à des fins d'affichage. Maximum 100 caractères.
Name
remove_background
Type
boolean
défaut false
Description
Lorsqu'il est réglé sur true, le rendu d'affichage retourné dans image_urls est un PNG RGBA transparent avec le fond supprimé, vous pouvez donc le composer sur n'importe quel fond.
Cela s'applique uniquement au rendu d'affichage. Le candidat que le point de terminaison de construction consomme n'est pas affecté, donc le résultat 3D est identique dans les deux cas.
Retours
La propriété result de la réponse contient l'id de la tâche de prototype de keycap nouvellement créée. Interrogez le point de terminaison Obtenir une Tâche ou abonnez-vous au flux jusqu'à ce que la tâche atteigne SUCCEEDED, puis prenez l'entrée de candidate_ids et passez-la, avec l'ID de la tâche, au point de terminaison de construction.
Modes d'Échec
Name
400 - Bad Request
Description
La requête était inacceptable. Causes courantes :
Paramètre manquant : image_url est requis.
Format d'image invalide : Le image_url fourni n'est pas un format supporté (.jpg, .jpeg, .png, .webp).
Dimensions de l'image hors limites : L'image est trop petite, dépasse la taille de fichier maximale, ou dépasse le nombre maximal de pixels.
URL inaccessible : Le image_url n'a pas pu être téléchargé (404 ou timeout).
Data URI invalide : La chaîne base64 est mal formée.
Contenu signalé : L'image d'entrée a été signalée par la modération NSFW.
Name
401 - Unauthorized
Description
L'authentification a échoué. Veuillez vérifier votre clé API.
Name
402 - Payment Required
Description
Le compte est sur le plan gratuit (un plan payant est requis pour créer des tâches) ou n'a pas suffisamment de crédits.
Name
403 - Forbidden
Description
L'image d'entrée a été signalée par la modération de propriété intellectuelle.
Name
429 - Too Many Requests
Description
Vous avez dépassé votre limite de débit.
Name
500 - Internal Server Error
Description
Une erreur inattendue côté serveur s'est produite — par exemple, le service de modération de contenu était indisponible, la mise en scène de l'image d'entrée a échoué, ou la tâche n'a pas pu être créée. Aucune tâche n'est créée dans ce cas, donc réessayer est sans risque.
Requête
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>" }'
Générer le modèle 3D final avec texture de la touche à partir d'une tâche prototype réussie et de l'un de ses candidats. Une seule tâche de construction exécute l'ensemble du pipeline de bout en bout — génération de modèle blanc à partir du design choisi, positionnement et découpe automatiques sur la base de la touche à l'aide d'une pose par défaut calibrée (aucun ajustement interactif nécessaire), coloration complète du modèle, et assemblage final et exportation. Une construction prend généralement 3 à 7 minutes, vers la limite supérieure lorsque plusieurs constructions sont exécutées simultanément.
Référez-vous à L'Objet Tâche de Construction de Keycap pour la forme de la réponse.
Paramètres
Name
input_task_id
Type
string
Requis
Description
L'ID de la tâche prototype créée via ce même point de terminaison OpenAPI. Le prototype doit avoir été créé par le même compte Meshy, doit avoir atteint SUCCEEDED, et doit avoir produit au moins un candidat.
Les tâches prototype créées via l'application web ne sont pas acceptées — le point de terminaison de construction accepte uniquement les tâches prototype produites par POST /openapi/creative-lab/keycap/v1/prototype et refuse toute autre source avec 404.
Name
candidate_id
Type
string
Requis
Description
Le candidat à construire, pris dans le tableau candidate_ids de la tâche prototype réussie. Doit appartenir à cette tâche ; toute autre valeur est rejetée avec 400.
Name
name
Type
string
Description
Nom de tâche optionnel à des fins d'affichage. Maximum 100 caractères.
options
Réglage optionnel de la géométrie. Chaque champ a une valeur par défaut calibrée — envoyez uniquement ceux que vous souhaitez remplacer.
Name
base_model
Type
string
défaut cherry-mx-1x1-r1
Description
La base de la touche à construire. Actuellement, la seule valeur disponible est cherry-mx-1x1-r1 — une touche standard de profil Cherry MX 1u. 3 à 5 tailles standard grand public supplémentaires sont prévues ; les tailles personnalisées ne sont pas prises en charge.
Name
head_size_mm
Type
number
défaut 23
Description
Taille cible de la tête sculptée, en millimètres : sa plus grande dimension est mise à l'échelle à cette valeur. Plage : [10, 40]. Les valeurs supérieures à environ 32.9 peuvent être réduites pour que la tête s'adapte toujours à la limite de l'empreinte de protection de la base, de sorte que la plus grande dimension livrée peut être inférieure à celle demandée. La valeur appliquée n'est pas renvoyée aujourd'hui sur l'objet tâche — si vous devez confirmer la taille que vous avez réellement reçue, mesurez la boîte englobante du maillage keycap-head dans le modèle téléchargé.
Name
vertical_offset_mm
Type
number
défaut 0
Description
Décalage vertical appliqué à la tête avant qu'elle ne soit placée sur la base, en millimètres. Plage : [-5, 5].
Retours
La propriété result de la réponse contient l'id de la tâche de construction de keycap nouvellement créée. Interrogez le point de terminaison Obtenir une Tâche ou abonnez-vous au flux jusqu'à ce que la tâche atteigne SUCCEEDED, puis téléchargez les artefacts depuis model_urls.glb et model_urls.obj_zip.
Le GLB et le paquet OBJ sont tous deux exportés à l'échelle réelle en millimètres, avec un système de coordonnées Y-up et l'avant de la touche orienté vers +Z.
Modes d'Échec
Name
400 - Bad Request
Description
La requête était inacceptable. Causes courantes :
Paramètre manquant : input_task_id et candidate_id sont requis.
UUID invalide : Le input_task_id n'est pas un UUID valide.
Parent non réussi : La tâche prototype référencée n'a pas encore atteint SUCCEEDED.
Aucun candidat : La tâche prototype a réussi mais n'a produit aucun candidat.
Candidat inconnu : candidate_id n'est pas l'un des candidats de la tâche d'entrée.
Options hors plage : L'un des champs options est tombé en dehors de sa plage autorisée ou de son ensemble d'énumération.
Name
401 - Unauthorized
Description
L'authentification a échoué. Veuillez vérifier votre clé API.
Name
402 - Payment Required
Description
Le compte est sur le plan gratuit (un plan payant est requis pour créer des tâches) ou n'a pas suffisamment de crédits.
Name
404 - Not Found
Description
La tâche prototype référencée n'existe pas, appartient à un utilisateur différent, ou a été créée via l'application web (seules les tâches prototype en mode API s'enchaînent dans la construction).
Name
429 - Too Many Requests
Description
Vous avez dépassé votre limite de débit.
Name
500 - Internal Server Error
Description
Une erreur inattendue côté serveur s'est produite — par exemple, le service de modération de contenu était indisponible, la mise en scène de l'image d'entrée a échoué, ou la tâche n'a pas pu être créée. Aucune tâche n'est créée dans ce cas, donc réessayer est sans danger.
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 } }'
Récupérer une tâche de prototype ou de construction donnée avec un id de tâche valide. Le chemin de l'URL
doit correspondre à l'étape de la tâche — une tâche de construction récupérée via
/prototype/:id renvoie 404, et vice versa.
Annuler une tâche de keycap. Si la tâche est encore PENDING, les crédits
consommés lors de la création sont remboursés. Les tâches qui sont déjà
IN_PROGRESS sont annulées sans remboursement (le travailleur peut déjà être
en train de consommer des ressources). Les tâches qui ont déjà atteint un état terminal
(SUCCEEDED, FAILED, CANCELED) ne peuvent pas être annulées.
Le chemin URL doit correspondre à l'étape de la tâche — DELETE sur
/prototype/:buildId renvoie 404.
Paramètres de Chemin
Name
id
Type
path
Description
Identifiant unique pour la tâche de keycap à annuler.
Retours
Retourne 204 No Content en cas de succès avec un corps vide.
Modes d'Échec
Name
400 - Bad Request
Description
La tâche est déjà dans un état terminal et ne peut pas être annulée.
Name
404 - Not Found
Description
La tâche n'existe pas, appartient à un autre utilisateur, ou son étape ne correspond pas au chemin URL.
Name
500 - Internal Server Error
Description
Une erreur inattendue côté serveur s'est produite lors de l'annulation. La tâche peut ou non avoir été annulée — relisez-la pour confirmer avant de réessayer.
Diffusez des mises à jour en temps réel pour une tâche keycap via Server-Sent Events (SSE).
Le chemin URL doit correspondre à l'étape de la tâche — ouvrir un flux à
/prototype/:buildId/stream émet une seule charge utile event: error avec
status_code: 404 et ferme le flux.
Paramètres
Name
id
Type
path
Description
Identifiant unique pour la tâche keycap à diffuser.
Retours
Retourne un flux d'objets de tâche Keycap Prototype
ou Keycap Build en tant que Server-Sent Events. Pour les tâches PENDING ou IN_PROGRESS, le flux de réponse inclura uniquement les champs nécessaires progress et status.
Récupérez une liste paginée de vos tâches keycap pour une seule étape. Le chemin URL
sélectionne l'étape — /prototype renvoie les tâches de prototype ; /build
renvoie les tâches de construction. Les tâches de l'autre étape ne sont pas incluses dans l'une ou l'autre réponse.
Paramètres de chemin
Name
stage
Type
path
Requis
Description
Soit prototype soit build. La collection renvoie uniquement les tâches
dont l'étape correspond à l'URL — récupérer /prototype ne renvoie jamais
de tâches de construction et vice versa.
Paramètres de requête
Name
page_num
Type
integer
défaut 1
Description
Numéro de page pour la pagination.
Name
page_size
Type
integer
défaut 10
Description
Limite de taille de page. Le maximum autorisé est de 100 éléments.
Name
sort_by
Type
string
défaut -created_at
Description
Champ par lequel trier. Valeurs disponibles :
+created_at: Trier par heure de création par ordre croissant.
-created_at: Trier par heure de création par ordre décroissant.
L'objet Tâche Prototype de Keycap est une unité de travail que Meshy suit pour générer une image de conception de keycap finie à partir d'une photo source. La sortie de cette étape est enchaînée dans l'étape de construction via input_task_id plus candidate_id.
Propriétés
Name
id
Type
string
Description
Identifiant unique pour la tâche. Bien que nous utilisions un UUID triable par k pour les identifiants de tâche comme détail d'implémentation, vous ne devez pas faire d'hypothèses sur le format de l'id.
Name
type
Type
string
Description
Type de la tâche. La valeur est creative-lab-keycap-prototype.
Name
name
Type
string
Description
Le nom de la tâche fourni lors de la création de la tâche. Chaîne vide si aucun nom n'a été fourni.
Name
status
Type
string
Description
Statut de la tâche. Les valeurs possibles sont PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Progression de la tâche. Si la tâche n'est pas encore commencée, cette propriété sera 0. Une fois la tâche réussie, cela deviendra 100.
Name
created_at
Type
timestamp
Description
Horodatage de la création de la tâche, en millisecondes.
Un horodatage représente le nombre de millisecondes écoulées depuis le 1er janvier 1970 UTC, suivant
la norme RFC 3339.
Par exemple, vendredi 1er septembre 2023 à 12:00:00 GMT est représenté par 1693569600000. Cela s'applique
à tous les horodatages dans Meshy API.
Name
started_at
Type
timestamp
Description
Horodatage du début de la tâche, en millisecondes. Si la tâche n'est pas encore commencée, cette propriété sera 0.
Name
finished_at
Type
timestamp
Description
Horodatage de la fin de la tâche, en millisecondes. Si la tâche n'est pas encore terminée, cette propriété sera 0.
Name
expires_at
Type
timestamp
Description
Horodatage de l'expiration du résultat de la tâche, en millisecondes.
Name
preceding_tasks
Type
integer
Description
Le nombre de tâches précédentes.
La valeur de ce champ est significative uniquement si le statut de la tâche est PENDING.
Name
task_error
Type
object
Description
Détails des erreurs pour les tâches échouées. Voir Erreurs pour la référence complète de l'objet task_error.
Name
consumed_credits
Type
integer
Description
Le nombre de crédits consommés par cette tâche. Une tâche qui atteint SUCCEEDED est facturée du montant total pour son étape. Une tâche qui n'est jamais créée (un 4xx au moment de la demande, y compris un rejet de modération) n'est pas facturée du tout. Une tâche qui atteint FAILED retourne 0 — le montant est remboursé, y compris un blocage de modération asynchrone. L'annulation via DELETE est remboursée uniquement tant que la tâche est encore PENDING; une tâche déjà IN_PROGRESS reste facturée, car le travail a été effectué.
Name
image_urls
Type
array of strings
Description
URL téléchargeable du rendu de conception de keycap fini — à quoi ressemble le candidat en tant que keycap fini. Contient une seule entrée; image_urls[i] correspond à candidate_ids[i]. Vide jusqu'à ce que la tâche atteigne SUCCEEDED. L'URL est uniquement pour l'affichage; le point de terminaison de construction consomme candidate_ids, pas ces URL. Même cycle de vie d'URL que model_urls: signé, sans en-tête Authorization, valide jusqu'à expires_at, et stable lorsque la tâche est relue.
Name
candidate_ids
Type
array of strings
Description
Identifiants de candidats opaques, parallèles à image_urls. Passez l'entrée correspondant à votre conception choisie comme candidate_id de la demande de construction. Ne faites aucune hypothèse sur le format de ces identifiants.
L'objet Tâche de Construction de Keycap est une unité de travail que Meshy suit pour générer le keycap 3D texturé final à partir d'une tâche prototype réussie et d'un candidat choisi. Une seule construction exécute le pipeline complet — génération de modèle blanc, placement et découpe automatiques, coloration, assemblage et exportation.
Propriétés
Name
id
Type
string
Description
Identifiant unique pour la tâche.
Name
type
Type
string
Description
Type de la tâche. La valeur est creative-lab-keycap-build.
Name
name
Type
string
Description
Le nom de la tâche fourni lors de la création de la tâche. Chaîne vide si aucun nom n'a été fourni.
Name
status
Type
string
Description
Statut de la tâche. Les valeurs possibles sont l'une de PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Avancement de la tâche. Si la tâche n'est pas encore commencée, cette propriété sera 0. Une fois la tâche réussie, cela deviendra 100.
Name
created_at
Type
timestamp
Description
Horodatage de la création de la tâche, en millisecondes.
Name
started_at
Type
timestamp
Description
Horodatage du début de la tâche, en millisecondes.
Name
finished_at
Type
timestamp
Description
Horodatage de la fin de la tâche, en millisecondes.
Name
expires_at
Type
timestamp
Description
Horodatage de l'expiration du résultat de la tâche, en millisecondes.
Name
preceding_tasks
Type
integer
Description
Le nombre de tâches précédentes. Significatif uniquement lorsque le statut est PENDING.
Name
task_error
Type
object
Description
Détails des erreurs pour les tâches échouées. Voir Erreurs pour la référence complète de l'objet task_error.
Name
consumed_credits
Type
integer
Description
Le nombre de crédits consommés par cette tâche. Une tâche qui atteint SUCCEEDED est facturée du montant total pour son étape. Une tâche qui n'est jamais créée (un 4xx au moment de la demande, y compris un rejet de modération) n'est pas facturée du tout. Une tâche qui atteint FAILED retourne 0 — la charge est remboursée, y compris un blocage de modération asynchrone. L'annulation via DELETE rembourse uniquement tant que la tâche est encore PENDING; une tâche déjà IN_PROGRESS reste facturée, car le travail a été dépensé.
Name
model_urls
Type
object
Description
URLs téléchargeables pour les artefacts de modèle générés. Les deux, le GLB et le bundle OBJ, sont exportés à l'échelle réelle en millimètres, Y-up, avec l'avant du keycap face au +Z. Les maillages sont nommés keycap-head et keycap-base; lorsque la base revient à un remplissage de motif, un troisième maillage keycap-base-interior est également présent pour la cavité de la tige. Ne supposez pas exactement deux maillages.
Ce sont des URLs signées : récupérez-les sans en-tête Authorization. Elles restent valides jusqu'à expires_at, qui est 3 jours après finished_at, et relire la tâche dans cette fenêtre retourne l'URL identique plutôt qu'une nouvelle signée. Téléchargez et stockez les fichiers vous-même avant cela — il n'y a aucun moyen de rafraîchir un lien expiré.
Name
glb
Type
string
Description
URL téléchargeable vers le model.glb texturé final.
Name
obj_zip
Type
string
Description
URL téléchargeable vers un bundle zip contenant model.obj, model.mtl, et les PNGs de texture que son MTL référence réellement. Une base de couleur unie expédie uniquement keycap-head.png; une base à motifs expédie également keycap-base.png.
Name
process_image_urls
Type
object
Description
URLs téléchargeables pour les images de processus intermédiaires, indexées par type. Même cycle de vie d'URL que model_urls: signées, sans en-tête Authorization, valides jusqu'à expires_at, et stables lorsque la tâche est relue. Types actuellement émis :
head_design — l'image de conception du candidat choisi que la construction a consommée (toujours présente).
composite — le rendu d'affichage du keycap fini du candidat choisi (présent lorsque disponible).
base_canvas — la toile de base du keycap peinte (présente lorsque disponible).
Considérez l'ensemble des clés comme ouvert ; de nouveaux types peuvent être ajoutés sans changement majeur.
Le flux complet : créer un prototype à partir d'une photo, le sonder jusqu'à
SUCCEEDED, choisir un candidat parmi candidate_ids, créer une construction avec
ce candidat, sonder la construction jusqu'à SUCCEEDED, puis télécharger le GLB et
le paquet OBJ depuis model_urls.
L'exemple choisit le premier candidat de manière programmatique. Dans une véritable
intégration, vous afficheriez l'entrée image_urls à l'utilisateur final et
le laisseriez choisir ; l'index choisi correspond 1:1 à candidate_ids.
Flux complet
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"