Creative Lab — Fidget Pixel API

Turn a source photo into a multi-color 3D-printable pixel-art fidget board in two stages: prototype pixelizes your photo into a pixel-art image, then build samples that image onto a 16×16 or 32×32 grid and turns every pixel into an interlocking square or hexagonal piece, delivered as a single 3MF whose objects carry their colors so a multi-filament slicer prints each piece in the right color. The two stages are linked via input_task_id.

  • POST /openapi/creative-lab/fidget-pixel/v1/prototype
  • POST /openapi/creative-lab/fidget-pixel/v1/build

POST/openapi/creative-lab/fidget-pixel/v1/prototype

Create a Fidget Pixel Prototype Task

Generate a single pixel-art image from the source photo. The returned task ID is what you pass as input_task_id to the build endpoint. Call this endpoint again for another take if the result is not what you want — each call is billed separately. Refer to The Fidget Pixel Prototype Task Object for the response shape.

Parameters

  • Name
    image_url
    Type
    string
    Required
    Description

    Source photo for Meshy to pixelize. We currently support .jpg, .jpeg, .png, and .webp formats.

    The format is detected by decoding the image data, not from the URL's file extension — a URL with no extension, or one that redirects, works as long as the bytes decode to a supported format. HTTP redirects are followed.

    There are two ways to provide the image:

    • Publicly accessible URL: A URL that is accessible from the public internet.
    • Data URI: A base64-encoded data URI of the image. Example of a data URI: data:image/jpeg;base64,<your base64-encoded image data>.
  • Name
    type
    Type
    string
    Required
    Description

    What the photo shows. Selects the pixelization style, so choose deliberately — the two produce visibly different results. Available values:

    • person — the subject is a person (portrait or full body). Produces a chibi-style pixel sprite of the subject.
    • other — anything else: pets, objects, mascots, logos, landscapes. Produces a bead-art style pixel icon of the subject.
  • Name
    name
    Type
    string
    Description

    Optional task name for display purposes. Maximum 100 characters.

Returns

The result property of the response contains the task id of the newly created fidget pixel prototype task. Poll the Get a Task endpoint or subscribe to the stream until the task reaches SUCCEEDED, then pass that ID to the build endpoint as input_task_id.

Failure Modes

  • Name
    400 - Bad Request
    Description

    The request was unacceptable. Common causes:

    • Missing parameter: image_url and type are both required.
    • Invalid type: type must be person or other.
    • Invalid image format: The provided image_url is not a supported format (.jpg, .jpeg, .png, .webp).
    • Image dimensions out of range: The image is too small, exceeds the maximum file size, or exceeds the maximum pixel count.
    • Unreachable URL: The image_url could not be downloaded (404 or timeout).
    • Invalid Data URI: The base64 string is malformed.
    • Content flagged: The input image was flagged by NSFW moderation.
  • Name
    401 - Unauthorized
    Description

    Authentication failed. Please check your API key.

  • Name
    402 - Payment Required
    Description

    Insufficient credits to perform this task, or the API key belongs to a free-plan account.

  • Name
    403 - Forbidden
    Description

    The input image was flagged by intellectual property moderation (Content flagged for intellectual property violation). Only Enterprise accounts with intellectual property filtering enabled are blocked; nothing is charged.

  • Name
    429 - Too Many Requests
    Description

    You have exceeded your rate limit.

  • Name
    500 - Internal Server Error
    Description

    The intellectual property check itself could not be completed (Unable to perform intellectual property check, please try again). Enterprise accounts with intellectual property filtering enabled fail closed on this check; nothing is charged — retry the request.

Request

POST
/openapi/creative-lab/fidget-pixel/v1/prototype
# Stage 1: pixelize the source photo
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/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>",
    "type": "person"
  }'

Response

{
  "result": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7"
}

POST/openapi/creative-lab/fidget-pixel/v1/build

Create a Fidget Pixel Build Task

