Transforme uma foto de origem em uma tecla mecânica personalizada e colorida em
duas etapas: protótipo gera um render de design de "tecla finalizada"
a partir da sua foto de entrada. Uma vez que você tenha confirmado esse render, construir transforma-o em um
modelo 3D de tecla texturizada em uma única execução — geração de modelo branco, posicionamento e corte automáticos em uma pose padrão calibrada, coloração do modelo completo e
montagem final acontecem todos dentro de uma única tarefa de construção. As duas etapas são vinculadas
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 exigem um plano de assinatura pago. Solicitações de
contas de plano gratuito são rejeitadas com 402 Payment Required.
Gere um render de design de keycap finalizado a partir da foto de origem. O resultado da tarefa carrega um array image_urls (o render de exibição do keycap finalizado) e um array paralelo candidate_ids; ambos contêm uma única entrada. Chame este endpoint novamente para outro render se o resultado não for o que você deseja — cada chamada é cobrada separadamente. Passe o candidate_id junto 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 é detectado decodificando os dados da imagem, não pela extensão do arquivo da URL — uma URL sem extensão, ou que redireciona, funciona desde que os bytes sejam decodificados para um formato suportado. Redirecionamentos HTTP são seguidos. A orientação EXIF é normalizada, então uma foto de telefone rotacionada é usada da forma como parece.
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 baixado. Para um Data URI, o limite se aplica aos bytes decodificados, então o arquivo de origem em si pode ter até esse tamanho — é o texto base64 que é cerca de um terço maior, o que importa para o corpo da sua solicitação, 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: Uma URL acessível da internet pública.
Data URI: Um Data URI codificado em base64 da imagem. Exemplo de um Data URI: data:image/jpeg;base64,<seus dados de imagem codificados em base64>.
Name
name
Type
string
Description
Nome de tarefa opcional para fins de exibição. Máximo de 100 caracteres.
Name
remove_background
Type
boolean
padrão false
Description
Quando definido como true, o render de exibição retornado em image_urls é um PNG RGBA transparente com o fundo removido, para que você possa compô-lo em qualquer fundo.
Isso se aplica apenas ao render de exibição. O candidato que o endpoint de construção consome não é afetado, então o resultado 3D é idêntico de qualquer forma.
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 assine o stream até que a tarefa atinja SUCCEEDED, então pegue a entrada de candidate_ids e passe-a, junto com o ID da tarefa, para o endpoint de construção.
Modos de Falha
Name
400 - Bad Request
Description
A solicitação foi inaceitável. Causas comuns:
Parâmetro ausente: 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 arquivo ou excede a contagem máxima de pixels.
URL inacessível: O image_url não pôde ser baixado (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 sua chave de API.
Name
402 - Payment Required
Description
A conta está no plano gratuito (um plano pago é necessário para criar tarefas) ou não possui 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
Você excedeu seu limite de taxa.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado no 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, então tentar novamente é seguro.
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>" }'
Gere o modelo 3D final texturizado de keycap a partir de uma tarefa de protótipo bem-sucedida e um de 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 geralmente leva 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 o formato 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 do webapp não são aceitas — 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 ser construído, 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 você deseja substituir.
Name
base_model
Type
string
padrã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 planejados 3–5 tamanhos padrão adicionais; tamanhos personalizados não são suportados.
Name
head_size_mm
Type
number
padrão 23
Description
Tamanho alvo da cabeça esculpida, em milímetros: sua maior dimensão é 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, então a maior dimensão entregue pode ser menor do que a solicitada. O valor aplicado não é refletido de volta no objeto da tarefa hoje — se você 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
padrã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 recém-criada de construção de keycap. Consulte o endpoint Obter uma Tarefa ou assine o stream até que a tarefa alcance SUCCEEDED, então baixe os artefatos de model_urls.glb e model_urls.obj_zip.
Tanto o pacote GLB quanto o OBJ são exportados em 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
A solicitação foi inaceitável. Causas comuns:
Parâmetro ausente: 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 alcançou 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 sua chave de API.
Name
402 - Payment Required
Description
A conta está no plano gratuito (um plano pago é necessário para criar tarefas) ou não possui créditos suficientes.
Name
404 - Not Found
Description
A tarefa de protótipo referenciada não existe, pertence a um usuário diferente ou foi criada através do webapp (apenas tarefas de protótipo em modo API encadeiam em construção).
Name
429 - Too Many Requests
Description
Você excedeu 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, então 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 fornecendo um id de tarefa válido. O caminho da URL
deve corresponder ao estágio da tarefa — uma tarefa de construção buscada 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 consumindo recursos). Tarefas que já atingiram um estado terminal (SUCCEEDED, FAILED, CANCELED) não podem ser canceladas.
O caminho da URL deve corresponder ao estágio da tarefa — DELETE em
/prototype/:buildId retorna 404.
Parâmetros de Caminho
Name
id
Type
path
Description
Identificador único para a tarefa de keycap a ser cancelada.
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á em um estado terminal e não pode ser cancelada.
Name
404 - Not Found
Description
A tarefa não existe, pertence a um usuário diferente ou seu estágio não corresponde ao caminho da URL.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado no servidor ao cancelar. A tarefa pode ou não ter sido cancelada — leia novamente para confirmar antes de tentar novamente.
Transmita atualizações em tempo real para uma tarefa de keycap via Server-Sent Events (SSE).
O caminho da URL deve corresponder ao estágio 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 ser transmitida.
Retornos
Retorna uma transmissão de objetos de tarefa Protótipo de Keycap
ou Construção de Keycap 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 de 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 sã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 corresponda à URL — buscar /prototype nunca retorna
tarefas de construção e vice-versa.
Parâmetros de Consulta
Name
page_num
Type
integer
padrão 1
Description
Número da página para paginação.
Name
page_size
Type
integer
padrão 10
Description
Limite de tamanho da página. O máximo permitido é 100 itens.
Name
sort_by
Type
string
padrão -created_at
Description
Campo para ordenar. Valores disponíveis:
+created_at: Ordenar por tempo de criação em ordem crescente.
-created_at: Ordenar por tempo de criação em ordem decrescente.
O objeto de Tarefa de 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. A saída desta etapa é encadeada na etapa de construção via 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, você não deve fazer 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, isso 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. Isso se aplica 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 de 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 de sua etapa. Uma tarefa que nunca é criada (um 4xx no momento da solicitação, incluindo uma rejeição de moderação) não é cobrada. 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, pois o trabalho já foi realizado.
Name
image_urls
Type
array of strings
Description
URL para download 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. A URL é apenas para exibição; o endpoint de construção consome candidate_ids, não essas URLs. Mesmo ciclo de vida da URL que model_urls: assinada, sem cabeçalho Authorization, válida 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 da solicitação de construção. Não faça suposições sobre o formato desses ids.
Exemplo de Objeto de Tarefa de Protótipo de Keycap
O objeto de Tarefa de Construção de Keycap é uma unidade de trabalho que a Meshy acompanha para gerar o keycap 3D texturizado final a partir de uma tarefa de protótipo bem-sucedida e um candidato escolhido. Uma única construção executa todo o pipeline — geração do modelo branco, ajuste 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
Status da tarefa. Os valores possíveis são um dos 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, isso 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 status é 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 de sua etapa. Uma tarefa que nunca é criada (um 4xx no momento da solicitação, incluindo uma rejeição de moderação) não é cobrada. 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, pois o trabalho já foi gasto.
Name
model_urls
Type
object
Description
URLs para download dos artefatos do modelo gerado. Tanto o GLB quanto o pacote OBJ são exportados em escala real em milímetros, Y para cima, com a frente do keycap voltada para +Z. As malhas são nomeadas keycap-head e keycap-base; quando a base recai para 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: busque-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 a URL idêntica em vez de uma recém-assinada. Baixe 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 texturizado final.
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 do keycap finalizado do candidato escolhido (presente quando disponível).
base_canvas — a tela da base do keycap pintada (presente quando disponível).
Trate o conjunto de chaves como aberto; novos tipos podem ser adicionados sem uma alteração de ruptura.
Exemplo de Objeto de Tarefa de Construção de Keycap
O fluxo completo: criar um protótipo a partir de uma foto, verificar seu status até SUCCEEDED, escolher um candidato de candidate_ids, criar uma construção com esse candidato, verificar a construção até SUCCEEDED, e então baixar o GLB e o pacote OBJ de model_urls.
O exemplo escolhe o primeiro candidato programaticamente. Em uma integração real, você exibiria a entrada image_urls para o usuário 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"