Creative Lab — Keycap API

Превратите исходную фотографию в полноцветный кастомный колпачок механической клавиатуры в два этапа: prototype генерирует рендер дизайна «готового колпачка» на основе вашей исходной фотографии. После того как вы подтвердили этот рендер, build превращает его в текстурированную 3D-модель колпачка за один запуск — генерация белой модели, автоматическая посадка и обрезка по калиброванной позе по умолчанию, полная раскраска модели и финальная сборка — всё это происходит в рамках одной задачи build. Оба этапа связаны через input_task_id и candidate_id.

  • POST /openapi/creative-lab/keycap/v1/prototype
  • POST /openapi/creative-lab/keycap/v1/build

POST/openapi/creative-lab/keycap/v1/prototype

Создать задачу прототипа кейкапа

Сгенерируйте рендер готового дизайна кейкапа из исходной фотографии. Результат задачи содержит массив image_urls (отображаемый рендер готового кейкапа) и параллельный массив candidate_ids; оба содержат по одной записи. Вызовите этот эндпоинт снова для другого рендера, если результат вас не устраивает — каждый вызов оплачивается отдельно. Передайте candidate_id вместе с ID задачи прототипа в эндпоинт сборки. Обратитесь к разделу Объект задачи прототипа кейкапа для получения информации о структуре ответа.