Generate the 3D-printable pieces from a succeeded prototype task. The build samples the prototype's pixel-art image onto the requested grid, quantizes it to at most color_count colors, and generates one interlocking piece per grid cell. The deliverable is a single 3MF in which every piece is a separate object tagged with its color, ready for a multi-filament slicer. Refer to The Fidget Pixel Build Task Object for the response shape.

Parameters

  • Name
    input_task_id
    Type
    string
    Required
    Description

    The task ID of a prototype task created via this same OpenAPI endpoint. The prototype must have been created by the same Meshy account and must have reached SUCCEEDED.

    Prototype tasks created through the webapp are not accepted — the build endpoint accepts only prototype tasks produced by POST /openapi/creative-lab/fidget-pixel/v1/prototype and refuses any other source with 404.

  • Name
    name
    Type
    string
    Description

    Optional task name for display purposes. Maximum 100 characters.

options

Optional piece geometry. Every field has a default — send only the ones you want to override. These are the same controls the Creative Lab webapp exposes; plug height, cap scale, and the other manufacturing presets are derived from shape and piece_size_mm and are not exposed.

  • Name
    shape
    Type
    string
    default square
    Description

    Footprint of each piece. Available values:

    • square (default) — square pieces on a square grid.
    • hex — hexagonal pieces on a hexagonal grid. Hex pieces are available in 6 and 8 mm only.
  • Name
    grid_size
    Type
    integer
    default 32
    Description

    Number of pieces along each side of the board. Available values: 16 or 32. A 32 grid keeps more detail; a 16 grid means fewer, larger pieces for the same subject.

  • Name
    piece_size_mm
    Type
    integer
    default 8
    Description

    Edge length of each piece, in millimeters. Available values: 6, 8, or 10. Together with grid_size this sets the printed board size — for example 32 × 8 mm ≈ 26 cm per side. 10 is not available for shape: "hex" (the slanted hex face overhangs on most consumer FDM printers).

  • Name
    color_count
    Type
    integer
    default 8
    Description

    Maximum number of colors in the palette the image is quantized to. Range: [1, 8]. Each color becomes one filament in your slicer.

  • Name
    piece_height_mm
    Type
    integer
    default 15
    Description

    Height of each piece, in millimeters. Range: [10, 80].

output

Optional wire-format selector. Defaults to 3mf, which is currently the only supported value.

  • Name
    format
    Type
    string
    default 3mf
    Description

    Artifact returned by the build. Available values:

    • 3mf (default) — returns a single model.3mf under model_urls.3mf, with one object per piece and the piece color attached to each object.

Returns

The result property of the response contains the task id of the newly created fidget pixel build task. Poll the Get a Task endpoint or subscribe to the stream until the task reaches SUCCEEDED, then download the artifact from model_urls.3mf.

Failure Modes

  • Name
    400 - Bad Request
    Description

    The request was unacceptable. Common causes:

    • Missing parameter: input_task_id is required.
    • Invalid UUID: The input_task_id is not a valid UUID.
    • Parent not succeeded: The referenced prototype task has not reached SUCCEEDED yet.
    • No candidate: The prototype task succeeded but produced no pixel-art image; create a new prototype.
    • Options out of range: One of the options fields is outside its allowed set or range — for example options.grid_size must be 16 or 32, or options.piece_size_mm=10 is not supported for shape=hex; hex pieces are available in 6 and 8 mm.
    • Unsupported format: output.format must be 3mf.
  • Name
    401 - Unauthorized
    Description

    Authentication failed. Please check your API key.

  • Name
    402 - Payment Required
    Description

    Insufficient credits to perform this task, or the API key belongs to a free-plan account.

  • Name
    403 - Forbidden
    Description

    The referenced prototype's image was flagged by intellectual property moderation. Only Enterprise accounts with intellectual property filtering enabled are blocked; nothing is charged.

  • Name
    404 - Not Found
    Description

    The referenced prototype task does not exist, belongs to a different user, or was created through the webapp (only API-mode prototype tasks chain into build).

  • Name
    429 - Too Many Requests
    Description

    You have exceeded your rate limit.

  • Name
    500 - Internal Server Error
    Description

    The referenced prototype's intellectual property verdict could not be established (Unable to perform intellectual property check, please try again). Enterprise accounts with intellectual property filtering enabled fail closed on this check; nothing is charged — retry the request.

