Transforme uma foto de origem em uma tecla (keycap) personalizada e colorida para teclado mecânico em
duas etapas: prototype gera uma renderização de design de "keycap finalizada"
a partir da sua foto de entrada. Depois de confirmar essa renderização, build a transforma em um
modelo 3D de keycap texturizado em uma única execução — geração do modelo branco, encaixe automático
e corte em uma pose padrão calibrada, coloração do modelo completo e
montagem final, tudo isso acontece dentro de uma única tarefa de build. As duas etapas são conectadas
por meio de 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. Requisições de
contas do plano gratuito são rejeitadas com 402 Payment Required.
Gera uma renderização do design de um keycap finalizado a partir da foto de origem. O resultado da tarefa carrega 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 obter outra renderização 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 prototype para o endpoint de build. Consulte
The Keycap Prototype Task Object
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 oferecemos suporte aos formatos .jpg, .jpeg, .png e .webp.
O formato é detectado ao decodificar os dados da imagem, não pela extensão de arquivo da URL — uma URL sem extensão, ou uma que redireciona, funciona desde que os bytes sejam decodificados para um formato compatível. Redirecionamentos HTTP são seguidos. A orientação EXIF é normalizada, então uma foto de celular rotacionada é usada da forma como aparece visualmente.
Limites: no mínimo 32 pixels em cada lado, no máximo 178.956.970 pixels no total, e no máximo 20.000.000 bytes após o download. 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 em base64 que fica cerca de um terço maior, o que importa para o corpo da sua requisição, não para esse limite. Um Data URI deve declarar um content type image/* e ;base64.
Existem duas maneiras de fornecer a imagem:
URL publicamente acessível: Uma URL que é acessível pela internet pública.
Data URI: Um data URI da imagem codificado em base64. Exemplo de um Data URI: data:image/jpeg;base64,<seus dados de imagem codificados em base64>.
Name
name
Type
string
Description
Nome opcional da tarefa para fins de exibição. Máximo de 100 caracteres.
Name
remove_background
Type
boolean
padrã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 você possa compor sobre qualquer fundo.
Isso se aplica apenas à renderização de exibição. O candidate consumido pelo endpoint de build não é afetado, então o resultado 3D é idêntico em ambos os casos.
Retornos
A propriedade result da resposta contém o id da tarefa da tarefa de prototype de keycap recém-criada. Faça polling no endpoint Get a Task ou inscreva-se no 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 build.
Modos de Falha
Name
400 - Bad Request
Description
A requisição era inaceitável. Causas comuns:
Parâmetro ausente: image_url é obrigatório.
Formato de imagem inválido: O image_url fornecido não está em um formato suportado (.jpg, .jpeg, .png, .webp).
Dimensões de imagem fora do intervalo: A imagem é muito pequena, excede o tamanho máximo de 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 em base64 está malformada.
Conteúdo sinalizado: A imagem de entrada foi sinalizada pela moderation de NSFW.
Name
401 - Unauthorized
Description
A autenticação falhou. 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 possui créditos insuficientes.
Name
403 - Forbidden
Description
A imagem de entrada foi sinalizada pela moderation 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 lado do servidor — por exemplo, o serviço de moderation de conteúdo estava indisponível, o staging da imagem de entrada falhou, ou a tarefa não pôde ser criada. Nenhuma tarefa é criada nesse caso, então é seguro tentar novamente.
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>" }'
Gera o modelo final de keycap 3D texturizado a partir de uma tarefa de protótipo bem-sucedida e um de seus candidatos. Uma única tarefa de build executa todo o pipeline de ponta a ponta — geração do modelo branco a partir do design escolhido, assentamento e corte automáticos sobre a base do keycap usando uma pose padrão calibrada (sem necessidade de ajuste interativo), coloração completa do modelo e montagem e exportação finais. Um build geralmente leva de 3 a 7 minutos, aproximando-se do limite superior quando vários builds são executados simultaneamente.
Consulte O Objeto de Tarefa de Build 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 por meio deste mesmo endpoint da OpenAPI. O protótipo deve ter sido criado pela mesma conta Meshy, deve ter atingido SUCCEEDED e deve ter produzido pelo menos um candidato.
Tarefas de protótipo criadas por meio da webapp não são aceitas — o endpoint de build aceita apenas tarefas de protótipo produzidas por POST /openapi/creative-lab/keycap/v1/prototype e recusa qualquer outra origem com 404.
Name
candidate_id
Type
string
Obrigatório
Description
O candidato a ser construído, obtido a partir 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 possui um valor padrão calibrado — envie apenas os que deseja substituir.
Name
base_model
Type
string
padrão cherry-mx-1x1-r1
Description
A base de keycap sobre a qual construir. Atualmente, o único valor disponível é
cherry-mx-1x1-r1 — um keycap padrão de perfil Cherry MX 1u. Estão planejados de 3 a 5
tamanhos padrão adicionais amplamente utilizados; 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 esse valor. Intervalo: [10, 40]. Valores acima de aproximadamente 32.9 podem ser reduzidos para que a cabeça ainda caiba dentro do limite de área de proteção da base, de modo que a maior dimensão entregue pode ser menor do que a solicitada. O valor aplicado não é retornado no objeto da tarefa atualmente — se você precisar confirmar o tamanho realmente obtido, 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 ela 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 build de keycap criada. Faça polling do endpoint Obter uma Tarefa ou inscreva-se no stream até que a tarefa atinja SUCCEEDED, e então baixe os artefatos a partir de model_urls.glb e model_urls.obj_zip.
Tanto o GLB quanto o pacote OBJ são exportados em escala real de
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 requisiçã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.
Tarefa pai não concluída: 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 nenhum candidato.
Candidato desconhecido: candidate_id não é um dos candidatos da tarefa de entrada.
Opções fora do intervalo: Um dos campos de options ficou fora do intervalo permitido ou do conjunto de valores enumerados.
Name
401 - Unauthorized
Description
A autenticação falhou. Verifique sua chave de API.
Name
402 - Payment Required
Description
A conta está no plano gratuito (é necessário um plano pago para criar tarefas) ou possui créditos insuficientes.
Name
404 - Not Found
Description
A tarefa de protótipo referenciada não existe, pertence a outro usuário ou foi criada por meio da webapp (apenas tarefas de protótipo criadas em modo API se encadeiam para o build).
Name
429 - Too Many Requests
Description
Você excedeu seu limite de taxa.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado do lado do servidor — por exemplo, o serviço de moderation estava indisponível, o estágio de preparação da imagem de entrada falhou, ou a tarefa não pôde ser criada. Nesse caso, nenhuma tarefa é criada, 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 } }'
Recupera uma tarefa de protótipo ou de build a partir de um id de tarefa válido. O caminho da URL
deve corresponder ao estágio da tarefa — uma tarefa de build buscada através de
/prototype/:id retorna 404, e vice-versa.
Cancela uma tarefa 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 worker já pode estar
consumindo recursos). Tarefas que já atingiram um estado terminal
(SUCCEEDED, FAILED, CANCELED) não podem ser canceladas.
O caminho da URL deve corresponder à etapa da tarefa — DELETE em
/prototype/:buildId retorna 404.
Parâmetros de Caminho
Name
id
Type
path
Description
Identificador único da tarefa keycap a ser cancelada.
Retornos
Retorna 204 No Content em caso de sucesso, com 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 sua etapa não corresponde ao caminho da URL.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado no servidor durante o cancelamento. A tarefa pode ou não ter sido cancelada — leia-a novamente para confirmar antes de tentar de novo.
Transmite 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 um stream em
/prototype/:buildId/stream emite um único event: error payload com
status_code: 404 e encerra o stream.
Parâmetros
Name
id
Type
path
Description
Identificador único da tarefa de keycap a ser transmitida.
Retornos
Retorna um stream de objetos de tarefa Keycap Prototype
ou Keycap Build como
Server-Sent Events. Cada frame carrega o objeto de tarefa completo do estágio — o mesmo formato que o
endpoint Get retorna — então, enquanto a tarefa está PENDING ou IN_PROGRESS, os
campos de saída simplesmente ainda não estão preenchidos (null, [] ou {}) e
finished_at é null.
// Error event example (wrong stage or task not found)event: errordata: {"status_code": 404,"message": "Task not found"}// Message event examples illustrate task progress.// Every frame is the full task object; fields not yet populated are null / empty.// The PENDING frame below is abbreviated to the fields that change.event: messagedata: {"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af","progress": 0,"status": "PENDING"}event: messagedata: {"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af","type": "creative-lab-keycap-build","status": "SUCCEEDED","progress": 100,"created_at": 1753142600000,"started_at": 1753142610000,"finished_at": 1753143050000,"expires_at": 1753402250000,"task_error": null,"consumed_credits": 50,"model_urls": {"glb":"https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***","obj_zip":"https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***" },"process_image_urls": {"head_design":"https://assets.meshy.ai/***/head-design.png?Expires=***" }}
Recupera 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 build. 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
prototype ou build. A coleção retorna apenas tarefas
cujo estágio corresponda à URL — buscar /prototype nunca retorna
tarefas de build 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 ordenação. Valores disponíveis:
+created_at: Ordena pelo horário de criação em ordem crescente.
-created_at: Ordena pelo horário de criação em ordem decrescente.
O objeto Keycap Prototype Task é uma unidade de trabalho que o Meshy monitora para
gerar uma imagem de design de keycap finalizado a partir de uma foto de origem. A
saída dessa etapa é encadeada com a etapa de build
por meio de input_task_id mais candidate_id.
Propriedades
Name
id
Type
string
Description
Identificador único da tarefa. Embora usemos um UUID k-sortable para os 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 dos seguintes: PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
Name
progress
Type
integer
Description
Progress da tarefa. Se a tarefa ainda não foi iniciada, essa propriedade será 0. Assim que a tarefa for concluída com sucesso, ela 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 transcorridos 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, essa 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, essa 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 desse campo só é significativo se o status da tarefa for PENDING.
Name
task_error
Type
object
Description
Detalhes do erro para tarefas com falha. Consulte 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 integral referente à sua etapa. Uma tarefa que nunca chega a ser criada (um 4xx no momento da requisição, incluindo uma rejeição de moderation) não é cobrada de forma alguma. Uma tarefa que atinge FAILED retorna 0 — a cobrança é reembolsada, incluindo um bloqueio de moderation assíncrono. Cancelar por meio de DELETE reembolsa apenas enquanto a tarefa ainda estiver 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 da renderização do design de keycap finalizado — a aparência do candidato 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 build consome candidate_ids, não essas URLs. Mesmo ciclo de vida de 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 opacos de candidatos, paralelos a image_urls. Passe a entrada correspondente ao design escolhido como o candidate_id da requisição de build. Não faça suposições sobre o formato desses ids.
O objeto Keycap Build Task é uma unidade de trabalho que a Meshy monitora para
gerar o keycap 3D texturizado final a partir de uma tarefa de prototype bem-sucedida e um
candidato escolhido. Uma única build executa o pipeline completo — geração do
white-model, encaixe (seating) 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 ela foi criada. Uma 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
Progress da tarefa. Se a tarefa ainda não foi iniciada, essa propriedade será 0. Uma vez que a tarefa tenha sido concluída com sucesso, ela 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 finalizada, 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. Relevante apenas quando o status é PENDING.
Name
task_error
Type
object
Description
Detalhes de erro para tarefas com falha. Consulte Erros para a referência completa do objeto task_error.
Name
consumed_credits
Type
integer
Description
O número de créditos consumidos por essa tarefa. Uma tarefa que atinge SUCCEEDED é cobrada no valor total referente à sua etapa. Uma tarefa que nunca chega a ser criada (um 4xx no momento da requisição, incluindo uma rejeição de moderation) não é cobrada de forma alguma. Uma tarefa que atinge FAILED retorna 0 — a cobrança é reembolsada, incluindo em um bloqueio de moderation 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
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 de milímetros do mundo real, com Y para cima, com a frente do keycap voltada para +Z. As malhas são nomeadas keycap-head e keycap-base; quando a base recorre a um preenchimento de padrão (pattern fill), uma terceira malha keycap-base-interior também está presente para a cavidade do stem. Não assuma que existirão exatamente duas malhas.
Estas são URLs assinadas: busque-as sem um header 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. Faça o download e armazene os arquivos por conta própria antes disso — não há como renovar 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 entrega apenas keycap-head.png; uma base com padrão também entrega keycap-base.png.
Name
process_image_urls
Type
object
Description
URLs para download de imagens intermediárias do processo, indexadas por tipo (kind). Mesmo ciclo de vida de URL que model_urls: assinadas, sem header Authorization, válidas até expires_at, e estáveis quando a tarefa é relida. Tipos emitidos atualmente:
head_design — a imagem de design do candidato escolhido que a build consumiu (sempre presente).
composite — a renderização de exibição do keycap finalizado do candidato escolhido (presente quando disponível).
base_canvas — o canvas pintado da base do keycap (presente quando disponível).
Trate o conjunto de chaves como aberto a expansão; novos tipos podem ser adicionados sem que isso seja uma alteração que quebre a compatibilidade.
O fluxo completo: criar um prototype a partir de uma foto, fazer polling até
SUCCEEDED, escolher um candidato em candidate_ids, criar um build com
esse candidato, fazer polling do build até SUCCEEDED, e então baixar o GLB e
o pacote OBJ a partir de model_urls.
O exemplo escolhe o primeiro candidato programaticamente. Em uma integração
real, você exibiria a entrada de image_urls para o usuário final e
deixaria que ele escolhesse; o índice escolhido mapeia 1:1 para candidate_ids.
Complete flow
POST
/openapi/creative-lab/keycap/v1
#!/usr/bin/env bashset-euopipefail# Requires curl and jq. Point IMAGE_PATH at a local photo, or IMAGE_URL at a public one:# export MESHY_API_KEY=msy_...# export IMAGE_PATH=./portrait.jpg # or: export IMAGE_URL=https://...:"${MESHY_API_KEY:?export MESHY_API_KEY first}"if [[ -z"${IMAGE_PATH:-}"&&-z"${IMAGE_URL:-}" ]]; thenecho"export IMAGE_PATH (local file) or IMAGE_URL (public url) first">&2exit1fiBASE="https://api.meshy.ai/openapi/creative-lab/keycap/v1"AUTH="Authorization: Bearer $MESHY_API_KEY"# api METHOD URL [curl args...] -> prints the response body, non-zero on failure.# Note we do not use -f/--fail: it discards the body, and the body is the only# place the reason appears.api() {local method=$1 url=$2 out http_code bodyshift2 out=$(curl--silent--show-error--max-time60--write-out$'\n%{http_code}' \-X"$method""$url"-H"$AUTH""$@") ||return1 http_code=${out##*$'\n'} body=${out%$'\n'*}if ((http_code >=400)); thenecho"HTTP $http_code for $url: $body">&2return1fiprintf'%s'"$body"}# Each task gets its own 40-minute budget.poll() {local kind=$1 id=$2 delay=5 task_status deadline deadline=$(($(date+%s) + 2400))while:; doif (($(date +%s) >= deadline)); thenecho"gave up waiting for $kind $id">&2return1fi task_status=$(apiGET"$BASE/$kind/$id"|jq-r'.status')echo"$kind: $task_status"case"$task_status"inSUCCEEDED)return0 ;;FAILED|CANCELED)return1 ;;esacsleep"$delay" delay=$((delay*2>30?30:delay*2))done}# Build the request body in a file. A base64 data URI must never go on the# command line or into an exported variable - a photo of any real size will# exceed the OS argument limit.BODY=$(mktemp)trap'rm -f "$BODY"'EXITif [[ -n"${IMAGE_PATH:-}" ]]; then# Declare the real type: the API accepts JPEG, PNG and WebP.case"$(printf '%s' "${IMAGE_PATH##*.}" |tr 'A-Z' 'a-z')"inpng) MIME=image/png ;;webp) MIME=image/webp ;;*) MIME=image/jpeg ;;esac {printf'{"image_url":"data:%s;base64,'"$MIME"base64<"$IMAGE_PATH"|tr-d'\n'printf'"}' } >"$BODY"elseprintf'{"image_url":"%s"}'"$IMAGE_URL">"$BODY"fi# 1. Create the prototype taskPROTO_ID=$(apiPOST"$BASE/prototype" \-H'Content-Type: application/json'--data-binary@"$BODY"|jq-r'.result')# 2. Wait for the design renderpollprototype"$PROTO_ID"# 3. Pick a candidate (first one here; show image_urls to a user in production)CANDIDATE_ID=$(apiGET"$BASE/prototype/$PROTO_ID"|jq-r'.candidate_ids[0]')# 4. Create the build taskjq-n--argp"$PROTO_ID"--argc"$CANDIDATE_ID" \'{input_task_id: $p, candidate_id: $c}'>"$BODY"BUILD_ID=$(apiPOST"$BASE/build" \-H'Content-Type: application/json'--data-binary@"$BODY"|jq-r'.result')# 5. Wait for the model (a build usually takes 3-7 minutes)pollbuild"$BUILD_ID"# 6. Download the artifacts. These are signed URLs: no Authorization header,# and they stay valid for 3 days after the task finishes.TASK=$(apiGET"$BASE/build/$BUILD_ID")curl--silent--show-error--fail--max-time900 \-okeycap.glb"$(jq-r '.model_urls.glb' <<<"$TASK")"curl--silent--show-error--fail--max-time900 \-okeycap-obj.zip"$(jq-r '.model_urls.obj_zip' <<<"$TASK")"echo"Done: keycap.glb + keycap-obj.zip"