Параметры

  • Name
    image_url
    Type
    string
    Обязательный
    Description

    Исходная фотография, из которой Meshy создаст изображения дизайна кейкапа. В настоящее время поддерживаются форматы .jpg, .jpeg, .png и .webp.

    Формат определяется путём декодирования данных изображения, а не по расширению файла в URL — URL без расширения или URL с редиректом также подходит, если байты декодируются в поддерживаемый формат. HTTP-редиректы обрабатываются автоматически. Ориентация EXIF нормализуется, так что повёрнутое фото с телефона используется в том виде, в котором оно выглядит.

    Ограничения: минимум 32 пикселя с каждой стороны, максимум 178 956 970 пикселей в общей сложности и максимум 20 000 000 байт после загрузки. Для Data URI ограничение применяется к декодированным байтам, поэтому сам исходный файл может быть вплоть до этого размера — именно base64-текст примерно на треть больше, что имеет значение для тела вашего запроса, но не для этого ограничения. Data URI должен указывать тип содержимого image/* и ;base64.

    Есть два способа предоставить изображение:

    • Публично доступный URL: URL, доступный из публичного интернета.
    • Data URI: изображение, закодированное в base64 в виде data URI. Пример data URI: data:image/jpeg;base64,<ваши закодированные в base64 данные изображения>.
  • Name
    name
    Type
    string
    Description

    Необязательное имя задачи для отображения. Максимум 100 символов.

  • Name
    remove_background
    Type
    boolean
    по умолчанию false
    Description

    Если установлено значение true, отображаемый рендер, возвращаемый в image_urls, представляет собой прозрачный PNG в формате RGBA с удалённым фоном, что позволяет вам скомпоновать его с любым фоном.

    Это относится только к отображаемому рендеру. Кандидат, используемый эндпоинтом сборки, не затрагивается, поэтому 3D-результат в любом случае одинаков.

Возвращаемые значения

Свойство result ответа содержит id задачи только что созданной задачи прототипа кейкапа. Опрашивайте эндпоинт Получить задачу или подпишитесь на поток, пока задача не достигнет статуса SUCCEEDED, затем возьмите запись из candidate_ids и передайте её вместе с ID задачи в эндпоинт сборки.

Режимы сбоев

  • Name
    400 - Bad Request
    Description

    Запрос был некорректен. Распространённые причины:

    • Отсутствует параметр: требуется image_url.
    • Недопустимый формат изображения: предоставленный image_url имеет неподдерживаемый формат (.jpg, .jpeg, .png, .webp).
    • Размеры изображения вне допустимого диапазона: изображение слишком маленькое, превышает максимальный размер файла или превышает максимальное количество пикселей.
    • Недоступный URL: не удалось загрузить image_url (404 или timeout).
    • Недопустимый Data URI: строка base64 имеет неверный формат.
    • Содержимое отмечено: входное изображение было отмечено системой moderation NSFW-контента.
  • Name
    401 - Unauthorized
    Description

    Ошибка аутентификации. Пожалуйста, проверьте ваш API-ключ.

  • Name
    402 - Payment Required
    Description

    Аккаунт находится на бесплатном плане (для создания задач требуется платный план) или имеет недостаточно кредитов.

  • Name
    403 - Forbidden
    Description

    Входное изображение было отмечено системой moderation интеллектуальной собственности.

  • Name
    429 - Too Many Requests
    Description

    Вы превысили ограничение частоты.

  • Name
    500 - Internal Server Error
    Description

    Произошла непредвиденная ошибка на стороне сервера — например, служба модерации контента была недоступна, не удалось выполнить подготовку входного изображения, либо не удалось создать задачу. В этом случае задача не создаётся, поэтому повторный запрос безопасен.

Request

POST
/openapi/creative-lab/keycap/v1/prototype
# Stage 1: generate a finished-keycap design render
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype \
  -X POST \
  -H "Authorization: Bearer ${YOUR_API_KEY}" \
  -H 'Content-Type: application/json' \
  -d '{
    "image_url": "<your publicly accessible image url or base64-encoded data URI>"
  }'

Response

{
  "result": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef"
}

POST/openapi/creative-lab/keycap/v1/build

Создание задачи сборки Keycap

Генерирует финальную текстурированную 3D-модель keycap на основе успешно выполненной задачи прототипирования и одного из её кандидатов. Одна задача сборки выполняет весь конвейер от начала до конца — генерацию белой модели из выбранного дизайна, автоматическую посадку и обрезку на основании keycap с использованием откалиброванной позы по умолчанию (интерактивная настройка не требуется), полноцветную окраску модели, а также финальную сборку и экспорт. Сборка обычно занимает 3–7 минут, ближе к верхней границе при одновременном выполнении нескольких сборок. Форму ответа смотрите в разделе Объект задачи сборки Keycap.

Параметры

  • Name
    input_task_id
    Type
    string
    Обязательный
    Description

    ID задачи прототипирования, созданной через этот же эндпоинт OpenAPI. Прототип должен быть создан той же учётной записью Meshy, должен находиться в статусе SUCCEEDED и должен содержать хотя бы одного кандидата.

    Задачи прототипирования, созданные через веб-приложение, не принимаются — эндпоинт сборки принимает только задачи прототипирования, созданные через POST /openapi/creative-lab/keycap/v1/prototype, и отклоняет любой другой источник с кодом 404.

  • Name
    candidate_id
    Type
    string
    Обязательный
    Description

    Кандидат для сборки, взятый из массива candidate_ids успешно завершённой задачи прототипирования. Должен принадлежать этой задаче; любое другое значение будет отклонено с кодом 400.

  • Name
    name
    Type
    string
    Description

    Необязательное имя задачи для отображения. Максимум 100 символов.

options

Необязательная настройка геометрии. Каждое поле имеет откалиброванное значение по умолчанию — отправляйте только те, которые хотите переопределить.

  • Name
    base_model
    Type
    string
    по умолчанию cherry-mx-1x1-r1
    Description

    Основание keycap, на котором выполняется сборка. В настоящее время единственное доступное значение — cherry-mx-1x1-r1 — стандартный keycap профиля Cherry MX 1u. Планируется добавить 3–5 дополнительных распространённых стандартных размеров; пользовательские размеры не поддерживаются.

  • Name
    head_size_mm
    Type
    number
    по умолчанию 23
    Description

    Целевой размер скульптурной головки в миллиметрах: её наибольший размер масштабируется до этого значения. Диапазон: [10, 40]. Значения выше примерно 32.9 могут быть уменьшены, чтобы головка вписывалась в ограничение защитной площади основания, поэтому фактически полученный наибольший размер может быть меньше запрошенного. Применённое значение пока не возвращается в объекте задачи — если вам нужно подтвердить фактически полученный размер, измерьте ограничивающий параллелепипед сетки keycap-head в загруженной модели.

  • Name
    vertical_offset_mm
    Type
    number
    по умолчанию 0
    Description

    Вертикальное смещение, применяемое к головке перед её посадкой на основание, в миллиметрах. Диапазон: [-5, 5].

Возвращаемое значение

Свойство result ответа содержит id задачи новой созданной задачи сборки keycap. Опрашивайте эндпоинт Получение задачи или подпишитесь на поток, пока задача не перейдёт в статус SUCCEEDED, а затем скачайте артефакты из model_urls.glb и model_urls.obj_zip.

Режимы сбоя

  • Name
    400 - Bad Request
    Description

    Запрос был неприемлемым. Распространённые причины:

    • Отсутствует параметр: input_task_id и candidate_id обязательны.
    • Недопустимый UUID: input_task_id не является допустимым UUID.
    • Родительская задача не завершена успешно: указанная задача прототипирования ещё не достигла статуса SUCCEEDED.
    • Нет кандидатов: задача прототипирования завершилась успешно, но не создала ни одного кандидата.
    • Неизвестный кандидат: candidate_id не является одним из кандидатов исходной задачи.
    • Параметры вне допустимого диапазона: одно из полей options вышло за пределы допустимого диапазона или набора значений enum.
  • Name
    401 - Unauthorized
    Description

    Ошибка аутентификации. Проверьте свой API-ключ.

  • Name
    402 - Payment Required
    Description

    Учётная запись использует бесплатный тарифный план (для создания задач требуется платный план) или на ней недостаточно кредитов.

  • Name
    404 - Not Found
    Description

    Указанная задача прототипирования не существует, принадлежит другому пользователю или была создана через веб-приложение (только задачи прототипирования, созданные в режиме API, могут использоваться для сборки).

  • Name
    429 - Too Many Requests
    Description

    Вы превысили ограничение частоты запросов.

  • Name
    500 - Internal Server Error
    Description

    Произошла непредвиденная ошибка на стороне сервера — например, сервис модерации контента был недоступен, не удалось подготовить входное изображение, или задачу не удалось создать. В этом случае задача не создаётся, поэтому повторный запрос безопасен.

Request

POST
/openapi/creative-lab/keycap/v1/build
# Stage 2: build the chosen candidate into a 3D keycap
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build \
  -X POST \
  -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
    }
  }'

Response

{
  "result": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af"
}

GET/openapi/creative-lab/keycap/v1/(prototype|build)/:id

Получение задачи Keycap

Получение задачи прототипа или сборки по действительному id задачи. Путь URL должен соответствовать этапу задачи — если задача сборки запрашивается через /prototype/:id, возвращается 404, и наоборот.

Форматы ответов см. в разделах Объект задачи прототипа Keycap и Объект задачи сборки Keycap.

Параметры

  • Name
    id
    Type
    path
    Description

    Уникальный идентификатор задачи keycap, которую нужно получить.

Возвращаемые значения

Ответ содержит объект задачи keycap. Его структура зависит от того, какой этап был запрошен.

Request

GET
/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef
# Prototype
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

# Build
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Prototype Response

{
  "id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
  "type": "creative-lab-keycap-prototype",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1753142456000,
  "started_at": 1753142460000,
  "finished_at": 1753142516000,
  "expires_at": 1753401716000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 12,
  "image_urls": [
    "https://assets.meshy.ai/***/design-1.png?Expires=***"
  ],
  "candidate_ids": [
    "0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
  ]
}

