Transforme uma fotografia de origem numa keycap de teclado mecânico personalizada, a cores, em
duas etapas: prototype gera uma renderização de design de "keycap acabada"
a partir da sua fotografia de entrada. Depois de confirmar essa renderização, build transforma-a num
modelo 3D de keycap texturizado numa única execução — geração do modelo branco, assentamento e
corte automáticos numa pose predefinida calibrada, coloração completa do modelo e
montagem final acontecem tudo dentro de uma única tarefa de construção. As duas etapas são associadas
através 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 requerem um plano de subscrição pago. Os pedidos de
contas do plano gratuito são rejeitados com 402 Payment Required.
Gera 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 obter outra renderização se o resultado não for o que pretende — cada chamada é faturada separadamente. Passe o candidate_id juntamente com o ID da tarefa de protótipo para o endpoint de build.
Consulte
O Objeto de Tarefa de Protótipo de Keycap
para saber o formato da resposta.
Parâmetros
Name
image_url
Type
string
Obrigatório
Description
Foto de origem para o Meshy transformar em imagens de design de keycap. Atualmente, suportamos os formatos .jpg, .jpeg, .png e .webp.
O formato é detetado através da descodificação dos dados da imagem, não a partir da extensão de ficheiro do URL — um URL sem extensão, ou que redirecione, funciona desde que os bytes sejam descodificados para um formato suportado. Os redirecionamentos HTTP são seguidos. A orientação EXIF é normalizada, pelo que uma foto de telemóvel rodada é utilizada tal como é apresentada visualmente.
Limites: pelo menos 32 pixels em cada lado, no máximo 178.956.970 pixels no total, e no máximo 20.000.000 bytes após a transferência. Para um Data URI, o limite aplica-se aos bytes descodificados, pelo que o ficheiro de origem em si pode ter até esse tamanho — é o texto base64 que fica cerca de um terço maior, o que é relevante 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 formas de fornecer a imagem:
URL publicamente acessível: Um URL que seja acessível a partir da internet pública.
Data URI: Um Data URI da imagem codificado em base64. Exemplo de um Data URI: data:image/jpeg;base64,<os seus dados de imagem codificados em base64>.
Name
name
Type
string
Description
Nome opcional da tarefa para fins de apresentação. Máximo de 100 carateres.
Name
remove_background
Type
boolean
predefinição false
Description
Quando definido como true, a renderização de exibição devolvida em image_urls é um PNG RGBA transparente com o fundo removido, para que possa compô-la sobre qualquer fundo.
Isto aplica-se apenas à renderização de exibição. O candidato consumido pelo endpoint de build não é afetado, pelo que o resultado 3D é idêntico em ambos os casos.
Retorno
A propriedade result da resposta contém o id da tarefa da nova tarefa de protótipo de keycap criada. Consulte periodicamente o endpoint Obter uma Tarefa ou subscreva o stream até a tarefa atingir o estado SUCCEEDED e, em seguida, obtenha a entrada de candidate_ids e passe-a, juntamente com o ID da tarefa, para o endpoint de build.
Modos de Falha
Name
400 - Bad Request
Description
O pedido era inaceitável. Causas comuns:
Parâmetro em falta: image_url é obrigatório.
Formato de imagem inválido: O image_url fornecido não está num formato suportado (.jpg, .jpeg, .png, .webp).
Dimensões da imagem fora do intervalo permitido: A imagem é demasiado pequena, excede o tamanho máximo de ficheiro ou excede o número máximo de pixels.
URL inacessível: Não foi possível transferir o image_url (404 ou timeout).
Data URI inválido: A cadeia base64 está mal formada.
Conteúdo assinalado: A imagem fornecida foi assinalada pela moderation de conteúdo impróprio (NSFW).
Name
401 - Unauthorized
Description
A autenticação falhou. 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 fornecida foi assinalada pela moderation 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 moderation de conteúdo estava indisponível, o carregamento da imagem de entrada falhou, ou não foi possível criar a tarefa. Neste caso, nenhuma tarefa é criada, pelo que é 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 3D de keycap com textura final 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 do modelo em branco a partir do design escolhido, assentamento e corte automáticos na base da keycap usando uma pose predefinida calibrada (sem necessidade de ajuste interativo), coloração do modelo completo, e montagem e exportação finais. Uma construção demora normalmente 3 a 7 minutos, aproximando-se do limite superior quando várias construções são executadas em simultâneo.
Consulte O Objeto de Tarefa de Construção de Keycap
para conhecer o formato da resposta.
Parâmetros
Name
input_task_id
Type
string
Obrigatório
Description
O ID da tarefa de uma tarefa de protótipo criada através deste mesmo endpoint 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 através da webapp 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 origem 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 apresentação. Máximo de 100 caracteres.
options
Ajuste opcional da geometria. Cada campo tem um valor predefinido calibrado — envie apenas os que pretende substituir.
Name
base_model
Type
string
predefinição cherry-mx-1x1-r1
Description
A base de keycap sobre a qual construir. Atualmente, o único valor disponível é
cherry-mx-1x1-r1 — uma keycap padrão de perfil Cherry MX 1u. Estão planeadas
3–5 dimensões padrão adicionais habituais; 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 continue a caber no limite de área protegida da base, pelo que a dimensão mais longa entregue pode ser inferior à solicitada. O valor aplicado não é atualmente devolvido no objeto da tarefa — se precisar de confirmar o tamanho que realmente recebeu, meça a caixa delimitadora da malha keycap-head no modelo transferido.
Name
vertical_offset_mm
Type
number
predefinição 0
Description
Deslocamento vertical aplicado à cabeça antes de esta ser assente 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. Faça polling ao endpoint Obter uma Tarefa ou subscreva o stream até a tarefa atingir SUCCEEDED e, em seguida, transfira os artefactos a partir de model_urls.glb e model_urls.obj_zip.
Tanto o GLB como o pacote OBJ são exportados à escala real em
milímetros, com um sistema de coordenadas Y para cima e a frente
da keycap virada 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.
Tarefa principal 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 candidatos.
Candidato desconhecido: candidate_id não é um dos candidatos da tarefa de entrada.
Opções fora do intervalo: Um dos campos de options encontrava-se fora do intervalo permitido ou do conjunto de valores enumerados.
Name
401 - Unauthorized
Description
A autenticação falhou. 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 outro utilizador, ou foi criada através da webapp (apenas tarefas de protótipo em modo API podem encadear-se numa construção).
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 moderation estava indisponível, falhou a preparação da imagem de entrada, ou não foi possível criar a tarefa. Neste caso, não é criada nenhuma tarefa, pelo que é seguro tentar novamente.
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 } }'
Obtenha uma tarefa de protótipo ou de build indicando um id de tarefa válido. O caminho do URL
tem de corresponder à fase da tarefa — uma tarefa de build obtida através de
/prototype/:id devolve 404, e vice-versa.
Cancela uma tarefa de keycap. Se a tarefa ainda estiver PENDING, os
créditos consumidos no momento da criação são reembolsados. As tarefas que já
estejam IN_PROGRESS são canceladas sem reembolso (o worker pode já estar
a consumir recursos). As tarefas que já tenham atingido um estado terminal
(SUCCEEDED, FAILED, CANCELED) não podem ser canceladas.
O caminho do 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 da 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á se encontra 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 o seu estágio não corresponde ao caminho do URL.
Name
500 - Internal Server Error
Description
Ocorreu um erro inesperado do lado do servidor durante o cancelamento. A tarefa pode ou não ter sido cancelada — volte a lê-la para confirmar antes de tentar novamente.
Faz stream de atualizações em tempo real para uma tarefa de keycap através de Server-Sent Events (SSE).
O caminho do URL deve corresponder à fase 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 para fazer stream.
Retorna
Retorna um stream de objetos de tarefa Keycap Prototype
ou Keycap Build como
Server-Sent Events. Cada frame contém o objeto de tarefa completo para a fase — a mesma forma que o
endpoint Get devolve — pelo que, 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=***" }}
Obtenha uma lista paginada das suas tarefas keycap para um único estágio. O caminho
do URL seleciona o estágio — /prototype devolve tarefas de protótipo; /build
devolve tarefas de compilação. As 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 devolve apenas tarefas
cujo estágio corresponda ao URL — obter /prototype nunca devolve
tarefas de compilaçã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 de tamanho da página. O máximo permitido é 100 itens.
Name
sort_by
Type
string
predefinição -created_at
Description
Campo pelo qual ordenar. Valores disponíveis:
+created_at: Ordena por hora de criação em ordem ascendente.
-created_at: Ordena por hora de criação em ordem descendente.
O objeto de Tarefa de Protótipo de Keycap é uma unidade de trabalho que a Meshy monitoriza para
gerar uma imagem de design de keycap finalizado a partir de uma fotografia de origem. O
resultado desta etapa é encadeado na etapa de construção
através de input_task_id mais candidate_id.
Propriedades
Name
id
Type
string
Description
Identificador único da tarefa. Embora utilizemos um UUID k-sortable para os ids das tarefas 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 no momento da criação da tarefa. String vazia se não tiver sido fornecido nenhum nome.
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
Progress da tarefa. Se a tarefa ainda não tiver começado, esta propriedade será 0. Assim que a tarefa for concluída com sucesso, este valor passará a ser 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
a norma RFC 3339.
Por exemplo, sexta-feira, 1 de setembro de 2023 às 12:00:00 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 tiver sido 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 tiver sido 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 só é significativo se o estado da tarefa for PENDING.
Name
task_error
Type
object
Description
Detalhes de erro para tarefas falhadas. 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 na totalidade correspondente à sua etapa. Uma tarefa que nunca chega a ser criada (um 4xx no momento do pedido, incluindo uma rejeição por moderation) não é cobrada de todo. Uma tarefa que atinge FAILED devolve 0 — a cobrança é reembolsada, incluindo um bloqueio de moderation assíncrono. Cancelar através de DELETE só reembolsa enquanto a tarefa ainda estiver PENDING; uma tarefa já IN_PROGRESS mantém-se cobrada, porque o trabalho já foi despendido.
Name
image_urls
Type
array of strings
Description
URL transferível da renderização do design de keycap finalizado — o aspeto que o candidato tem como keycap acabado. Contém uma única entrada; image_urls[i] corresponde a candidate_ids[i]. Vazio até a tarefa atingir SUCCEEDED. O URL destina-se apenas a exibição; o endpoint de construção consome candidate_ids, não estes URLs. O mesmo ciclo de vida de 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 opacos de candidatos, paralelos a image_urls. Passe a entrada correspondente ao design escolhido como o candidate_id do pedido de construção. Não faça quaisquer suposições sobre o formato destes ids.
O objeto Keycap Build Task é uma unidade de trabalho que a Meshy acompanha para
gerar o keycap 3D texturizado final a partir de uma prototype task bem-sucedida e de um
candidato escolhido. Uma única build executa o pipeline completo — geração
do modelo branco, assentamento e corte automáticos, coloração, montagem e exportação.
Propriedades
Name
id
Type
string
Description
Identificador único da 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 no momento da criação da tarefa. Cadeia vazia se não foi fornecido nenhum nome.
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 tiver sido iniciada, esta propriedade será 0. Assim que a tarefa for bem-sucedida, este valor passará a ser 100.
Name
created_at
Type
timestamp
Description
Carimbo de data/hora da criação da tarefa, em milissegundos.
Name
started_at
Type
timestamp
Description
Carimbo de data/hora do início da tarefa, em milissegundos.
Name
finished_at
Type
timestamp
Description
Carimbo de data/hora da conclusão da tarefa, em milissegundos.
Name
expires_at
Type
timestamp
Description
Carimbo de data/hora de expiração do resultado da tarefa, em milissegundos.
Name
preceding_tasks
Type
integer
Description
O número de tarefas precedentes. Só é relevante quando o estado é 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 com o valor total correspondente à sua etapa. Uma tarefa que nunca chega a ser criada (um 4xx no momento do pedido, incluindo uma rejeição de moderation) não é cobrada de todo. Uma tarefa que atinge FAILED devolve 0 — o valor é reembolsado, incluindo um bloqueio de moderation assíncrono. O cancelamento via DELETE só reembolsa enquanto a tarefa ainda estiver PENDING; uma tarefa já IN_PROGRESS mantém-se cobrada, porque o trabalho já foi realizado.
Name
model_urls
Type
object
Description
URLs transferíveis para os artefactos do modelo gerado. Tanto o GLB como o pacote OBJ são exportados à escala real em milímetros, com o eixo Y para cima, com a frente do keycap virada para +Z. As malhas têm o nome keycap-head e keycap-base; quando a base recorre a um preenchimento em padrão, está também presente uma terceira malha keycap-base-interior para a cavidade da haste. Não presuma que existem exatamente duas malhas.
Estes são URLs assinados: obtenha-os sem um cabeçalho Authorization. Permanecem válidos até expires_at, que corresponde a 3 dias após finished_at, e reler a tarefa dentro desse período devolve o mesmo URL, e não um novo URL assinado. Transfira e guarde os ficheiros por si mesmo antes disso — não há forma de renovar uma ligação expirada.
Name
glb
Type
string
Description
URL transferível para o model.glb texturizado final.
Name
obj_zip
Type
string
Description
URL transferível para um pacote zip que contém model.obj, model.mtl, e os PNGs de textura que o respetivo MTL efetivamente referencia. Uma base de cor sólida inclui apenas keycap-head.png; uma base com padrão inclui também keycap-base.png.
Name
process_image_urls
Type
object
Description
URLs transferíveis para imagens intermédias do processo, organizadas por tipo. O mesmo ciclo de vida de URL que model_urls: assinados, sem cabeçalho Authorization, válidos até expires_at, e estáveis ao reler a tarefa. Tipos atualmente emitidos:
head_design — a imagem de design do candidato escolhido que a build consumiu (sempre presente).
composite — o render de apresentação do keycap finalizado do candidato escolhido (presente quando disponível).
base_canvas — a tela pintada da base do keycap (presente quando disponível).
Considere o conjunto de chaves como aberto; podem ser adicionados novos tipos sem constituir uma alteração disruptiva.
O fluxo completo: criar um protótipo a partir de uma fotografia, consultá-lo (poll) até
SUCCEEDED, escolher um candidato a partir de candidate_ids, criar uma build com
esse candidato, consultar (poll) a build até SUCCEEDED e depois transferir o GLB e
o pacote OBJ a partir de model_urls.
O exemplo escolhe programaticamente o primeiro candidato. Numa integração
real, mostraria a entrada image_urls ao utilizador final e deixaria-o
escolher; 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"