> **Reading as an AI agent?** This is the Markdown version of https://docs.meshy.ai/api/creative-lab-fidget-collapsible.
>
> - Full docs index: https://docs.meshy.ai/llms.txt
> - Single-fetch full content: https://docs.meshy.ai/llms-full.txt
> - Tool-calling access via MCP server: https://docs.meshy.ai/api/ai

---
# Creative Lab — Collapsible Fidget API

Turn a source photo into a print-in-place collapsible fidget toy: the subject's
silhouette becomes a set of nested concentric rings that collapse flat and
stretch out again, printed in one piece with no assembly.

- `POST /openapi/creative-lab/fidget-collapsible/v1`

Unlike the other Creative Lab endpoints, this one has **no prototype/build stage
pair** — there are no intermediate candidates to choose between, so a single
task carries the image all the way to a 3D model. The geometry controls the web
app exposes (size, layer count, gap width, wall thickness, extrusion depth,
bulge) are **not** part of the request either: every task builds with the same
server-side defaults.

---

## POST /openapi/creative-lab/fidget-collapsible/v1 -- Create a Collapsible Fidget Task

Generate a collapsible fidget model from a source photo. Refer to
[The Collapsible Fidget Task Object](#the-collapsible-fidget-task-object)
for the response shape.

Each task costs **6 credits** and requires a paid plan.

### Parameters

  - `image_url` · *string* · **required**

  Source photo for Meshy to turn into a collapsible fidget. We currently support `.jpg`, `.jpeg`, `.png`, and `.webp` formats.

  Maximum file size: 20 MB.

  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>`.

  A subject with one clear, closed outline works best — the silhouette is what becomes the rings. Busy backgrounds, several separate subjects, or very thin shapes can leave too little area for the nested walls, and the task fails with a task error.

  - `name` · *string*

  Optional task name for display purposes. Maximum 100 characters. This is a task label only; nothing is engraved on the model.

### Returns

The `result` property of the response contains the task `id` of the newly created collapsible fidget task. Poll the [Get a Task](#retrieve-a-collapsible-fidget-task) endpoint or subscribe to the [stream](#stream-a-collapsible-fidget-task) until the task reaches `SUCCEEDED`, then download the printable STL from `model_urls.stl` (and, when present, the GLB from `model_urls.glb` to preview it first).

### Failure Modes

  - `400 - Bad Request`

  The request was unacceptable. Common causes:
  * **Missing parameter**: `image_url` is required.
  * **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.

  - `401 - Unauthorized`

  Authentication failed. Please check your API key.

  - `402 - Payment Required`

  Either your account is on the free plan — task creation on this endpoint requires a paid plan — or you have insufficient credits.

  - `403 - Forbidden`

  The input image was flagged for intellectual property violation.

  - `429 - Too Many Requests`

  You have exceeded your rate limit.

**cURL**

```bash
curl https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1 \
  -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>"
  }'
```

```javascript
import axios from 'axios'

const headers = { Authorization: `Bearer ${YOUR_API_KEY}` };

const payload = {
  image_url: "<your publicly accessible image url or base64-encoded data URI>",
};

try {
  const response = await axios.post(
    'https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1',
    payload,
    { headers }
  );
  console.log(response.data);
} catch (error) {
  console.error(error);
}
```

```python
import requests

headers = {
    "Authorization": f"Bearer {YOUR_API_KEY}"
}

payload = {
    "image_url": "<your publicly accessible image url or base64-encoded data URI>",
}

response = requests.post(
    "https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1",
    headers=headers,
    json=payload,
)
response.raise_for_status()
print(response.json())
```

**Response**

```json
{
  "result": "018a210d-8ba4-705c-b111-1f1776f7f578"
}
```

---

## GET /openapi/creative-lab/fidget-collapsible/v1/:id -- Retrieve a Collapsible Fidget Task

Retrieve a collapsible fidget task given a valid task `id`. Only tasks
created through this endpoint are addressable here — a task from another
Creative Lab endpoint, or one created in the web app, returns `404`.

Refer to [The Collapsible Fidget Task Object](#the-collapsible-fidget-task-object)
for the response shape.

### Parameters

  - `id` · *path*

  Unique identifier for the collapsible fidget task to retrieve.

### Returns

The response contains the collapsible fidget task object.

**cURL**

```bash
curl https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/018a210d-8ba4-705c-b111-1f1776f7f578 \
  -H "Authorization: Bearer ${YOUR_API_KEY}"
```

```javascript
import axios from 'axios'

const headers = { Authorization: `Bearer ${YOUR_API_KEY}` };

const taskId = '018a210d-8ba4-705c-b111-1f1776f7f578';
const { data } = await axios.get(
  `https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/${taskId}`,
  { headers }
);
console.log(data);
```

```python
import requests

headers = {
    "Authorization": f"Bearer {YOUR_API_KEY}"
}