Request

POST
/openapi/creative-lab/fidget-pixel/v1/build
# Stage 2: chain build off a succeeded prototype task
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/build \
  -X POST \
  -H "Authorization: Bearer ${YOUR_API_KEY}" \
  -H 'Content-Type: application/json' \
  -d '{
    "input_task_id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
    "options": {
      "shape": "square",
      "grid_size": 32,
      "piece_size_mm": 8,
      "color_count": 8,
      "piece_height_mm": 15
    },
    "output": {
      "format": "3mf"
    }
  }'

Response

{
  "result": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98"
}

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

Retrieve a Fidget Pixel Task

Retrieve a prototype or build task given a valid task id. The URL path must match the task's stage — a build task fetched through /prototype/:id returns 404, and vice versa.

Refer to The Fidget Pixel Prototype Task Object and The Fidget Pixel Build Task Object for response shapes.

Parameters

  • Name
    id
    Type
    path
    Description

    Unique identifier for the fidget pixel task to retrieve.

Returns

The response contains the fidget pixel task object. The shape depends on which stage was requested.

Failure Modes

  • Name
    400 - Bad Request
    Description

    id is not a valid UUID (Invalid ID).

  • Name
    403 - Forbidden
    Description

    The task's image was flagged by intellectual property moderation. Only Enterprise accounts with intellectual property filtering enabled are blocked.

  • Name
    404 - Not Found
    Description

    The task does not exist, belongs to a different user, or its stage does not match the URL path.

  • Name
    500 - Internal Server Error
    Description

    The intellectual property check could not be completed (Unable to perform intellectual property check, please try again); Enterprise accounts with intellectual property filtering enabled fail closed. Retry the request.

Request

GET
/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7
# Prototype
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

# Build
curl https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/build/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Prototype Response

{
  "id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
  "type": "creative-lab-fidget-pixel-prototype",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001000000,
  "started_at": 1757001005000,
  "finished_at": 1757001178000,
  "expires_at": 1757260378000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 6,
  "image_urls": [
    "https://assets.meshy.ai/***/pixel-art.jpg?Expires=***"
  ]
}

Build Response

{
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001300000,
  "started_at": 1757001304000,
  "finished_at": 1757001309000,
  "expires_at": 1757260509000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {
    "3mf": "https://assets.meshy.ai/***/tasks/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/output/model.3mf?Expires=***"
  }
}

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

Delete a Fidget Pixel Task

Cancel a fidget pixel task. If the task is still PENDING, the credits consumed at create-time are refunded. Tasks that are already IN_PROGRESS are cancelled without a refund (the worker may already be burning resources). Tasks that have already reached a terminal state (SUCCEEDED, FAILED, CANCELED) cannot be cancelled.

The URL path must match the task's stage — DELETE on /prototype/:buildId returns 404.

Path Parameters

  • Name
    id
    Type
    path
    Description

    Unique identifier for the fidget pixel task to cancel.

Returns

Returns 204 No Content on success with an empty body.

Failure Modes

  • Name
    400 - Bad Request
    Description

    The request was unacceptable. Common causes:

    • Invalid ID: id is not a valid UUID.
    • Terminal state: The task is already SUCCEEDED, FAILED or CANCELED and cannot be cancelled.
  • Name
    404 - Not Found
    Description

    The task does not exist, belongs to a different user, or its stage does not match the URL path.

Request

DELETE
/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7
curl --request DELETE \
  --url https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/prototype/019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"

Response

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

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