Build Response

{
  "id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
  "type": "creative-lab-keycap-build",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1753142600000,
  "started_at": 1753142610000,
  "finished_at": 1753143050000,
  "expires_at": 1753402250000,
  "preceding_tasks": 0,
  "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=***",
    "composite": "https://assets.meshy.ai/***/composite.png?Expires=***",
    "base_canvas": "https://assets.meshy.ai/***/base-canvas.png?Expires=***"
  }
}

DELETE/openapi/creative-lab/keycap/v1/(prototype|build)/:id

Удаление задачи Keycap

Отменяет задачу keycap. Если задача всё ещё находится в состоянии PENDING, кредиты, списанные при создании, возвращаются. Задачи, уже находящиеся в состоянии IN_PROGRESS, отменяются без возврата средств (воркер, возможно, уже расходует ресурсы). Задачи, уже достигшие конечного состояния (SUCCEEDED, FAILED, CANCELED), отменить нельзя.

Путь URL должен соответствовать стадии задачи — DELETE для /prototype/:buildId вернёт 404.

Параметры пути

  • Name
    id
    Type
    path
    Description

    Уникальный идентификатор задачи keycap, которую нужно отменить.

Возвращаемое значение

В случае успеха возвращает 204 No Content с пустым телом.

Возможные ошибки

  • Name
    400 - Bad Request
    Description

    Задача уже находится в конечном состоянии и не может быть отменена.

  • Name
    404 - Not Found
    Description

    Задача не существует, принадлежит другому пользователю, либо её стадия не соответствует пути URL.

  • Name
    500 - Internal Server Error
    Description

    При отмене произошла непредвиденная ошибка на стороне сервера. Задача могла быть отменена, а могла и нет — прежде чем повторять попытку, перечитайте её, чтобы убедиться в результате.

