Creative Lab — 키캡 API

원본 사진을 완전 컬러의 커스텀 기계식 키보드 키캡으로 변환하는 과정은 두 단계로 이루어집니다. prototype 단계는 입력한 사진으로부터 "완성된 키캡" 디자인 렌더를 생성합니다. 해당 렌더를 확인한 후, build 단계는 이를 텍스처가 적용된 3D 키캡 모델로 한 번의 실행으로 변환합니다 — 화이트 모델 생성, 보정된 기본 포즈에서의 자동 시팅 및 커팅, 전체 모델 컬러링, 최종 조립까지 모든 과정이 하나의 빌드 작업 안에서 이루어집니다. 두 단계는 input_task_idcandidate_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 텍스트는 약 3분의 1 정도 더 커지지만, 이는 요청 본문에 관한 문제일 뿐 이 제한과는 무관합니다. Data URI는 image/* 콘텐츠 타입과 ;base64를 명시해야 합니다.

    이미지를 제공하는 방법은 두 가지입니다:

    • 공개적으로 접근 가능한 URL: 공개 인터넷에서 접근 가능한 URL입니다.
    • Data URI: 이미지의 base64 인코딩된 data URI입니다. data URI 예시: data:image/jpeg;base64,<your base64-encoded image data>.
  • Name
    name
    Type
    string
    Description

    표시 목적의 선택적 작업 이름입니다. 최대 100자입니다.

  • Name
    remove_background
    Type
    boolean
    기본값 false
    Description

    true로 설정하면 image_urls에 반환되는 디스플레이 렌더는 배경이 제거된 투명 RGBA PNG가 되어, 원하는 배경 위에 합성할 수 있습니다.

    이는 디스플레이 렌더에만 적용됩니다. 빌드 엔드포인트가 사용하는 candidate에는 영향을 주지 않으므로, 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 문자열의 형식이 올바르지 않습니다.
    • 콘텐츠 플래그 처리됨: 입력 이미지가 NSFW moderation에 의해 플래그 처리되었습니다.
  • 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

    예기치 않은 서버 측 오류가 발생했습니다 — 예를 들어 콘텐츠 moderation 서비스를 사용할 수 없었거나, 입력 이미지를 스테이징하는 데 실패했거나, 작업을 생성할 수 없었던 경우입니다. 이 경우 작업이 생성되지 않으므로 재시도해도 안전합니다.

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

키캡 빌드 태스크 생성

성공한 프로토타입 태스크와 그 후보 중 하나로부터 최종 텍스처가 적용된 3D 키캡 모델을 생성합니다. 하나의 빌드 태스크가 전체 파이프라인을 처음부터 끝까지 실행합니다 — 선택된 디자인으로부터 화이트 모델 생성, 보정된 기본 포즈를 사용한 키캡 베이스로의 자동 안착 및 절단 (인터랙티브 조정 불필요), 전체 모델 컬러링, 최종 조립 및 내보내기까지 포함됩니다. 빌드는 일반적으로 3~7분이 소요되며, 여러 빌드가 동시에 실행될 경우 상한선에 가까워집니다. 응답 형태에 대해서는 키캡 빌드 태스크 객체를 참고하세요.

파라미터

  • Name
    input_task_id
    Type
    string
    필수
    Description

    동일한 OpenAPI 엔드포인트를 통해 생성된 프로토타입 태스크의 태스크 ID입니다. 프로토타입은 동일한 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

    빌드할 키캡 베이스입니다. 현재 사용 가능한 유일한 값은 cherry-mx-1x1-r1 — 표준 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가 포함됩니다. 태스크가 SUCCEEDED에 도달할 때까지 태스크 가져오기 엔드포인트를 폴링하거나 스트림을 구독한 다음, model_urls.glbmodel_urls.obj_zip에서 산출물을 다운로드하세요.

실패 모드

  • Name
    400 - Bad Request
    Description

    요청이 허용되지 않았습니다. 일반적인 원인:

    • 파라미터 누락: input_task_idcandidate_id가 필요합니다.
    • 유효하지 않은 UUID: input_task_id가 유효한 UUID가 아닙니다.
    • 부모가 성공하지 않음: 참조된 프로토타입 태스크가 아직 SUCCEEDED에 도달하지 않았습니다.
    • 후보 없음: 프로토타입 태스크는 성공했지만 후보를 생성하지 못했습니다.
    • 알 수 없는 후보: candidate_id가 입력 태스크의 후보 중 하나가 아닙니다.
    • 옵션 범위 초과: options 필드 중 하나가 허용된 범위나 열거값 집합을 벗어났습니다.
  • 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

    예기치 않은 서버 측 오류가 발생했습니다 — 예를 들어 콘텐츠 moderation 서비스를 사용할 수 없었거나, 입력 이미지를 스테이징하는 데 실패했거나, 태스크를 생성할 수 없었습니다. 이 경우 태스크가 생성되지 않으므로 재시도해도 안전합니다.

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

키캡 작업 조회

유효한 작업 id가 주어지면 프로토타입 또는 빌드 작업을 조회합니다. URL 경로는 해당 작업의 단계와 일치해야 합니다 — /prototype/:id를 통해 조회한 빌드 작업은 404를 반환하며, 반대의 경우도 마찬가지입니다.

응답 형식에 대해서는 키캡 프로토타입 작업 객체키캡 빌드 작업 객체를 참고하세요.

매개변수

  • Name
    id
    Type
    path
    Description

    조회할 키캡 작업의 고유 식별자입니다.

반환 값

응답에는 키캡 작업 객체가 포함됩니다. 그 형식은 요청한 단계에 따라 달라집니다.

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

키캡 작업 삭제

키캡 작업을 취소합니다. 작업이 아직 PENDING 상태라면 생성 시 소모된 크레딧이 환불됩니다. 이미 IN_PROGRESS 상태인 작업은 환불 없이 취소됩니다(워커가 이미 리소스를 소모하고 있을 수 있습니다). 이미 최종 상태(SUCCEEDED, FAILED, CANCELED)에 도달한 작업은 취소할 수 없습니다.

URL 경로는 작업의 단계와 일치해야 합니다 — /prototype/:buildId에 대한 DELETE404를 반환합니다.

경로 매개변수

  • Name
    id
    Type
    path
    Description

    취소할 키캡 작업의 고유 식별자입니다.

반환값

성공 시 빈 본문과 함께 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

Stream a Keycap Task

Server-Sent Events(SSE)를 통해 키캡 작업의 실시간 업데이트를 스트리밍합니다. URL 경로는 작업의 단계와 일치해야 합니다 — /prototype/:buildId/stream에서 스트림을 열면 status_code: 404가 포함된 단일 event: error 페이로드를 내보내고 스트림이 닫힙니다.

매개변수

  • Name
    id
    Type
    path
    Description

    스트리밍할 키캡 작업의 고유 식별자입니다.

반환값

Server-Sent Events로 Keycap Prototype 또는 Keycap Build 작업 객체의 스트림을 반환합니다. 모든 프레임은 해당 단계의 전체 작업 객체를 담고 있으며 — Get 엔드포인트가 반환하는 것과 동일한 형태입니다 — 따라서 작업이 PENDING 또는 IN_PROGRESS 상태인 동안에는 출력 필드가 아직 채워지지 않은 상태(null, [] 또는 {})이며 finished_atnull입니다.

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)

List Keycap Tasks

단일 단계에 대한 키캡 작업의 페이지네이션된 목록을 가져옵니다. 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: 생성 시간 내림차순으로 정렬합니다.

반환값

단계별 작업 객체의 페이지네이션된 목록을 반환합니다 — /prototype을 조회할 때는 키캡 프로토타입 작업 객체, /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_idcandidate_id를 통해 빌드 단계로 연결됩니다.

속성

  • Name
    id
    Type
    string
    Description

    작업의 고유 식별자입니다. 구현 세부사항으로 작업 id에 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

    작업의 progress입니다. 작업이 아직 시작되지 않은 경우 이 속성은 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, moderation 거부 포함)은 전혀 청구되지 않습니다. FAILED에 도달한 작업은 0을 반환합니다 — 비동기 moderation 차단을 포함하여 청구액이 환불됩니다. DELETE를 통한 취소는 작업이 아직 PENDING인 동안에만 환불되며, 이미 IN_PROGRESS 상태인 작업은 작업이 이미 소비되었기 때문에 청구된 상태로 유지됩니다.

  • Name
    image_urls
    Type
    array of strings
    Description

    완성된 키캡 디자인 렌더링의 다운로드 가능한 URL입니다 — 후보가 완성된 키캡으로 어떻게 보이는지를 나타냅니다. 항목이 하나만 있으며, image_urls[i]candidate_ids[i]에 대응합니다. 작업이 SUCCEEDED에 도달하기 전까지는 비어 있습니다. 이 URL은 표시 전용이며, 빌드 엔드포인트는 이 URL이 아니라 candidate_ids를 사용합니다. model_urls와 동일한 URL 수명 주기를 가집니다: 서명되어 있고, 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 빌드 태스크 객체

Keycap 빌드 태스크 객체는 성공한 프로토타입 태스크와 선택된 candidate로부터 최종 텍스처가 적용된 3D keycap을 생성하기 위해 Meshy가 추적하는 작업 단위입니다. 하나의 빌드는 화이트 모델 생성, 자동 시팅 및 커팅, 컬러링, 조립, 익스포트로 이루어진 전체 파이프라인을 실행합니다.

속성

  • 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, moderation 거부 포함)는 전혀 청구되지 않습니다. FAILED에 도달한 태스크는 0을 반환하며, 이는 비동기 moderation 차단을 포함해 요금이 환불됨을 의미합니다. DELETE를 통한 취소는 태스크가 아직 PENDING 상태일 때만 환불되며, 이미 IN_PROGRESS인 태스크는 작업이 이미 소비되었기 때문에 계속 청구된 상태로 유지됩니다.

  • Name
    model_urls
    Type
    object
    Description

    생성된 모델 아티팩트를 다운로드할 수 있는 URL입니다. GLB와 OBJ 번들 모두 실제 세계 밀리미터 단위 스케일, Y-up으로 익스포트되며, keycap의 정면은 +Z 방향을 향합니다. 메시는 keycap-headkeycap-base로 명명되며, 베이스가 패턴 채우기로 대체되는 경우 스템 캐비티를 위한 세 번째 메시 keycap-base-interior도 함께 존재합니다. 메시가 정확히 두 개라고 가정하지 마세요.

    이는 서명된 URL이므로 Authorization 헤더 없이 가져와야 합니다. 이 URL은 expires_at까지 유효하며, 이는 finished_at으로부터 3일 후이고, 그 기간 내에 태스크를 다시 읽으면 새로 서명된 URL이 아니라 동일한 URL이 반환됩니다. 만료되기 전에 파일을 직접 다운로드하여 저장하세요. 만료된 링크를 새로고침할 방법은 없습니다.

    • Name
      glb
      Type
      string
      Description

      최종 텍스처가 적용된 model.glb를 다운로드할 수 있는 URL입니다.

    • Name
      obj_zip
      Type
      string
      Description

      model.obj, model.mtl, 그리고 해당 MTL이 실제로 참조하는 텍스처 PNG 파일들을 포함하는 zip 번들을 다운로드할 수 있는 URL입니다. 단색 베이스는 keycap-head.png만 포함하며, 패턴 베이스는 keycap-base.png도 함께 포함합니다.

  • Name
    process_image_urls
    Type
    object
    Description

    종류별로 키가 지정된 중간 프로세스 이미지를 다운로드할 수 있는 URL입니다. model_urls와 동일한 URL 라이프사이클을 가지며, 서명되어 있고 Authorization 헤더가 필요 없으며, expires_at까지 유효하고, 태스크를 다시 읽어도 값이 안정적으로 유지됩니다. 현재 생성되는 종류는 다음과 같습니다:

    • head_design — 빌드가 소비한, 선택된 candidate의 디자인 이미지입니다(항상 존재).
    • composite — 선택된 candidate의 완성된 keycap 디스플레이 렌더입니다(사용 가능한 경우 존재).
    • base_canvas — 채색된 keycap 베이스 캔버스입니다(사용 가능한 경우 존재).

    이 키 집합은 열려 있는 것으로 간주하세요. 이후 호환성을 깨지 않으면서 새로운 종류가 추가될 수 있습니다.

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 상태가 될 때까지 폴링하고, 마지막으로 model_urls에서 GLB와 OBJ 번들을 다운로드합니다.

이 예제에서는 프로그래밍 방식으로 첫 번째 후보를 선택합니다. 실제 통합에서는 최종 사용자에게 image_urls 항목을 표시하여 직접 선택하도록 하며, 선택된 인덱스는 candidate_ids에 1:1로 매핑됩니다.

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"