Stream a Fidget Pixel Task

Stream real-time updates for a fidget pixel task via Server-Sent Events (SSE). The URL path must match the task's stage — opening a stream at /prototype/:buildId/stream emits a single event: error payload with status_code: 404 and closes the stream; a malformed id does the same with status_code: 400 (Invalid ID).

Parameters

  • Name
    id
    Type
    path
    Description

    Unique identifier for the fidget pixel task to stream.

Returns

Returns a stream of Fidget Pixel Prototype or Fidget Pixel Build task objects as Server-Sent Events. Every frame carries the full task object for the stage — the same shape the Get endpoint returns — so while the task is PENDING or IN_PROGRESS the output fields are simply not populated yet (null, [] or {}) and finished_at is null.

Request

GET
/openapi/creative-lab/fidget-pixel/v1/build/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/stream
curl -N https://api.meshy.ai/openapi/creative-lab/fidget-pixel/v1/build/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/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 for the stage; fields not yet populated are null / empty.
event: message
data: {
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "name": "",
  "status": "PENDING",
  "progress": 0,
  "created_at": 1757001300000,
  "started_at": null,
  "finished_at": null,
  "expires_at": 1757260500000,
  "preceding_tasks": 2,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {}
}

event: message
data: {
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001300000,
  "started_at": 1757001304000,
  "finished_at": 1757001309000,
  "expires_at": 1757260509000,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {
    "3mf": "https://assets.meshy.ai/***/tasks/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/output/model.3mf?Expires=***"
  }
}

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

List Fidget Pixel Tasks

Retrieve a paginated list of your fidget pixel tasks for a single stage. The URL path selects the stage — /prototype returns prototype tasks; /build returns build tasks. Tasks from the other stage are not included in either response.

Path Parameters

  • Name
    stage
    Type
    path
    Required
    Description

    Either prototype or build. The collection returns only tasks whose stage matches the URL — fetching /prototype never returns build tasks and vice versa.

Query Parameters

  • Name
    page_num
    Type
    integer
    default 1
    Description

    Page number for pagination.

  • Name
    page_size
    Type
    integer
    default 10
    Description

    Page size limit. Maximum allowed is 100 items.

  • Name
    sort_by
    Type
    string
    default -created_at
    Description

    Field to sort by. Available values:

    • +created_at: Sort by creation time in ascending order.
    • -created_at: Sort by creation time in descending order.

Returns

Returns a paginated list of the per-stage task object — either the fidget pixel prototype task object when listing /prototype or the fidget pixel build task object when listing /build.

Request

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

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

Response (List Prototype Tasks)

[
  {
    "id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
    "type": "creative-lab-fidget-pixel-prototype",
    "name": "",
    "status": "SUCCEEDED",
    "progress": 100,
    "created_at": 1757001000000,
    "started_at": 1757001005000,
    "finished_at": 1757001178000,
    "expires_at": 1757260378000,
    "preceding_tasks": 0,
    "task_error": null,
    "consumed_credits": 6,
    "image_urls": [
      "https://assets.meshy.ai/***/pixel-art.jpg?Expires=***"
    ]
  }
]

The Fidget Pixel Prototype Task Object

The Fidget Pixel Prototype Task object is a work unit that Meshy keeps track of to pixelize a source photo into a pixel-art image. The output of this stage is chained into the build stage via input_task_id.