Request

DELETE
/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef
curl --request DELETE \
  --url https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Response

// Returns 204 No Content on success (empty body).

GET/openapi/creative-lab/keycap/v1/(prototype|build)/:id/stream

Потоковая передача задачи Keycap

Транслирует обновления задачи keycap в реальном времени через Server-Sent Events (SSE). Путь URL должен соответствовать этапу задачи — открытие потока по адресу /prototype/:buildId/stream приводит к отправке единственной полезной нагрузки event: error с status_code: 404 и закрытию потока.

Параметры

  • Name
    id
    Type
    path
    Description

    Уникальный идентификатор задачи keycap для потоковой передачи.

Возвращает

Возвращает поток объектов задачи Keycap Prototype или Keycap Build в виде Server-Sent Events. Каждый кадр содержит полный объект задачи для данного этапа — в том же формате, что возвращает эндпоинт Get, — поэтому пока задача находится в статусе PENDING или IN_PROGRESS, выходные поля просто ещё не заполнены (null, [] или {}), а finished_at равно null.

Request

GET
/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/stream
curl -N https://api.meshy.ai/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/stream \
-H "Authorization: Bearer ${YOUR_API_KEY}"

Response Stream

// Error event example (wrong stage or task not found)
event: error
data: {
  "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: message
data: {
  "id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
  "progress": 0,
  "status": "PENDING"
}

event: message
data: {
  "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=***"
  }
}

GET/openapi/creative-lab/keycap/v1/(prototype|build)

Список задач Keycap

Получить постраничный список ваших задач keycap для одного этапа. Путь URL определяет этап — /prototype возвращает задачи прототипа; /build возвращает задачи сборки. Задачи из другого этапа не включаются ни в один из ответов.

Параметры пути

  • Name
    stage
    Type
    path
    Обязательный
    Description

    Либо prototype, либо build. Коллекция возвращает только задачи, этап которых соответствует URL — запрос к /prototype никогда не возвращает задачи сборки, и наоборот.

Параметры запроса

  • Name
    page_num
    Type
    integer
    по умолчанию 1
    Description

    Номер страницы для пагинации.

  • Name
    page_size
    Type
    integer
    по умолчанию 10
    Description

    Ограничение размера страницы. Максимально допустимое значение — 100 элементов.

  • Name
    sort_by
    Type
    string
    по умолчанию -created_at
    Description

    Поле для сортировки. Доступные значения:

    • +created_at: Сортировка по времени создания в порядке возрастания.
    • -created_at: Сортировка по времени создания в порядке убывания.

Возвращает

Возвращает постраничный список объектов задач для соответствующего этапа — либо объект задачи прототипа keycap при получении списка /prototype, либо объект задачи сборки keycap при получении списка /build.

Request

