Antes de começar
A API funciona sem conta por meio de uma sessão de convidado. A API fica no endereço do próprio site sob /api. Defina-o uma vez:
API=https://dev.uliketechnology.com/api
F=board.zip
board.zip é um zip simples com os seus arquivos Gerber e de furação. Sobre o que entra nele, veja Arquivos para fabricação e montagem. Os exemplos precisam de curl e jq.
Toda solicitação à API depois da sessão de convidado leva Authorization: Bearer <token>. Todo erro tem a mesma forma:
{ "error": { "code": "VALIDATION_FAILED", "message": "…", "details": {}, "requestId": "…" } }
Ramifique pelo code. A message é para desenvolvedores.
- Sessão de convidado: o token é exibido uma vez.
- Upload: envie o zip com
PUTpara o endereço que a API indicar, com os cabeçalhos que ela indicar. - Conclua, depois consulte até a análise terminar.
- O relatório: apontamentos, quais verificações foram realizadas e quais não foram.
- A prévia de orçamento.
O fluxo com curl
Leia o que pode ser pedido. Isso não precisa de token:
curl -s $API/v1/capabilities | jq '.options[].id'
Abra uma sessão de convidado. O token é exibido uma vez e dura 30 dias:
T=$(curl -s -X POST $API/v1/guest-sessions | jq -r .token)
Anuncie o arquivo. A resposta tem uploadId, designId, uploadUrl e requiredHeaders:
J=$(curl -s -X POST $API/v1/uploads -H "authorization: Bearer $T" -H 'content-type: application/json' \
-d "{\"fileName\":\"board.zip\",\"sizeBytes\":$(stat -c %s $F),\"sha256\":\"$(sha256sum $F | cut -d' ' -f1)\",\"purpose\":\"design\"}")
Envie o arquivo para o endereço que a resposta indicar, com todos os cabeçalhos em requiredHeaders (tipo de conteúdo, comprimento e checksum; sem eles o upload é recusado com 403). Depois conclua o upload:
curl -s -X PUT --data-binary @$F \
$(echo $J | jq -r '.requiredHeaders | to_entries[] | "-H \(.key):\(.value)"') \
"$(echo $J | jq -r .uploadUrl)"
curl -s -X POST $API/v1/uploads/$(echo $J | jq -r .uploadId)/complete -H "authorization: Bearer $T" | jq .design.status
O status é analysing no início. Leia o projeto até ele mudar. Quando for ready, o relatório está na resposta:
D=$(echo $J | jq -r .designId)
curl -s $API/v1/designs/$D -H "authorization: Bearer $T" | jq '.design.status, .report.headline, .report.counts'
O título é no_problems_in_checks_run, problems_found ou could_not_check. Leia também checksRun e checksNotRun: uma verificação que não foi realizada não é uma aprovação. Os outros status são needs_mapping (defina as camadas com PUT /v1/designs/{designId}/layer-mapping), needs_engineer e rejected.
Peça uma prévia de preço. selection e destinationCountry são obrigatórios; um selection vazio usa os padrões:
curl -s -X POST $API/v1/quote-previews -H "authorization: Bearer $T" -H 'content-type: application/json' \
-d "{\"designId\":\"$D\",\"quantities\":{\"boardsFabricated\":10,\"boardsAssembled\":0},\"selection\":{},\"destinationCountry\":\"DE\"}" \
| jq '.mode, .reviewReasons, .totalMinor'
mode é instant (o preço vale como exibido) ou review (um engenheiro o confirma); reviewReasons diz o motivo. totalMinor está em unidades menores da currency da resposta e fica ausente quando um engenheiro precisa precificar o trabalho. Para salvar um orçamento, POST /v1/quotes recebe o mesmo corpo.
O mesmo fluxo com o cliente JavaScript
O cliente @ulike/sdk envolve essas rotas. Ele ainda não foi publicado como pacote; esta é a sua forma, para que você possa escrever as mesmas chamadas com fetch. Nomes dos métodos:
import { createClient } from '@ulike/sdk';
let token;
const api = createClient({ baseUrl: 'https://dev.uliketechnology.com/api', token: () => token });
const caps = await api.capabilities();
token = (await api.createGuestSession()).token;
const started = await api.uploadDesign({ name: 'board.zip', bytes }); // announce, PUT, complete
const view = await api.waitForDesign(started.design.id); // polls getDesign
const preview = await api.previewQuote({
designId: view.design.id,
quantities: { boardsFabricated: 10, boardsAssembled: 0 },
selection: {},
destinationCountry: 'DE',
});
Os outros métodos são createUpload, completeUpload, getDesign, setLayerMapping, validateDesign, getValidation, waitForValidation, createQuote, getQuote e reviseQuote. Os erros são lançados como ApiClientError com um status e um code.
A verificação para um pedido
O relatório que você recebe após o upload é feito apenas com os arquivos. Para verificar os arquivos em relação a um pedido, chame POST /v1/designs/{designId}/validations (validateDesign) com order contendo quantities e selection. A resposta é um estado: running, failed ou done. Enquanto for running, leia GET /v1/validations/{reportId} (getValidation) a cada um ou dois segundos até ser done.
- Faça upload do zip.
- Leia o relatório.
- Corrija e faça upload como um novo projeto. Pare e pergunte à pessoa quando uma correção alterar a função, o preço ou o prazo de entrega.
- Arquivos limpos: execute a verificação para o pedido.
Um orçamento ou prévia com opções que trazem outros limites (cobre mais espesso, outra espessura) precisa de uma verificação para essa seleção. Se nenhuma existir, uma é iniciada, e a prévia traz o motivo de revisão file_check_pending e um check com seu reportId. O preço é exibido de imediato, mas o orçamento permanece em review até essa verificação terminar. Leia a verificação como acima e depois peça a prévia novamente.
Erros que você encontrará
| Code | Status | O que significa | O que fazer |
|---|---|---|---|
DESIGN_NOT_READY |
422 | O projeto ainda está sendo lido (details.status diz em qual estado), ou ainda não tem relatório. |
Leia o projeto até o status não ser analysing, depois peça novamente. |
VALIDATION_FAILED |
400 | A solicitação não tem a forma esperada, ou indica uma opção ou valor que o catálogo não tem. details lista as questões ou problemas. |
Corrija o campo indicado em details e envie novamente. Use ids e valores de opção de GET /v1/capabilities. |
NOT_MANUFACTURABLE |
422 | A seleção é algo que não fabricamos. details.because traz os motivos. |
Altere a seleção; não tente a mesma novamente. Pergunte à pessoa se a alteração mudaria a placa. |
ANONYMOUS_BUDGET_SPENT |
429 | A cota diária sem conta acabou. details.resetsOn é a data. |
Pare. Avise a pessoa; ela pode entrar ou esperar até essa data. |
REVISION_CONFLICT |
409 | Uma revisão indicou uma base que não é mais o head do orçamento. details traz os dois heads. |
Leia o head com GET /v1/quotes/{quoteId}, reaplique a sua alteração nele e envie novamente. |
Também são possíveis DESIGN_REJECTED (o upload foi recusado; veja details.reason), UNAUTHENTICATED para um token ausente ou incorreto (um token incorreto é recusado mesmo em rotas públicas) e FEATURE_DISABLED para uma rota que ainda não está aberta.
A cota diária sem conta
Uma sessão de convidado tem uma cota diária de computação. Uploads e verificações iniciadas custam mais do que uma leitura simples, e abrir muitas sessões de convidado a partir de um mesmo endereço de rede também é contado. Uma verificação que o site inicia por conta própria para uma nova seleção é cobrada da mesma cota. Quando ela acaba, você recebe ANONYMOUS_BUDGET_SPENT com a data em que ela é reposta. Quem entrou com conta não é contado dessa forma.
O que a API não pode fazer hoje: pedido, pagamento, chaves de API e entrada para agentes estão planejados e não estão disponíveis. O servidor MCP planejado está descrito em O que é MCP e como os agentes pedem.