Прежде чем Вы начнёте
API работает без аккаунта через гостевую сессию. API находится по собственному адресу сайта, в пути /api. Установите его один раз:
API=https://dev.uliketechnology.com/api
F=board.zip
board.zip — это плоский zip с Вашими файлами Gerber и файлом сверловки. Что в него входит, см. в статье Файлы для изготовления и монтажа. Примерам нужны curl и jq.
Каждый запрос к API после гостевой сессии принимает Authorization: Bearer <token>. У каждой ошибки одинаковая форма:
{ "error": { "code": "VALIDATION_FAILED", "message": "…", "details": {}, "requestId": "…" } }
Ветвитесь по code. Поле message — для разработчиков.
- Гостевая сессия: токен показывается один раз.
- Загрузка: отправьте zip методом
PUTна адрес, который называет API, с заголовками, которые он называет. - Завершение, затем опрос, пока анализ не будет готов.
- Отчёт: замечания, какие проверки выполнены, а какие нет.
- Предварительный расчёт стоимости.
Процесс с curl
Прочитайте, что можно заказать. Для этого токен не нужен:
curl -s $API/v1/capabilities | jq '.options[].id'
Откройте гостевую сессию. Токен показывается один раз и действует 30 дней:
T=$(curl -s -X POST $API/v1/guest-sessions | jq -r .token)
Объявите файл. В ответе есть uploadId, designId, uploadUrl и 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\"}")
Отправьте файл на адрес, который называет ответ, с каждым заголовком из requiredHeaders (тип содержимого, длина и контрольная сумма; без них загрузка отклоняется с кодом 403). Затем завершите загрузку:
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
Статус сначала analysing. Читайте проект, пока он не изменится. Когда он будет ready, отчёт находится в ответе:
D=$(echo $J | jq -r .designId)
curl -s $API/v1/designs/$D -H "authorization: Bearer $T" | jq '.design.status, .report.headline, .report.counts'
Заголовок — no_problems_in_checks_run, problems_found или could_not_check. Прочитайте также checksRun и checksNotRun: проверка, которая не выполнилась, — не пройденная. Остальные статусы — needs_mapping (установите слои вызовом PUT /v1/designs/{designId}/layer-mapping), needs_engineer и rejected.
Запросите предварительную цену. Поля selection и destinationCountry обязательны; пустое selection берёт значения по умолчанию:
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 (цена действует как показано) или review (инженер её подтверждает); поле reviewReasons говорит почему. Поле totalMinor — в младших единицах валюты currency ответа и отсутствует, когда инженер должен оценить работу. Чтобы сохранить расчёт стоимости, вызов POST /v1/quotes принимает то же тело.
Тот же процесс с клиентом JavaScript
Клиент @ulike/sdk оборачивает эти маршруты. Он ещё не опубликован как пакет; это его форма, чтобы Вы могли написать те же вызовы с помощью fetch. Имена методов:
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',
});
Остальные методы — createUpload, completeUpload, getDesign, setLayerMapping, validateDesign, getValidation, waitForValidation, createQuote, getQuote и reviseQuote. Ошибки выбрасываются как ApiClientError с полями status и code.
Проверка для заказа
Отчёт, который Вы получаете после загрузки, сделан только по файлам. Чтобы проверить файлы против заказа, вызовите POST /v1/designs/{designId}/validations (validateDesign) с полем order, содержащим quantities и selection. Ответ — состояние: running, failed или done. Пока running, читайте GET /v1/validations/{reportId} (getValidation) каждую секунду или две, пока не будет done.
- Загрузка zip.
- Чтение отчёта.
- Исправление и загрузка как новый проект. Остановитесь и спросите человека, когда исправление меняет функцию, цену или срок поставки.
- Чистые файлы: запустите проверку для заказа.
Расчёт стоимости или предварительный расчёт с опциями, которые приносят другие ограничения (более толстая медь, другая толщина), нуждается в проверке для этого выбора. Если такой нет, одна запускается, и предварительный расчёт несёт причину проверки file_check_pending и поле check с его reportId. Цена показывается сразу, но расчёт стоимости остаётся в review, пока эта проверка не будет готова. Прочитайте проверку, как выше, затем запросите предварительный расчёт снова.
Ошибки, которые Вы встретите
| Код | Статус | Что это означает | Что делать |
|---|---|---|---|
DESIGN_NOT_READY |
422 | Проект ещё читается (details.status говорит, какое состояние), или у него ещё нет отчёта. |
Читайте проект, пока статус не перестанет быть analysing, затем спросите снова. |
VALIDATION_FAILED |
400 | Запрос не имеет ожидаемой формы или называет опцию или значение, которых нет в каталоге. Поле details перечисляет проблемы. |
Исправьте поле, названное в details, и отправьте снова. Идентификаторы опций и значения берите из GET /v1/capabilities. |
NOT_MANUFACTURABLE |
422 | Выбор — то, что мы не изготавливаем. Поле details.because несёт причины. |
Измените выбор; не повторяйте тот же. Спросите человека, изменит ли изменение плату. |
ANONYMOUS_BUDGET_SPENT |
429 | Дневной лимит без аккаунта израсходован. Поле details.resetsOn — дата. |
Остановитесь. Скажите человеку; он может войти или подождать до этой даты. |
REVISION_CONFLICT |
409 | Ревизия назвала базу, которая больше не голова расчёта стоимости. Поле details несёт обе головы. |
Прочитайте голову вызовом GET /v1/quotes/{quoteId}, повторно примените изменение на ней и отправьте снова. |
Также возможны коды DESIGN_REJECTED (загрузка была отклонена; см. details.reason), UNAUTHENTICATED при отсутствующем или неверном токене (неверный токен отклоняется даже на публичных маршрутах) и FEATURE_DISABLED для маршрута, который ещё не открыт.
Дневной лимит без аккаунта
У гостевой сессии есть дневной лимит вычислений. Загрузки и запущенные проверки стоят больше простого чтения, а открытие многих гостевых сессий с одного сетевого адреса также считается. Проверка, которую сайт запускает сам для нового выбора, списывается на тот же лимит. Когда он израсходован, Вы получаете ANONYMOUS_BUDGET_SPENT с датой его сброса. Вошедшие вызывающие так не считаются.
Что API не может сегодня: заказ, оплата, ключи API и вход для агентов запланированы и недоступны. Запланированный сервер MCP описан в статье Что такое MCP и как заказывают агенты.