GET
/openapi/creative-lab/keycap/v1/prototype
# List prototype tasks
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype?page_size=10 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

# List build tasks
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build?page_size=10 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Response (List Prototype Tasks)

[
  {
    "id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
    "type": "creative-lab-keycap-prototype",
    "name": "",
    "status": "SUCCEEDED",
    "progress": 100,
    "created_at": 1753142456000,
    "started_at": 1753142460000,
    "finished_at": 1753142516000,
    "expires_at": 1753401716000,
    "preceding_tasks": 0,
    "task_error": null,
    "consumed_credits": 12,
    "image_urls": [
      "https://assets.meshy.ai/***/design-1.png?Expires=***"
    ],
    "candidate_ids": [
      "0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
    ]
  }
]

Объект задачи прототипа кейкапа

Объект задачи прототипа кейкапа — это единица работы, которую Meshy отслеживает для генерации одного изображения готового дизайна кейкапа из исходной фотографии. Результат этого этапа передаётся дальше на этап сборки через input_task_id и candidate_id.

Свойства

  • Name
    id
    Type
    string
    Description

    Уникальный идентификатор задачи. Хотя в качестве деталей реализации мы используем k-сортируемый UUID для идентификаторов задач, вам не следует делать никаких предположений о формате id.

  • Name
    type
    Type
    string
    Description

    Тип задачи. Значение — creative-lab-keycap-prototype.

  • Name
    name
    Type
    string
    Description

    Название задачи, указанное при её создании. Пустая строка, если название не было указано.

  • Name
    status
    Type
    string
    Description

    Статус задачи. Возможные значения: одно из PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.

  • Name
    progress
    Type
    integer
    Description

    Прогресс задачи. Если задача ещё не начата, это свойство будет равно 0. Когда задача завершится успешно, оно станет 100.

  • Name
    created_at
    Type
    timestamp
    Description

    Временная метка создания задачи, в миллисекундах.

  • Name
    started_at
    Type
    timestamp
    Description

    Временная метка начала выполнения задачи, в миллисекундах. Если задача ещё не начата, это свойство будет равно 0.

  • Name
    finished_at
    Type
    timestamp
    Description

    Временная метка завершения задачи, в миллисекундах. Если задача ещё не завершена, это свойство будет равно 0.

  • Name
    expires_at
    Type
    timestamp
    Description

    Временная метка истечения срока действия результата задачи, в миллисекундах.

  • Name
    preceding_tasks
    Type
    integer
    Description

    Количество предшествующих задач.

  • Name
    task_error
    Type
    object
    Description

    Детали ошибки для неудавшихся задач. См. Ошибки для полного описания объекта task_error.

  • Name
    consumed_credits
    Type
    integer
    Description

    Количество кредитов, потреблённых этой задачей. С задачи, достигшей SUCCEEDED, взимается полная плата за её этап. Задача, которая так и не была создана (4xx на момент запроса, включая отказ модерации), не оплачивается вовсе. Задача, достигшая FAILED, возвращает 0 — плата возвращается, включая случай асинхронной блокировки модерацией. Отмена через DELETE возвращает плату только пока задача ещё находится в статусе PENDING; задача, уже находящаяся в статусе IN_PROGRESS, остаётся оплаченной, так как работа уже выполнена.

  • Name
    image_urls
    Type
    array of strings
    Description

    Загружаемый URL рендера готового дизайна кейкапа — как выглядит кандидат в виде готового кейкапа. Содержит одну запись; image_urls[i] соответствует candidate_ids[i]. Пусто, пока задача не достигнет SUCCEEDED. URL предназначен только для отображения; эндпоинт сборки использует candidate_ids, а не эти URL. Тот же жизненный цикл URL, что и у model_urls: подписан, без заголовка Authorization, действителен до expires_at и остаётся стабильным при повторном чтении задачи.

  • Name
    candidate_ids
    Type
    array of strings
    Description

    Непрозрачные идентификаторы кандидатов, параллельные image_urls. Передайте запись, соответствующую выбранному вами дизайну, в качестве candidate_id запроса сборки. Не делайте никаких предположений о формате этих id.