task_id = "018a210d-8ba4-705c-b111-1f1776f7f578"
response = requests.get(
    f"https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/{task_id}",
    headers=headers,
)
response.raise_for_status()
print(response.json())
```

**Response**

```json
{
  "id": "018a210d-8ba4-705c-b111-1f1776f7f578",
  "type": "creative-lab-fidget-collapsible",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1729123456000,
  "started_at": 1729123460000,
  "finished_at": 1729123512000,
  "expires_at": 1729382712000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 6,
  "model_urls": {
    "glb": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.glb?Expires=***",
    "stl": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.stl?Expires=***"
  }
}
```

---

## DELETE /openapi/creative-lab/fidget-collapsible/v1/:id -- Delete a Collapsible Fidget Task

Cancel a collapsible fidget 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.

### Path Parameters

  - `id` · *path*

  Unique identifier for the collapsible fidget task to cancel.

### Returns

Returns `204 No Content` on success with an empty body.

### Failure Modes

  - `400 - Bad Request`

  The task is already in a terminal state and cannot be cancelled.

  - `404 - Not Found`

  The task does not exist, belongs to a different user, or was not created through this endpoint.

  **cURL**

  ```bash
  curl --request DELETE \
    --url https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/018a210d-8ba4-705c-b111-1f1776f7f578 \
    -H "Authorization: Bearer ${YOUR_API_KEY}"
  ```

  ```javascript
  import axios from 'axios'

  const taskId = '018a210d-8ba4-705c-b111-1f1776f7f578'
  const headers = { Authorization: `Bearer ${YOUR_API_KEY}` }

  try {
    await axios.delete(
      `https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/${taskId}`,
      { headers }
    )
  } catch (error) {
    console.error(error)
  }
  ```

  ```python
  import requests

  task_id = "018a210d-8ba4-705c-b111-1f1776f7f578"
  headers = {
      "Authorization": f"Bearer {YOUR_API_KEY}"
  }

  response = requests.delete(
      f"https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/{task_id}",
      headers=headers,
  )
  response.raise_for_status()
  ```

**Response**

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

---

## GET /openapi/creative-lab/fidget-collapsible/v1/:id/stream -- Stream a Collapsible Fidget Task

Stream real-time updates for a collapsible fidget task via Server-Sent
Events (SSE). A task that does not exist, or was not created through this
endpoint, emits a single `event: error` payload with `status_code: 404`
and closes the stream.

### Parameters

  - `id` · *path*

  Unique identifier for the collapsible fidget task to stream.

### Returns

Returns a stream of [Collapsible Fidget](#the-collapsible-fidget-task-object)
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`.

  **cURL**

  ```bash
  curl -N https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/018a210d-8ba4-705c-b111-1f1776f7f578/stream \
  -H "Authorization: Bearer ${YOUR_API_KEY}"
  ```

  ```javascript
  const response = await fetch(
    'https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/018a210d-8ba4-705c-b111-1f1776f7f578/stream',
    {
      headers: { Authorization: `Bearer ${YOUR_API_KEY}` }
    }
  );

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop();

    for (const line of lines) {
      if (line.startsWith('data:')) {
        const data = JSON.parse(line.slice(5));
        console.log(data);

        if (['SUCCEEDED', 'FAILED', 'CANCELED'].includes(data.status)) {
          reader.cancel();
        }
      }
    }
  }
  ```

  ```python
  import requests
  import json

  headers = {
      "Authorization": f"Bearer {YOUR_API_KEY}",
      "Accept": "text/event-stream"
  }

  response = requests.get(
      'https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1/018a210d-8ba4-705c-b111-1f1776f7f578/stream',
      headers=headers,
      stream=True
  )

  for line in response.iter_lines():
      if line:
          if line.startswith(b'data:'):
              data = json.loads(line.decode('utf-8')[5:])
              print(data)

              if data['status'] in ['SUCCEEDED', 'FAILED', 'CANCELED']:
                  break

  response.close()
  ```

  **Response Stream**

  ```javascript
  // Error event example (task not found, or not created through this endpoint)
  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": "018a210d-8ba4-705c-b111-1f1776f7f578",
    "progress": 0,
    "status": "PENDING"
  }

  event: message
  data: {
    "id": "018a210d-8ba4-705c-b111-1f1776f7f578",
    "type": "creative-lab-fidget-collapsible",
    "status": "SUCCEEDED",
    "progress": 100,
    "created_at": 1729123456000,
    "started_at": 1729123460000,
    "finished_at": 1729123512000,
    "expires_at": 1729382712000,
    "task_error": null,
    "consumed_credits": 6,
    "model_urls": {
      "glb": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.glb?Expires=***",
      "stl": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.stl?Expires=***"
    }
  }
  ```

---

## GET /openapi/creative-lab/fidget-collapsible/v1 -- List Collapsible Fidget Tasks

