Transforme uma foto de origem numa tecla de teclado mecânico personalizada a cores
em duas etapas: protótipo gera uma renderização de design de "tecla finalizada"
a partir da sua foto de entrada. Uma vez confirmado esse render, construir transforma-o num
modelo 3D de tecla texturizado numa única execução — geração de modelo branco, posicionamento e corte automáticos numa pose padrão calibrada, coloração do modelo completo e
montagem final, tudo acontece dentro de uma única tarefa de construção. As duas etapas estão ligadas
via input_task_id mais candidate_id.
POST /openapi/creative-lab/keycap/v1/prototype
POST /openapi/creative-lab/keycap/v1/build
Ambos os endpoints POST requerem um plano de subscrição pago. Pedidos de
contas de plano gratuito são rejeitados com 402 Payment Required.
Gere uma renderização de design de keycap finalizada a partir da foto de origem. O resultado da tarefa contém um array image_urls (a renderização de exibição do keycap finalizado) e um array paralelo candidate_ids; ambos contêm uma única entrada. Chame este endpoint novamente para outra renderização se o resultado não for o que deseja — cada chamada é cobrada separadamente. Passe o candidate_id juntamente com o ID da tarefa de protótipo para o endpoint de construção. Consulte
O Objeto de Tarefa de Protótipo de Keycap
para o formato da resposta.
Parâmetros
Name
image_url
Type
string
Obrigatório
Description
Foto de origem para a Meshy transformar em imagens de design de keycap. Atualmente suportamos os formatos .jpg, .jpeg, .png e .webp.
O formato é detetado ao decodificar os dados da imagem, não a partir da extensão do ficheiro no URL — um URL sem extensão, ou um que redireciona, funciona desde que os bytes sejam decodificados para um formato suportado. Redirecionamentos HTTP são seguidos. A orientação EXIF é normalizada, portanto, uma foto de telemóvel rodada é usada da forma como aparece.
Limites: pelo menos 32 pixels de cada lado, no máximo 178,956,970 pixels no total, e no máximo 20,000,000 bytes uma vez descarregados. Para um Data URI, o limite aplica-se aos bytes decodificados, portanto, o ficheiro de origem pode ter até esse tamanho — é o texto base64 que é cerca de um terço maior, o que importa para o corpo do seu pedido, não para este limite. Um Data URI deve declarar um tipo de conteúdo image/* e ;base64.
Existem duas maneiras de fornecer a imagem:
URL publicamente acessível: Um URL acessível a partir da internet pública.
Data URI: Um Data URI codificado em base64 da imagem. Exemplo de um Data URI: data:image/jpeg;base64,<your base64-encoded image data>.
Name
name
Type
string
Description
Nome de tarefa opcional para fins de exibição. Máximo de 100 caracteres.
Name
remove_background
Type
boolean
predefinição false
Description
Quando definido como true, a renderização de exibição retornada em image_urls é um PNG RGBA transparente com o fundo removido, para que possa compô-lo em qualquer fundo.
Isto aplica-se apenas à renderização de exibição. O candidato que o endpoint de construção consome não é afetado, portanto, o resultado 3D é idêntico de qualquer maneira.
Retornos
A propriedade result da resposta contém o id da tarefa da nova tarefa de protótipo de keycap criada. Consulte o endpoint Obter uma Tarefa ou subscreva o stream até que a tarefa atinja SUCCEEDED, depois pegue a entrada de candidate_ids e passe-a, juntamente com o ID da tarefa, para o endpoint de construção.
Modos de Falha
Name
400 - Bad Request
Description
O pedido foi inaceitável. Causas comuns:
Parâmetro em falta: image_url é obrigatório.
Formato de imagem inválido: O image_url fornecido não é um formato suportado (.jpg, .jpeg, .png, .webp).
Dimensões da imagem fora do intervalo: A imagem é muito pequena, excede o tamanho máximo do ficheiro ou excede o número máximo de pixels.
URL inacessível: O image_url não pôde ser descarregado (404 ou timeout).
Data URI inválido: A string base64 está malformada.
Conteúdo sinalizado: A imagem de entrada foi sinalizada pela moderação NSFW.
Name
401 - Unauthorized
Description
A autenticação falhou. Por favor, verifique a sua chave de API.
Name
402 - Payment Required
Description
A conta está no plano gratuito (é necessário um plano pago para criar tarefas) ou não tem créditos suficientes.
Name
403 - Forbidden
Description
A imagem de entrada foi sinalizada pela moderação de propriedade intelectual.
Name
429 - Too Many Requests
Description
Excedeu o seu limite de taxa.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado do lado do servidor — por exemplo, o serviço de moderação de conteúdo estava indisponível, a preparação da imagem de entrada falhou, ou a tarefa não pôde ser criada. Nenhuma tarefa é criada neste caso, portanto, tentar novamente é seguro.
Pedido
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>" }'
Gere o modelo 3D final texturizado de um keycap a partir de uma tarefa de protótipo bem-sucedida e um dos seus candidatos. Uma única tarefa de construção executa todo o pipeline de ponta a ponta — geração de modelo branco a partir do design escolhido, posicionamento e corte automáticos na base do keycap usando uma pose padrão calibrada (sem necessidade de ajuste interativo), coloração do modelo completo, e montagem e exportação finais. Uma construção normalmente demora 3–7 minutos, tendendo para o limite superior quando várias construções são executadas simultaneamente.
Consulte O Objeto de Tarefa de Construção de Keycap para a forma da resposta.
Parâmetros
Name
input_task_id
Type
string
Obrigatório
Description
O ID da tarefa de um protótipo criado através deste mesmo endpoint OpenAPI. O protótipo deve ter sido criado pela mesma conta Meshy, deve ter alcançado SUCCEEDED, e deve ter produzido pelo menos um candidato.
Tarefas de protótipo criadas através da aplicação web não são aceites — o endpoint de construção aceita apenas tarefas de protótipo produzidas por POST /openapi/creative-lab/keycap/v1/prototype e recusa qualquer outra fonte com 404.
Name
candidate_id
Type
string
Obrigatório
Description
O candidato a construir, retirado do array candidate_ids da tarefa de protótipo bem-sucedida. Deve pertencer a essa tarefa; qualquer outro valor é rejeitado com 400.
Name
name
Type
string
Description
Nome opcional da tarefa para fins de exibição. Máximo de 100 caracteres.
options
Ajuste opcional de geometria. Cada campo tem um padrão calibrado — envie apenas os que deseja substituir.
Name
base_model
Type
string
predefinição cherry-mx-1x1-r1
Description
A base do keycap a ser construída. Atualmente, o único valor disponível é cherry-mx-1x1-r1 — um keycap padrão de perfil Cherry MX 1u. Estão planeados 3–5 tamanhos padrão adicionais; tamanhos personalizados não são suportados.
Name
head_size_mm
Type
number
predefinição 23
Description
Tamanho alvo da cabeça esculpida, em milímetros: a sua dimensão mais longa é escalada para este valor. Intervalo: [10, 40]. Valores acima de aproximadamente 32.9 podem ser reduzidos para que a cabeça ainda caiba no limite de proteção da base, portanto, a dimensão mais longa entregue pode ser menor do que a solicitada. O valor aplicado não é refletido de volta no objeto da tarefa hoje — se precisar confirmar o tamanho que realmente recebeu, meça a caixa delimitadora da malha keycap-head no modelo baixado.
Name
vertical_offset_mm
Type
number
predefinição 0
Description
Deslocamento vertical aplicado à cabeça antes de ser assentada na base, em milímetros. Intervalo: [-5, 5].
Retornos
A propriedade result da resposta contém o id da tarefa da nova tarefa de construção de keycap criada. Consulte o endpoint Obter uma Tarefa ou subscreva o stream até que a tarefa atinja SUCCEEDED, depois baixe os artefatos de model_urls.glb e model_urls.obj_zip.
Tanto o pacote GLB quanto o OBJ são exportados na escala real em milímetros, com um sistema de coordenadas Y-up e a frente do keycap voltada para +Z.
Modos de Falha
Name
400 - Bad Request
Description
O pedido foi inaceitável. Causas comuns:
Parâmetro em falta: input_task_id e candidate_id são obrigatórios.
UUID inválido: O input_task_id não é um UUID válido.
Pai não bem-sucedido: A tarefa de protótipo referenciada ainda não atingiu SUCCEEDED.
Sem candidatos: A tarefa de protótipo foi bem-sucedida, mas não produziu candidatos.
Candidato desconhecido: candidate_id não é um dos candidatos da tarefa de entrada.
Opções fora do intervalo: Um dos campos options caiu fora do intervalo permitido ou conjunto de enumeração.
Name
401 - Unauthorized
Description
A autenticação falhou. Por favor, verifique a sua chave de API.
Name
402 - Payment Required
Description
A conta está no plano gratuito (é necessário um plano pago para criar tarefas) ou não tem créditos suficientes.
Name
404 - Not Found
Description
A tarefa de protótipo referenciada não existe, pertence a um utilizador diferente, ou foi criada através da aplicação web (apenas tarefas de protótipo em modo API encadeiam em construção).
Name
429 - Too Many Requests
Description
Excedeu o seu limite de taxa.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado no lado do servidor — por exemplo, o serviço de moderação de conteúdo estava indisponível, a preparação da imagem de entrada falhou, ou a tarefa não pôde ser criada. Nenhuma tarefa é criada neste caso, portanto, tentar novamente é seguro.
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 } }'
Recupere uma tarefa de protótipo ou construção dado um id de tarefa válido. O caminho do URL
deve corresponder à fase da tarefa — uma tarefa de construção obtida através de
/prototype/:id retorna 404, e vice-versa.
Cancelar uma tarefa de keycap. Se a tarefa ainda estiver PENDING, os créditos
consumidos no momento da criação são reembolsados. Tarefas que já estão
IN_PROGRESS são canceladas sem reembolso (o trabalhador pode já estar a
consumir recursos). Tarefas que já atingiram um estado terminal
(SUCCEEDED, FAILED, CANCELED) não podem ser canceladas.
O caminho do URL deve corresponder à fase da tarefa — DELETE em
/prototype/:buildId retorna 404.
Parâmetros do Caminho
Name
id
Type
path
Description
Identificador único para a tarefa de keycap a cancelar.
Retornos
Retorna 204 No Content em caso de sucesso com um corpo vazio.
Modos de Falha
Name
400 - Bad Request
Description
A tarefa já está num estado terminal e não pode ser cancelada.
Name
404 - Not Found
Description
A tarefa não existe, pertence a um utilizador diferente, ou a sua fase não corresponde ao caminho do URL.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado do lado do servidor ao cancelar. A tarefa pode ou não ter sido cancelada — releia-a para confirmar antes de tentar novamente.
Transmita atualizações em tempo real para uma tarefa de keycap via Server-Sent Events (SSE).
O caminho do URL deve corresponder à fase da tarefa — abrir uma transmissão em
/prototype/:buildId/stream emite um único event: error payload com
status_code: 404 e fecha a transmissão.
Parâmetros
Name
id
Type
path
Description
Identificador único para a tarefa de keycap a transmitir.
Retornos
Retorna uma transmissão de objetos de tarefa Keycap Prototype
ou Keycap Build como Server-Sent Events. Para tarefas PENDING ou IN_PROGRESS, a transmissão de resposta incluirá apenas os campos necessários progress e status.
Recupere uma lista paginada das suas tarefas de keycap para um único estágio. O caminho da URL
seleciona o estágio — /prototype retorna tarefas de protótipo; /build
retorna tarefas de construção. Tarefas do outro estágio não estão incluídas em nenhuma das respostas.
Parâmetros de Caminho
Name
stage
Type
path
Obrigatório
Description
Ou prototype ou build. A coleção retorna apenas tarefas
cujo estágio corresponde à URL — buscar /prototype nunca retorna
tarefas de construção e vice-versa.
Parâmetros de Consulta
Name
page_num
Type
integer
predefinição 1
Description
Número da página para paginação.
Name
page_size
Type
integer
predefinição 10
Description
Limite do tamanho da página. O máximo permitido é 100 itens.
Name
sort_by
Type
string
predefinição -created_at
Description
Campo para ordenar. Valores disponíveis:
+created_at: Ordenar por tempo de criação em ordem ascendente.
-created_at: Ordenar por tempo de criação em ordem descendente.
O objeto de Tarefa do Protótipo de Keycap é uma unidade de trabalho que a Meshy acompanha para gerar uma imagem de design de keycap finalizada a partir de uma foto de origem. O resultado desta fase é encadeado na fase de construção através de input_task_id mais candidate_id.
Propriedades
Name
id
Type
string
Description
Identificador único para a tarefa. Embora usemos um UUID ordenável por k para ids de tarefa como detalhe de implementação, não deve fazer quaisquer suposições sobre o formato do id.
Name
type
Type
string
Description
Tipo da tarefa. O valor é creative-lab-keycap-prototype.
Name
name
Type
string
Description
O nome da tarefa fornecido quando a tarefa foi criada. String vazia se nenhum nome foi fornecido.
Name
status
Type
string
Description
Status da tarefa. Os valores possíveis são um de PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Progresso da tarefa. Se a tarefa ainda não foi iniciada, esta propriedade será 0. Uma vez que a tarefa tenha sido concluída com sucesso, isto se tornará 100.
Name
created_at
Type
timestamp
Description
Carimbo de data/hora de quando a tarefa foi criada, em milissegundos.
Um carimbo de data/hora representa o número de milissegundos decorridos desde 1 de janeiro de 1970 UTC, seguindo o padrão RFC 3339. Por exemplo, sexta-feira, 1 de setembro de 2023, 12:00:00 PM GMT é representado como 1693569600000. Isto aplica-se a todos os carimbos de data/hora na Meshy API.
Name
started_at
Type
timestamp
Description
Carimbo de data/hora de quando a tarefa foi iniciada, em milissegundos. Se a tarefa ainda não foi iniciada, esta propriedade será 0.
Name
finished_at
Type
timestamp
Description
Carimbo de data/hora de quando a tarefa foi concluída, em milissegundos. Se a tarefa ainda não foi concluída, esta propriedade será 0.
Name
expires_at
Type
timestamp
Description
Carimbo de data/hora de quando o resultado da tarefa expira, em milissegundos.
Name
preceding_tasks
Type
integer
Description
A contagem de tarefas precedentes.
O valor deste campo é significativo apenas se o status da tarefa for PENDING.
Name
task_error
Type
object
Description
Detalhes do erro para tarefas falhadas. Veja Erros para a referência completa do objeto task_error.
Name
consumed_credits
Type
integer
Description
O número de créditos consumidos por esta tarefa. Uma tarefa que atinge SUCCEEDED é cobrada pelo valor total da sua fase. Uma tarefa que nunca é criada (um 4xx no momento do pedido, incluindo uma rejeição de moderação) não é cobrada de todo. Uma tarefa que atinge FAILED retorna 0 — o valor é reembolsado, incluindo um bloqueio de moderação assíncrono. Cancelar via DELETE reembolsa apenas enquanto a tarefa ainda está PENDING; uma tarefa já IN_PROGRESS permanece cobrada, porque o trabalho foi gasto.
Name
image_urls
Type
array of strings
Description
URL descarregável do render do design de keycap finalizado — como o candidato se parece como um keycap finalizado. Contém uma única entrada; image_urls[i] corresponde a candidate_ids[i]. Vazio até que a tarefa atinja SUCCEEDED. O URL é apenas para exibição; o endpoint de construção consome candidate_ids, não estes URLs. Mesmo ciclo de vida do URL que model_urls: assinado, sem cabeçalho Authorization, válido até expires_at, e estável quando a tarefa é relida.
Name
candidate_ids
Type
array of strings
Description
Identificadores de candidatos opacos, paralelos a image_urls. Passe a entrada correspondente ao design escolhido como candidate_id do pedido de construção. Não faça quaisquer suposições sobre o formato destes ids.
Exemplo de Objeto de Tarefa do Protótipo de Keycap
O objeto de Tarefa de Construção de Keycap é uma unidade de trabalho que a Meshy acompanha para gerar a keycap 3D final texturizada a partir de uma tarefa de protótipo bem-sucedida e um candidato escolhido. Uma única construção executa todo o pipeline — geração de modelo branco, posicionamento e corte automáticos, coloração, montagem e exportação.
Propriedades
Name
id
Type
string
Description
Identificador único para a tarefa.
Name
type
Type
string
Description
Tipo da tarefa. O valor é creative-lab-keycap-build.
Name
name
Type
string
Description
O nome da tarefa fornecido quando a tarefa foi criada. String vazia se nenhum nome foi fornecido.
Name
status
Type
string
Description
Estado da tarefa. Os valores possíveis são um de PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Progresso da tarefa. Se a tarefa ainda não foi iniciada, esta propriedade será 0. Uma vez que a tarefa tenha sido concluída com sucesso, isto se tornará 100.
Name
created_at
Type
timestamp
Description
Carimbo de data/hora de quando a tarefa foi criada, em milissegundos.
Name
started_at
Type
timestamp
Description
Carimbo de data/hora de quando a tarefa foi iniciada, em milissegundos.
Name
finished_at
Type
timestamp
Description
Carimbo de data/hora de quando a tarefa foi concluída, em milissegundos.
Name
expires_at
Type
timestamp
Description
Carimbo de data/hora de quando o resultado da tarefa expira, em milissegundos.
Name
preceding_tasks
Type
integer
Description
A contagem de tarefas precedentes. Significativo apenas quando o estado é PENDING.
Name
task_error
Type
object
Description
Detalhes do erro para tarefas falhadas. Veja Erros para a referência completa do objeto task_error.
Name
consumed_credits
Type
integer
Description
O número de créditos consumidos por esta tarefa. Uma tarefa que atinge SUCCEEDED é cobrada pelo valor total da sua fase. Uma tarefa que nunca é criada (um 4xx no momento da solicitação, incluindo uma rejeição de moderação) não é cobrada de todo. Uma tarefa que atinge FAILED retorna 0 — a cobrança é reembolsada, incluindo um bloqueio de moderação assíncrono. Cancelar via DELETE reembolsa apenas enquanto a tarefa ainda está PENDING; uma tarefa já IN_PROGRESS permanece cobrada, porque o trabalho já foi gasto.
Name
model_urls
Type
object
Description
URLs para download dos artefatos do modelo gerado. Tanto o pacote GLB quanto o OBJ são exportados em escala milimétrica do mundo real, Y-up, com a frente da keycap voltada para +Z. As malhas são nomeadas keycap-head e keycap-base; quando a base recorre a um preenchimento padrão, uma terceira malha keycap-base-interior também está presente para a cavidade do caule. Não assuma exatamente duas malhas.
Estas são URLs assinadas: obtenha-as sem um cabeçalho Authorization. Elas permanecem válidas até expires_at, que é 3 dias após finished_at, e reler a tarefa dentro desse período retorna o URL idêntico em vez de um novo assinado. Faça o download e armazene os arquivos você mesmo antes disso — não há como atualizar um link expirado.
Name
glb
Type
string
Description
URL para download do model.glb final texturizado.
Name
obj_zip
Type
string
Description
URL para download de um pacote zip contendo model.obj, model.mtl, e os PNGs de textura que seu MTL realmente referencia. Uma base de cor sólida envia apenas keycap-head.png; uma base padronizada também envia keycap-base.png.
Name
process_image_urls
Type
object
Description
URLs para download de imagens de processo intermediárias, indexadas por tipo. Mesmo ciclo de vida de URL que model_urls: assinadas, sem cabeçalho Authorization, válidas até expires_at, e estáveis quando a tarefa é relida. Tipos atualmente emitidos:
head_design — a imagem de design do candidato escolhido que a construção consumiu (sempre presente).
composite — a renderização de exibição da keycap finalizada do candidato escolhido (presente quando disponível).
base_canvas — a tela da base da keycap pintada (presente quando disponível).
Trate o conjunto de chaves como aberto; novos tipos podem ser adicionados sem uma alteração de quebra.
Exemplo de Objeto de Tarefa de Construção de Keycap
O fluxo completo: criar um protótipo a partir de uma foto, verificar até
SUCCEEDED, escolher um candidato de candidate_ids, criar uma construção com
esse candidato, verificar a construção até SUCCEEDED, e depois descarregar o pacote GLB e
OBJ de model_urls.
O exemplo escolhe o primeiro candidato programaticamente. Numa integração real
mostraria a entrada image_urls ao utilizador final e deixaria que ele escolhesse; o índice escolhido mapeia 1:1 para candidate_ids.
Fluxo completo
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"