Example Keycap Prototype Task Object

{
  "id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
  "type": "creative-lab-keycap-prototype",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1753142456000,
  "started_at": 1753142460000,
  "finished_at": 1753142516000,
  "expires_at": 1753401716000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 12,
  "image_urls": [
    "https://assets.meshy.ai/***/design-1.png?Expires=***"
  ],
  "candidate_ids": [
    "0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
  ]
}

Объект Keycap Build Task

Объект Keycap Build Task — это рабочая единица, которую Meshy отслеживает для генерации финального текстурированного 3D-колпачка клавиши на основе успешно завершённой задачи прототипа и выбранного кандидата. Один билд запускает полный конвейер — генерацию белой модели, автоматическую посадку и обрезку, окраску, сборку и экспорт.

Свойства

  • Name
    id
    Type
    string
    Description

    Уникальный идентификатор задачи.

  • Name
    type
    Type
    string
    Description

    Тип задачи. Значение — creative-lab-keycap-build.

  • Name
    name
    Type
    string
    Description

    Название задачи, указанное при её создании. Пустая строка, если название не было указано.

  • Name
    status
    Type
    string
    Description

    Статус задачи. Возможные значения: PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.

  • Name
    progress
    Type
    integer
    Description

    Прогресс выполнения задачи. Если задача ещё не начата, это свойство будет равно 0. Как только задача успешно завершится, значение станет 100.

  • Name
    created_at
    Type
    timestamp
    Description

    Временная метка создания задачи, в миллисекундах.

  • Name
    started_at
    Type
    timestamp
    Description

    Временная метка начала выполнения задачи, в миллисекундах.

  • Name
    finished_at
    Type
    timestamp
    Description

    Временная метка завершения задачи, в миллисекундах.

  • Name
    expires_at
    Type
    timestamp
    Description

    Временная метка истечения срока действия результата задачи, в миллисекундах.

  • Name
    preceding_tasks
    Type
    integer
    Description

    Количество предшествующих задач. Имеет значение только когда статус — PENDING.

  • Name
    task_error
    Type
    object
    Description

    Детали ошибки для неудачных задач. См. Ошибки для полного описания объекта task_error.

  • Name
    consumed_credits
    Type
    integer
    Description

    Количество кредитов, потраченных на эту задачу. За задачу, достигшую статуса SUCCEEDED, взимается полная стоимость её этапа. За задачу, которая так и не была создана (ошибка 4xx на этапе запроса, включая отклонение модерацией), плата не взимается вовсе. Задача, достигшая статуса FAILED, возвращает 0 — списание возвращается, включая случай асинхронной блокировки модерацией. Отмена через DELETE возвращает средства только пока задача ещё находится в статусе PENDING; за задачу, уже находящуюся в статусе IN_PROGRESS, плата сохраняется, поскольку работа уже была выполнена.

  • Name
    model_urls
    Type
    object
    Description

    Ссылки для скачивания сгенерированных артефактов модели. И GLB, и пакет OBJ экспортируются в реальном масштабе в миллиметрах, с осью Y вверх, при этом лицевая сторона колпачка направлена по оси +Z. Сетки называются keycap-head и keycap-base; когда основание переключается на заливку узором, также присутствует третья сетка keycap-base-interior для полости штока. Не предполагайте, что всегда ровно две сетки.

    Это подписанные ссылки: запрашивайте их без заголовка Authorization. Они остаются действительными до expires_at, то есть 3 дня после finished_at, и повторное чтение задачи в течение этого периода возвращает ту же самую ссылку, а не заново подписанную. Скачайте и сохраните файлы самостоятельно до истечения этого срока — способа обновить истёкшую ссылку не существует.

    • Name
      glb
      Type
      string
      Description

      Ссылка для скачивания финального текстурированного файла model.glb.

    • Name
      obj_zip
      Type
      string
      Description

      Ссылка для скачивания zip-архива, содержащего model.obj, model.mtl и PNG-текстуры, на которые реально ссылается его MTL-файл. Однотонное основание поставляется только с keycap-head.png; узорчатое основание также поставляется с keycap-base.png.

  • Name
    process_image_urls
    Type
    object
    Description

    Ссылки для скачивания промежуточных изображений процесса, с ключами по типу. Тот же жизненный цикл ссылок, что и у model_urls: подписанные, без заголовка Authorization, действительны до expires_at и стабильны при повторном чтении задачи. Типы, генерируемые в настоящее время:

    • head_design — изображение дизайна выбранного кандидата, использованное при билде (присутствует всегда).
    • composite — финальный рендер готового колпачка выбранного кандидата (присутствует, если доступен).
    • base_canvas — окрашенный холст основания колпачка (присутствует, если доступен).

    Считайте набор ключей открытым; новые типы могут быть добавлены без критических изменений.