Properties

  • Name
    id
    Type
    string
    Description

    Unique identifier for the task. While we use a k-sortable UUID for task ids as the implementation detail, you should not make any assumptions about the format of the id.

  • Name
    type
    Type
    string
    Description

    Type of the task. The value is creative-lab-fidget-pixel-prototype.

  • Name
    name
    Type
    string
    Description

    The task name supplied when the task was created. Empty string if no name was provided.

  • Name
    status
    Type
    string
    Description

    Status of the task. Possible values are one of PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.

  • Name
    progress
    Type
    integer
    Description

    Progress of the task. If the task is not started yet, this property will be 0. Once the task has succeeded, this will become 100.

  • Name
    created_at
    Type
    timestamp
    Description

    Timestamp of when the task was created, in milliseconds.

  • Name
    started_at
    Type
    timestamp
    Description

    Timestamp of when the task was started, in milliseconds. If the task is not started yet, this property will be null.

  • Name
    finished_at
    Type
    timestamp
    Description

    Timestamp of when the task was finished, in milliseconds. If the task is not finished yet, this property will be null.

  • Name
    expires_at
    Type
    timestamp
    Description

    Timestamp of when the task result expires, in milliseconds — 3 days after the task finished. Enterprise accounts keep API results indefinitely (see Asset Retention); for them this timestamp is set about 100 years out.

  • Name
    preceding_tasks
    Type
    integer
    Description

    The count of preceding tasks.

  • Name
    task_error
    Type
    object
    Description

    Error details for failed tasks. See Errors for the full task_error object reference.

  • Name
    consumed_credits
    Type
    integer
    Description

    The number of credits consumed by this task. A task that reaches SUCCEEDED is charged the full amount for its stage. A task that never gets created (a 4xx at request time, including a moderation rejection) is not charged at all. A task that reaches FAILED returns 0 — the charge is refunded. Cancelling via DELETE refunds only while the task is still PENDING; a task already IN_PROGRESS stays charged, because the work has been spent.

  • Name
    image_urls
    Type
    array of strings
    Description

    Downloadable URLs for the pixel-art image generated by this prototype task. Currently the API always returns exactly one image; the field is an array so future revisions can surface multiple candidates without a breaking change. Empty until the task reaches SUCCEEDED.

    These are signed URLs: fetch them without an Authorization header. They stay valid until expires_at, which is 3 days after finished_at, and re-reading the task inside that window returns the identical URL rather than a freshly signed one. Download and store the files yourself before then — there is no way to refresh an expired link.

Example Fidget Pixel Prototype Task Object

{
  "id": "019c4a1e-2b7d-7d03-8f6a-5c2e91f0a1b7",
  "type": "creative-lab-fidget-pixel-prototype",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001000000,
  "started_at": 1757001005000,
  "finished_at": 1757001178000,
  "expires_at": 1757260378000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 6,
  "image_urls": [
    "https://assets.meshy.ai/***/pixel-art.jpg?Expires=***"
  ]
}

The Fidget Pixel Build Task Object

The Fidget Pixel Build Task object is a work unit that Meshy keeps track of to generate the printable pieces from a succeeded prototype task. The build samples the prototype's pixel-art image onto the requested grid and publishes a single color-tagged 3MF.