Retrieve a paginated list of your collapsible fidget tasks. Only tasks
created through this endpoint are included.

### Query Parameters

  - `page_num` · *integer* · default: `1`

  Page number for pagination.

  - `page_size` · *integer* · default: `10`

  Page size limit. Maximum allowed is `100` items.

  - `sort_by` · *string* · default: `-created_at`

  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 collapsible fidget task object](#the-collapsible-fidget-task-object).

  **cURL**

  ```bash
  curl https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1?page_size=10 \
    -H "Authorization: Bearer ${YOUR_API_KEY}"
  ```

  ```javascript
  import axios from 'axios'

  const headers = { Authorization: `Bearer ${YOUR_API_KEY}` }

  try {
    const { data } = await axios.get(
      'https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1',
      { headers, params: { page_size: 10 } }
    )
    console.log(data)
  } catch (error) {
    console.error(error)
  }
  ```

  ```python
  import requests

  headers = {
      "Authorization": f"Bearer {YOUR_API_KEY}"
  }

  response = requests.get(
      "https://api.meshy.ai/openapi/creative-lab/fidget-collapsible/v1",
      headers=headers,
      params={"page_size": 10},
  )
  response.raise_for_status()
  print(response.json())
  ```

**Response**

```json
[
  {
    "id": "018a210d-8ba4-705c-b111-1f1776f7f578",
    "type": "creative-lab-fidget-collapsible",
    "name": "",
    "status": "SUCCEEDED",
    "progress": 100,
    "created_at": 1729123456000,
    "started_at": 1729123460000,
    "finished_at": 1729123512000,
    "expires_at": 1729382712000,
    "preceding_tasks": 0,
    "task_error": null,
    "consumed_credits": 6,
    "model_urls": {
      "glb": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.glb?Expires=***",
      "stl": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.stl?Expires=***"
    }
  }
]
```

---

## The Collapsible Fidget Task Object
The Collapsible Fidget Task object is a work unit that Meshy keeps track of to
turn a source photo into a print-in-place collapsible fidget model. It is a
single-stage task: there is no prototype to chain from, and the intermediate
silhouette is not part of the response.

### Properties

- `id` · *string*

  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.

- `type` · *string*

  Type of the task. The value is `creative-lab-fidget-collapsible`.

- `name` · *string*

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

- `status` · *string*

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

- `progress` · *integer*

  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`.

- `created_at` · *timestamp*

  Timestamp of when the task was created, in milliseconds.

  > **Note:** A timestamp represents the number of milliseconds elapsed since January 1, 1970 UTC, following
  >                     the [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) standard.
  >                     For example, Friday, September 1, 2023 12:00:00 PM GMT is represented as `1693569600000`. This applies
  >                     to **all** timestamps in Meshy API.

- `started_at` · *timestamp*

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

- `finished_at` · *timestamp*

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

- `expires_at` · *timestamp*

  Timestamp of when the task result expires, in milliseconds.

- `preceding_tasks` · *integer*

  The count of preceding tasks.

  > **Note:** The value of this field is meaningful only if the task status is `PENDING`.

- `task_error` · *object*

  Error details for failed tasks. See [Errors](/api/errors#task-errors) for the full `task_error` object reference.

- `consumed_credits` · *integer*

  The number of credits consumed by this task. Present when the task status is `PENDING`, `IN_PROGRESS`, or `SUCCEEDED`. Returns `0` for `FAILED` tasks (credits are refunded on failure).

- `model_urls` · *object*

  Downloadable URLs for the generated 3D model. Populated once the task has `SUCCEEDED`: `stl` is always present, `glb` only when the preview render succeeded.

  - `stl` · *string*

  Downloadable URL to the STL file. This is the printable deliverable — send it straight to a slicer.

  - `glb` · *string*

  Downloadable URL to the GLB file, for previewing the model in a 3D viewer. Its colours are for preview only: STL carries no colour, and a printed fidget takes its colour from the filament. The GLB is best-effort: when the preview render is unavailable the key is omitted from `model_urls` entirely, so read it defensively — `stl` is the deliverable and is always present on a `SUCCEEDED` task.

<span id="example-collapsible-fidget-task-object" />

**Example Collapsible Fidget Task Object**

```json
{
  "id": "018a210d-8ba4-705c-b111-1f1776f7f578",
  "type": "creative-lab-fidget-collapsible",
  "name": "",
  "status": "SUCCEEDED",
  "progress": 100,
  "created_at": 1729123456000,
  "started_at": 1729123460000,
  "finished_at": 1729123512000,
  "expires_at": 1729382712000,
  "preceding_tasks": 0,
  "task_error": null,
  "consumed_credits": 6,
  "model_urls": {
    "glb": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.glb?Expires=***",
    "stl": "https://assets.meshy.ai/***/tasks/018a210d-8ba4-705c-b111-1f1776f7f578/output/model.stl?Expires=***"
  }
}
```