Example Keycap Build Task Object

{
  "id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
  "type": "creative-lab-keycap-build",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1753142600000,
  "started_at": 1753142610000,
  "finished_at": 1753143050000,
  "expires_at": 1753402250000,
  "preceding_tasks": 0,
  "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=***",
    "composite": "https://assets.meshy.ai/***/composite.png?Expires=***",
    "base_canvas": "https://assets.meshy.ai/***/base-canvas.png?Expires=***"
  }
}

Полный пример

Полный процесс: создание прототипа из фотографии, опрос его состояния до SUCCEEDED, выбор кандидата из candidate_ids, создание билда с этим кандидатом, опрос билда до SUCCEEDED, а затем загрузка GLB и пакета OBJ из model_urls.

В примере программно выбирается первый кандидат. В реальной интеграции вы бы показывали записи image_urls конечному пользователю и давали ему возможность выбрать; выбранный индекс сопоставляется 1:1 с candidate_ids.

Complete flow

POST
/openapi/creative-lab/keycap/v1
#!/usr/bin/env bash
set -euo pipefail

# 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:-}" ]]; then
  echo "export IMAGE_PATH (local file) or IMAGE_URL (public url) first" >&2
  exit 1
fi

BASE="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 body
  shift 2
  out=$(curl --silent --show-error --max-time 60 --write-out $'\n%{http_code}' \
    -X "$method" "$url" -H "$AUTH" "$@") || return 1
  http_code=${out##*$'\n'}
  body=${out%$'\n'*}
  if ((http_code >= 400)); then
    echo "HTTP $http_code for $url: $body" >&2
    return 1
  fi
  printf '%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 :; do
    if (($(date +%s) >= deadline)); then
      echo "gave up waiting for $kind $id" >&2
      return 1
    fi
    task_status=$(api GET "$BASE/$kind/$id" | jq -r '.status')
    echo "$kind: $task_status"
    case "$task_status" in
    SUCCEEDED) return 0 ;;
    FAILED | CANCELED) return 1 ;;
    esac
    sleep "$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"' EXIT
if [[ -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')" in
    png) 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"
else
  printf '{"image_url":"%s"}' "$IMAGE_URL" >"$BODY"
fi

# 1. Create the prototype task
PROTO_ID=$(api POST "$BASE/prototype" \
  -H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')

# 2. Wait for the design render
poll prototype "$PROTO_ID"

# 3. Pick a candidate (first one here; show image_urls to a user in production)
CANDIDATE_ID=$(api GET "$BASE/prototype/$PROTO_ID" | jq -r '.candidate_ids[0]')

# 4. Create the build task
jq -n --arg p "$PROTO_ID" --arg c "$CANDIDATE_ID" \
  '{input_task_id: $p, candidate_id: $c}' >"$BODY"
BUILD_ID=$(api POST "$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)
poll build "$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=$(api GET "$BASE/build/$BUILD_ID")
curl --silent --show-error --fail --max-time 900 \
  -o keycap.glb "$(jq -r '.model_urls.glb' <<<"$TASK")"
curl --silent --show-error --fail --max-time 900 \
  -o keycap-obj.zip "$(jq -r '.model_urls.obj_zip' <<<"$TASK")"
echo "Done: keycap.glb + keycap-obj.zip"