Properties

  • Name
    id
    Type
    string
    Description

    Unique identifier for the task.

  • Name
    type
    Type
    string
    Description

    Type of the task. The value is creative-lab-fidget-pixel-build.

  • Name
    name
    Type
    string
    Description

    The task name supplied when the task was created. Empty string if no name was provided.

  • Name
    status
    Type
    string
    Description

    Status of the task. Possible values are one of PENDING, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.

  • Name
    progress
    Type
    integer
    Description

    Progress of the task. If the task is not started yet, this property will be 0. Once the task has succeeded, this will become 100.

  • Name
    created_at
    Type
    timestamp
    Description

    Timestamp of when the task was created, in milliseconds.

  • Name
    started_at
    Type
    timestamp
    Description

    Timestamp of when the task was started, in milliseconds. null until the task starts.

  • Name
    finished_at
    Type
    timestamp
    Description

    Timestamp of when the task was finished, in milliseconds. null until the task finishes.

  • Name
    expires_at
    Type
    timestamp
    Description

    Timestamp of when the task result expires, in milliseconds — 3 days after the task finished. Enterprise accounts keep API results indefinitely (see Asset Retention); for them this timestamp is set about 100 years out.

  • Name
    preceding_tasks
    Type
    integer
    Description

    The count of preceding tasks. Meaningful only when status is PENDING.

  • Name
    task_error
    Type
    object
    Description

    Error details for failed tasks. See Errors for the full task_error object reference.

  • Name
    consumed_credits
    Type
    integer
    Description

    The number of credits consumed by this task. A task that reaches SUCCEEDED is charged the full amount for its stage. A task that never gets created (a 4xx at request time, including a moderation rejection) is not charged at all. A task that reaches FAILED returns 0 — the charge is refunded. Cancelling via DELETE refunds only while the task is still PENDING; a task already IN_PROGRESS stays charged, because the work has been spent.

  • Name
    model_urls
    Type
    object
    Description

    Downloadable URLs for the generated artifact, keyed by format. Contains exactly one entry — the format requested via the build request's output.format. Empty until the task reaches SUCCEEDED.

    These are signed URLs: fetch them without an Authorization header. They stay valid until expires_at, which is 3 days after finished_at, and re-reading the task inside that window returns the identical URL rather than a freshly signed one. Download and store the files yourself before then — there is no way to refresh an expired link.

    • Name
      3mf
      Type
      string
      Description

      Downloadable URL to the 3MF file. One object per piece, each tagged with its palette color, so a multi-filament slicer assigns filaments per color. Present when output.format was 3mf (the default).

Example Fidget Pixel Build Task Object

{
  "id": "019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98",
  "type": "creative-lab-fidget-pixel-build",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1757001300000,
  "started_at": 1757001304000,
  "finished_at": 1757001309000,
  "expires_at": 1757260509000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 30,
  "model_urls": {
    "3mf": "https://assets.meshy.ai/***/tasks/019c4a2f-6e81-7b44-9a1d-0d7f3c5e2b98/output/model.3mf?Expires=***"
  }
}

End-to-End Example

The complete flow: create a prototype from a photo, poll it to SUCCEEDED, create a build from it, poll the build to SUCCEEDED, then download the 3MF from model_urls.

A prototype usually finishes within a few minutes; a build typically completes in well under a minute. In a real integration you would show the prototype's image_urls entry to the end user and let them confirm (or re-run the prototype) before spending credits on the build.

Complete flow

POST
/openapi/creative-lab/fidget-pixel/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://...
#   export PIXEL_TYPE=person                  # or: other
: "${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
PIXEL_TYPE=${PIXEL_TYPE:-person}

BASE="https://api.meshy.ai/openapi/creative-lab/fidget-pixel/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 '{"type":"%s","image_url":"data:%s;base64,' "$PIXEL_TYPE" "$MIME"
    base64 <"$IMAGE_PATH" | tr -d '\n'
    printf '"}'
  } >"$BODY"
else
  jq -n --arg t "$PIXEL_TYPE" --arg u "$IMAGE_URL" \
    '{type: $t, image_url: $u}' >"$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 pixel-art image (show image_urls[0] to a user in production)
poll prototype "$PROTO_ID"

# 3. Create the build task (defaults: square pieces, 32x32 grid, 8 mm, 8 colors, 15 mm tall)
jq -n --arg p "$PROTO_ID" \
  '{input_task_id: $p, options: {shape: "square", grid_size: 32, piece_size_mm: 8, color_count: 8, piece_height_mm: 15}, output: {format: "3mf"}}' >"$BODY"
BUILD_ID=$(api POST "$BASE/build" \
  -H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')

# 4. Wait for the pieces
poll build "$BUILD_ID"

# 5. Download the 3MF. This is a signed URL: no Authorization header,
#    and it stays valid for 3 days after the task finishes.
TASK=$(api GET "$BASE/build/$BUILD_ID")
curl --silent --show-error --fail --max-time 900 \
  -o fidget-pixel.3mf "$(jq -r '.model_urls["3mf"]' <<<"$TASK")"
echo "Done: fidget-pixel.3mf"