# Detail & Texture
Source: https://docs.thrixel.com/detail
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Callout } from 'fumadocs-ui/components/callout';
Phase 4 takes a completed submission as the source mesh and produces a high-resolution
textured model. You can also remesh or retexture an existing result without regenerating
from scratch.
Endpoints split across two routers:
- `/api/v1/detailer/*` — full detail + texture pass, geometry-only operations (remesh).
- `/api/v1/texture/*` — texture-only operations on an existing mesh (generate, rebake).
---
## Detail and texture a model
Requires API key.
Generate a high-resolution textured mesh from a parent submission. A reference image and
prompt guide the texture style; if omitted, they are auto-generated from the parent.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `parent_submission_id` | string (required) | ID of the submission to detail. |
| `image` | string | Reference image (base64 or HTTPS URL). Auto-generated if omitted. |
| `mesh` | string | Override source mesh (base64 or URL). Defaults to the parent's GLB. |
| `prompt` | string | Text description of the desired appearance. |
| `seed` | integer | Random seed for reproducibility. 0–999,999. Default: `42`. |
| `texture_size` | integer | Output texture resolution. Values: `512`, `768`, `1024`, `2048`. Default: `768`. |
| `decimation_target` | integer | Target triangle count after decimation. 10,000–500,000. Default: `160000`. |
| `preserve_parts` | boolean | Keep separate mesh parts intact during decimation. Default: `true`. |
| `focus_on_node_names` | string[] | Node names to focus the generation on. Empty = whole mesh. |
| `project_id` | string | Assign to a project. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/detailer/submit \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{
"parent_submission_id": "a1b2c3d4-...",
"prompt": "weathered bronze, green patina",
"texture_size": 1024
}'
```
### Example Response
```json
{
"submission_id": "e5f6a7b8-...",
"status": "queued",
"created_at": "2026-04-20T14:45:30Z",
"reference_image_url": "https://api.thrixel.com/api/v1/detailer/reference/e5f6a7b8-..."
}
```
---
## Re-decimate a Phase 4 mesh
Requires API key.
Produce a new triangle count from an existing Phase 4 checkpoint without regenerating
texture. Fast — reuses cached intermediate results.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `parent_submission_id` | string (required) | ID of a completed Phase 4 submission. |
| `decimation_target` | integer | Target triangle count. 10,000–500,000. Default: `200000`. |
| `remesh` | boolean | Run quad remeshing before decimation. Default: `true`. |
| `project_id` | string | Assign to a project. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/detailer/remesh \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{
"parent_submission_id": "e5f6a7b8-...",
"decimation_target": 80000
}'
```
---
## Generate textures for an existing mesh
Requires API key.
Produce fresh PBR textures from a reference image. Geometry is preserved; only materials
change. Works on any completed submission (phase 1–5), not just Phase 4 output. Image
priority: user-supplied → cached parent input → auto-generated from the GLB.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `parent_submission_id` | string (required) | ID of a completed submission to retexture. |
| `texture_size` | integer | Output texture resolution. Values: `512`, `768`, `1024`, `2048`. Default: `1024`. |
| `image` | string | Reference image (base64 data URI). Auto-generated from the GLB if omitted. |
| `apply_to_node_names` | string[] | Restrict texturing to specific mesh nodes. Empty = whole mesh. |
| `project_id` | string | Assign to a project. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/texture/generate \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{
"parent_submission_id": "e5f6a7b8-...",
"texture_size": 1024
}'
```
---
## Rebake textures at a new resolution
Requires API key.
Re-bake the existing texture at a different resolution. Picks the cheapest viable path
automatically: fast bake from a prior `/texture/generate` checkpoint when available,
otherwise the full reprocessing path.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `parent_submission_id` | string (required) | ID of a completed submission. |
| `texture_size` | integer | Output texture resolution. Values: `512`, `768`, `1024`, `2048`. Default: `1024`. |
| `project_id` | string | Assign to a project. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/texture/rebake \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{
"parent_submission_id": "e5f6a7b8-...",
"texture_size": 2048
}'
```
---
## Fetch reference image
Public.
Returns the user-provided or auto-generated reference image used to guide the Phase 4
pipeline. PNG.
### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of a Phase 4 submission. |
### Example Request
```bash
curl -L -o reference.png \
https://api.thrixel.com/api/v1/detailer/reference/e5f6a7b8-...
```
---
# Download
Source: https://docs.thrixel.com/download
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Callout } from 'fumadocs-ui/components/callout';
GLB is produced when a submission completes. Other formats are converted on-demand:
either inline via the unified `/download` endpoint (which waits for the result), or
asynchronously via the `/convert` job API.
---
## Download a submission asset
Public.
Unified endpoint for all downloadable artifacts. GLB is always ready; FBX/OBJ/STL/USDZ
trigger an on-demand conversion if not yet cached.
### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
### Query Parameters
| Name | Type | Description |
| --- | --- | --- |
| `format` | string | Asset format. Values: `glb`, `fbx`, `obj`, `stl`, `usdz`, `py`, `thumbnail`, `reference_image`. Default: `glb`. |
| `index` | integer | Index for `reference_image` format (0–5). Default: `0`. |
### Example Request
```bash
# GLB (instant)
curl -L -o model.glb \
"https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=glb"
# FBX (triggers conversion, may take a few seconds)
curl -L -o model.fbx \
"https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=fbx"
```
---
## Queue a conversion job
Public.
Async alternative to `/download` for non-GLB formats. Returns immediately with a `job_id`
to poll. Re-requesting the same (submission, format) returns the existing ready job
unless `force=true`.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission to convert. |
| `format` | string (required) | Target format. Values: `fbx`, `obj`, `stl`, `usdz`. |
| `force` | boolean | Re-run the conversion even if a ready job exists. Default: `false`. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/convert \
-H "Content-Type: application/json" \
-d '{"submission_id": "a1b2c3d4-...", "format": "usdz"}'
```
### Example Response
```json
{
"job_id": "f7a8b9c0-...",
"submission_id": "a1b2c3d4-...",
"format": "usdz",
"status": "queued",
"error": null,
"download_url": null,
"status_url": "https://api.thrixel.com/api/v1/convert/f7a8b9c0-..."
}
```
---
## Check conversion status
Public.
Returns the current status of a conversion job. Poll until status becomes `ready` or
`failed`.
### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `job_id` | string (required) | UUID returned by `POST /convert`. |
### Example Request
```bash
curl https://api.thrixel.com/api/v1/convert/f7a8b9c0-...
```
### Example Response
```json
{
"job_id": "f7a8b9c0-...",
"submission_id": "a1b2c3d4-...",
"format": "usdz",
"status": "ready",
"error": null,
"download_url": "https://api.thrixel.com/api/v1/convert/f7a8b9c0-.../download",
"status_url": "https://api.thrixel.com/api/v1/convert/f7a8b9c0-..."
}
```
---
## Download a converted file
Public.
Returns the converted file once ready. 409 if still queued/running, 410 if the job
failed.
### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `job_id` | string (required) | UUID of a ready conversion job. |
### Example Request
```bash
curl -L -o model.usdz \
https://api.thrixel.com/api/v1/convert/f7a8b9c0-.../download
```
---
# Ratings & Favorites
Source: https://docs.thrixel.com/feedback
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Callout } from 'fumadocs-ui/components/callout';
There are two ways to register that you liked (or didn't like) a submission:
- **Ratings** — a thumbs-up / thumbs-down signal. Thrixel uses these in
aggregate to evaluate model quality. One rating per API key per submission;
re-calling flips the value.
- **Favorites** — a personal bookmark. Surfaces the submission in your
"Favorite Assets" view. Independent of ratings (so you can love a model
for personal reasons even while thumbs-downing it for the quality signal).
Both are scoped to your API key — nothing here is public.
## Ratings
---
### Set a rating
Requires API key.
Upserts the rating for the calling API key on this submission. Re-calling with
a different value flips the rating; the response always reflects the current
state.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `rating` | integer (required) | `1` for thumbs-up, `-1` for thumbs-down. |
#### Example Request
```bash
curl -X PUT https://api.thrixel.com/api/v1/$SUBMISSION_ID/rating \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{"rating": 1}'
```
#### Example Response
```json
{
"submission_id": "sub-3c4d...",
"rating": 1
}
```
#### Errors
| Code | Meaning |
| --- | --- |
| `404` | Submission not found. |
| `422` | `rating` is not `1` or `-1`. |
---
### Clear a rating
Requires API key.
Removes the calling API key's rating on this submission. Idempotent — no error
if no rating exists. The response has `rating: null`.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Example Response
```json
{
"submission_id": "sub-3c4d...",
"rating": null
}
```
## Favorites
---
Favorites are a **personal collection** — they don't affect Thrixel's
internal quality signal. Use them to bookmark models you want to find later
in the "Favorite Assets" sidebar view.
### Favorite a submission
Requires API key.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `is_favorite` | boolean (required) | `true` to favorite, `false` to remove. |
#### Example Request
```bash
curl -X PATCH https://api.thrixel.com/api/v1/$SUBMISSION_ID/favorite \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{"is_favorite": true}'
```
#### Example Response
```json
{
"submission_id": "sub-3c4d...",
"is_favorite": true
}
```
---
### Pin a favorite
Requires API key.
Pin or unpin a favorited submission so it sorts to the top of the **Favorite
Assets** grid. Independent of the chat-sidebar pin (`PATCH /pin` in
[Organize](/docs/organize)) — pinning here only affects the favorites view.
The submission must already be a favorite (i.e. `is_favorite=true`) for pinning
to have a visible effect.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `is_favorite_pinned` | boolean (required) | `true` to pin within favorites, `false` to unpin. |
#### Example Response
```json
{
"submission_id": "sub-3c4d...",
"is_favorite_pinned": true
}
```
---
# Getting Started
Source: https://docs.thrixel.com/getting-started
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
## What is Thrixel?
Thrixel turns text prompts and reference images into production-ready 3D models through a
five-phase pipeline. You submit a request, poll (or stream) until it's ready, then download
the result in GLB, FBX, OBJ, STL, or USDZ.
## Base URL
```bash
https://api.thrixel.com/api/v1
```
## Authentication
Authenticated endpoints require an API key passed as a Bearer token. Keys are in the
format `sk-thrixel-...` and are issued by the Thrixel team — contact us to obtain one.
```bash
Authorization: Bearer sk-thrixel-
```
Public endpoints (info, stream, downloads, convert, gallery) do not require authentication.
## Rate Limits
Each API key is subject to three limits (defaults shown — your plan may differ):
- **Concurrent jobs** — 3 active (queued or processing) submissions at a time
- **Hourly** — 10 requests per rolling hour
- **Daily** — 50 requests per rolling 24 hours
## Quickstart
Submit a text prompt, wait for the job to complete, and download the GLB:
```bash
# 1. Submit a prompt
curl -X POST https://api.thrixel.com/api/v1/phase1/submit \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{"task": "a small stone gargoyle holding a lantern"}'
# → {"submission_id": "abc-123", "status": "queued", "created_at": "..."}
# 2. Poll until completed
curl https://api.thrixel.com/api/v1/abc-123/info
# → {"status": "completed", ...}
# 3. Download the GLB
curl -L -o model.glb https://api.thrixel.com/api/v1/abc-123/download?format=glb
```
```python
import requests, time
BASE = "https://api.thrixel.com/api/v1"
KEY = "sk-thrixel-"
# 1. Submit
r = requests.post(
f"{BASE}/phase1/submit",
headers={"Authorization": f"Bearer {KEY}"},
json={"task": "a small stone gargoyle holding a lantern"},
)
submission_id = r.json()["submission_id"]
# 2. Poll
while True:
info = requests.get(f"{BASE}/{submission_id}/info").json()
if info["status"] in ("completed", "failed"):
break
time.sleep(5)
# 3. Download
glb = requests.get(f"{BASE}/{submission_id}/download?format=glb")
open("model.glb", "wb").write(glb.content)
```
---
# Thrixel API
Source: https://docs.thrixel.com/
The Thrixel API turns text prompts and reference images into production-ready 3D
models through a five-phase pipeline. You submit a request, poll (or stream)
until it's ready, then download the result in GLB, FBX, OBJ, STL, or USDZ.
## Where to start
- [Getting Started](/docs/getting-started) — base URL, authentication, rate limits, and a quickstart.
- [Generation](/docs/workflow) — the main endpoints that create a 3D model (Phase 1, 2, 3, 5).
- [Writing Prompts](/docs/prompts) — what works and what doesn't, written for users with no 3D background.
- [Job Status](/docs/progress) — polling, streaming (SSE), and submission listing.
- [Download](/docs/download) — get the model out in GLB, FBX, OBJ, STL, or USDZ.
- [Detail & Texture](/docs/detail) — Phase 4 GPU detailing, remesh, and texture generate/rebake.
- [Projects & Sources](/docs/organize) — group submissions, attach style references, rename/archive/pin.
- [Ratings & Favorites](/docs/feedback) — thumbs up/down + personal bookmarks.
## Base URL
```bash
https://api.thrixel.com/api/v1
```
---
# Projects & Sources
Source: https://docs.thrixel.com/organize
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Callout } from 'fumadocs-ui/components/callout';
Group related submissions into projects, give them nicknames, pin favorites, and
archive older work. All endpoints below require authentication and act on resources
owned by your API key.
## Projects
---
### List projects
Requires API key.
Returns all projects owned by the authenticated API key.
#### Query Parameters
| Name | Type | Description |
| --- | --- | --- |
| `include_archived` | boolean | Include archived projects. Default: `false`. |
#### Example Request
```bash
curl -H "Authorization: Bearer sk-thrixel-" \
https://api.thrixel.com/api/v1/projects
```
#### Example Response
```json
{
"projects": [
{
"id": "p1q2r3s4-...",
"name": "Gargoyle variations",
"description": "Stylistic experiments",
"is_archived": false,
"created_at": "2026-04-18T09:00:00Z",
"updated_at": "2026-04-20T14:00:00Z",
"submission_count": 7
}
],
"count": 1
}
```
---
### Create a project
Requires API key.
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `name` | string (required) | Project name. 1–255 characters. |
| `description` | string | Optional description. Max 2000 characters. |
#### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/projects \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{"name": "Gargoyle variations"}'
```
---
### Get a project
Requires API key.
Returns project metadata plus an embedded list of its submissions (soft-deleted
excluded).
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `project_id` | string (required) | UUID of the project. |
---
### Update a project
Requires API key.
Rename or re-describe. At least one field required.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `project_id` | string (required) | UUID of the project. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `name` | string | New name. 1–255 characters. |
| `description` | string | New description. Max 2000 characters. |
---
### Delete a project
Requires API key.
Permanently deletes the project. Submissions in the project are unlinked (`project_id`
set to `null`) — they are not deleted.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `project_id` | string (required) | UUID of the project. |
## Project Sources
---
Each project can carry up to **256 KB of plain-text reference material** split
across files (max **64 KB per file**). Allowed extensions: `.md`, `.markdown`,
`.txt`, `.text`. When you submit a new generation inside the project, the parsed
text of every source file is injected into the LLM's system prompt — so this is
where you put your style guide, glossary of in-world names, dimensions you want
respected ("chairs are always 45 cm tall"), or any other context the model
should treat as constant.
Drop a `style.md` in a "Gargoyle variations" project that describes your
preferred polycount, the materials you want, and the silhouette rules. Every
Phase 1 prompt in that project will be evaluated against those constants
without you re-typing them.
### List source files
Requires API key.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `project_id` | string (required) | UUID of the project. |
#### Example Response
```json
{
"project_id": "p1q2r3s4-...",
"sources": [
{
"id": "src-9a8b...",
"project_id": "p1q2r3s4-...",
"filename": "style.md",
"mime_type": "text/markdown",
"size_bytes": 1842,
"created_at": "2026-04-20T14:00:00Z"
}
],
"count": 1,
"total_bytes": 1842
}
```
`total_bytes` is the sum across the whole project — useful for checking how
much of the 256 KB budget is left before uploading more.
---
### Get a single source
Requires API key.
Returns the metadata plus the **parsed text** of the file (what actually gets
injected into the prompt — line endings normalized, content decoded UTF-8).
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `project_id` | string (required) | UUID of the project. |
| `source_id` | string (required) | UUID of the source file. |
---
### Upload a source file
Requires API key. `multipart/form-data`.
The file is parsed at upload time and stored in the database; the raw upload is
also mirrored to S3. The parsed content is injected into the system prompt for
every new submission created in the project.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `project_id` | string (required) | UUID of the project. |
#### Form Fields
| Name | Type | Description |
| --- | --- | --- |
| `file` | file (required) | `.md`, `.markdown`, `.txt`, or `.text`. Max 64 KB per file. |
#### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/projects/$PROJECT_ID/sources \
-H "Authorization: Bearer sk-thrixel-" \
-F "file=@style.md"
```
#### Errors
| Code | Meaning |
| --- | --- |
| `400` | Empty file, or invalid UTF-8 content. |
| `413` | File exceeds 64 KB, or the project would exceed 256 KB total. |
| `415` | Unsupported file extension. |
---
### Update a source file
Requires API key.
Rename or rewrite the file's content. At least one of `filename` / `content`
must be present. Per-file and per-project caps still apply.
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `filename` | string | New filename including an allowed extension. |
| `content` | string | Replacement UTF-8 text. |
---
### Delete a source file
Requires API key.
Removes the DB row and the S3 mirror. Submissions already created in the project
are unaffected; only **new** submissions after this point will see the reduced
source set in the system prompt.
## Submission Management
---
### Set or clear the nickname
Requires API key.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `nickname` | string \| null | Display label. Pass `null` to clear. Max 255 characters. |
---
### Archive or unarchive
Requires API key.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `is_archived` | boolean (required) | `true` to archive, `false` to restore. |
---
### Toggle visibility
Requires API key.
Hides or shows the submission in list endpoints without deleting it.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `is_visible` | boolean (required) | `true` to show, `false` to hide. |
---
### Pin or unpin
Requires API key.
Pinned submissions sort to the top of lists.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `is_pinned` | boolean (required) | `true` to pin, `false` to unpin. |
---
### Assign to a project
Requires API key.
Move the submission into a project, or pass `null` to remove. Both the submission
and the target project must belong to your API key.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
#### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `project_id` | string \| null | UUID of the target project, or `null` to unassign. |
---
### Soft-delete a submission
Requires API key.
Hides the submission from listings but preserves its data. Include soft-deleted items
in `/submissions` via `include_deleted=true`.
#### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID of the submission. |
---
# Job Status
Source: https://docs.thrixel.com/progress
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Callout } from 'fumadocs-ui/components/callout';
Once a submission is created, monitor its lifecycle by polling `/info`, streaming
`/stream` (Server-Sent Events), or listing all your submissions.
---
## Get submission metadata
Public.
Returns current status, phase, input, and parent chain. Use this for simple polling.
### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID returned by any submit endpoint. |
### Example Request
```bash
curl https://api.thrixel.com/api/v1/a1b2c3d4-.../info
```
### Example Response
```json
{
"submission_id": "a1b2c3d4-...",
"phase": 1,
"status": "completed",
"model": "thrixel+",
"input": {
"type": "text",
"task": "a small stone gargoyle holding a lantern",
"image": null,
"images": null,
"modification_request": null
},
"parent_submission_id": null,
"nickname": null,
"is_archived": false,
"is_visible": true,
"is_deleted": false,
"is_pinned": false,
"adaptive_thinking": false,
"created_at": "2026-04-20T14:32:11Z",
"completed_at": "2026-04-20T14:34:02Z"
}
```
---
## Stream progress (SSE)
Public.
Server-Sent Events stream that emits progress updates, log messages, and status
transitions in real time. Closes automatically when the submission reaches a
terminal state.
### Path Parameters
| Name | Type | Description |
| --- | --- | --- |
| `submission_id` | string (required) | UUID returned by any submit endpoint. |
### Example Request
```bash
curl -N https://api.thrixel.com/api/v1/a1b2c3d4-.../stream
```
```javascript
const es = new EventSource(
"https://api.thrixel.com/api/v1/a1b2c3d4-.../stream"
);
es.onmessage = (e) => {
const event = JSON.parse(e.data);
console.log(event.type, event);
};
```
Event frames arrive as `data:` lines containing JSON. Common shapes:
```json
// Progress update
{"type": "progress", "percent": 0.42, "message": "Generating geometry..."}
// Status change
{"type": "status", "status": "processing"}
// Terminal event — stream closes after this
{"type": "status", "status": "completed"}
```
---
## List your submissions
Requires API key.
Returns all submissions owned by the authenticated API key. Filter by status, phase,
or project.
### Query Parameters
| Name | Type | Description |
| --- | --- | --- |
| `status` | string | Filter by status. Values: `queued`, `processing`, `completed`, `failed`. |
| `phase` | integer | Filter by phase. Values: `1`, `2`, `3`. |
| `project_id` | string | Filter by project. |
| `include_deleted` | boolean | Include soft-deleted submissions. Default: `false`. |
| `limit` | integer | Max results. Default: `50`. |
### Example Request
```bash
curl -H "Authorization: Bearer sk-thrixel-" \
"https://api.thrixel.com/api/v1/submissions?status=completed&limit=20"
```
### Example Response
```json
{
"submissions": [
{
"submission_id": "a1b2c3d4-...",
"phase": 1,
"status": "completed",
"nickname": null,
"is_pinned": false,
"project_id": null,
"created_at": "2026-04-20T14:32:11Z"
}
],
"count": 1
}
```
---
# Writing Prompts
Source: https://docs.thrixel.com/prompts
import { Callout } from 'fumadocs-ui/components/callout';
import PromptRefiner from '@/components/PromptRefiner';
You don't need any 3D background to write good prompts, but a few wording
patterns swing the result dramatically. This page is a cookbook of recipes
that work — same rules whether you're describing something new or editing
an existing model.
Type a rough draft, optionally drop a screenshot of your current model
(and a reference image of what you want), and a refined version comes back
that you can copy straight into Thrixel. The widget applies the same rules
described below.
---
## Five rules that apply everywhere
**1. Numbers beat adjectives.** "Decimate to 50,000 triangles" works; "make it
simpler" gambles. "Scale to 2 meters tall" works; "make it bigger" doesn't say
how. If you can put a number on it, do.
**2. Name the part, don't describe it.** Once a model is generated, open the
**Scene Hierarchy** panel — every named group (Body, Wheels, Chimney) is a
handle you can refer to. Prompts that reference part names ("scale up the
Wheels group by 1.5x") behave dramatically better than spatial guesses ("make
the round things at the bottom bigger").
In the SPA, you can also **click a part in the 3D viewer** to scope an edit
to that part. A chip appears above the Edit prompt ("Apply to: Wheels"), and
when you Apply Edit, the LLM is told to leave every other part bit-identical.
Same applies to the Refine button — it'll rewrite your draft to stay within
that scope rather than expanding to the whole model. Via the API, pass
`focus_on_node_names: ["Wheels"]` on `POST /api/v1/phase3/submit` or
`POST /api/v1/prompts/refine` to get the same behaviour.
**3. Anchor scale and direction to known things, not axes.** "The size of a
coffee mug" is unambiguous. "Facing the camera" beats "rotated 90° on Y" —
*forward / left / front / behind* gets re-projected correctly more reliably
than raw axes.
**4. One change per edit.** The single biggest mistake. If you say "make the
wheels red AND raise the body AND add a roof", at least one of those three
will silently revert or interact badly. Split into three sequential edits;
editing is iterative by design.
**5. "Keep X unchanged" is a real instruction.** Without it, a small change
can pull other parts along with it. Adding "keep the body, smokestack, and
tender exactly as they are" ties everything else down.
---
## Creating a model
A new-from-scratch prompt has to invent everything — geometry, proportions,
materials, parts — so specifics buy you a lot. A useful prompt usually has
three components:
- **Subject** — what the object actually is.
- **Style** — visual aesthetic (low-poly, stylized, realistic, voxel, cartoon).
- **Scale anchor** — a reference for size, if it matters.
"A wooden barrel with iron rings, low-poly stylized, the size of a person."
"A cool barrel."
The bad version leaves style, materials, scale, and detail level entirely
unconstrained. Each of those guesses will surprise you.
### When you have a reference image
If you upload an image, the prompt's job changes — it describes *intent*, not
appearance. Use the prompt to call out:
- The **focus** in the image (e.g. "model the dragon, ignore the
background rocks").
- Anything the image doesn't show (e.g. "the back side has a tail similar to
the front legs").
- Style overrides ("the image shows a real dog, but model it low-poly").
"Model the central character only. Stylized hand-painted look. The arms not
visible in the image should match the legs."
"Make this." *(prompt is empty, image attached)*
The empty-prompt case actually works *okay* most of the time, but framing is
guessed and the entire scene sometimes gets modelled rather than the subject
you cared about.
### Style vocabulary
| What you want | What works in the prompt |
| --- | --- |
| Cartoon look | "stylized", "exaggerated proportions", "no surface detail", "flat colors" |
| Realistic look | "PBR materials", "photoreal", "fine surface detail" |
| Game-asset look | "low-poly", "stylized PBR", "clean topology" |
| Toy / blocky look | "voxel-style", "blocky geometry" |
| Specific colors | Hex codes (`#a83232`) or named colors ("dark forest green") |
---
## Editing a model
Edit prompts are imperative — they describe *what to change* on top of the
existing model. The structure is already known, so you can refer to specific
named parts and they'll be matched.
### Geometry edits
| Goal | Recipe |
| --- | --- |
| Reduce triangle count | `Decimate the mesh so that there are 50000 triangles.` |
| Uniform resize | `Scale the entire model up by 1.5x.` |
| Anisotropic resize | `Scale only the height by 2x. Keep width and depth the same.` |
| Move a part | `Move the Chimney 0.3 units forward. Keep everything else unchanged.` |
| Rotate a part | `Rotate the Wheels group by 30 degrees.` |
| Add a part | `Add a small handle on the right side of the Body, near the top.` |
| Remove a part | `Remove the Antenna group entirely.` |
| Smooth surfaces | `Apply smooth shading to the Body and Roof groups.` |
### Color and material edits
| Goal | Recipe |
| --- | --- |
| Change one part's color | `Make the Wheels red (#cc2222). Keep the Body color unchanged.` |
| Change material type | `Change the Body material from matte plastic to polished chrome.` |
| Add an accent color | `Add a thin gold trim around the rim of the Hat.` |
"Reduce the polygon count of the Body group to 8000 triangles. Keep the
Wheels and Chimney unchanged."
"Make it less complex. Also bigger. And the wheels should be black."
The bad version mashes three unrelated edits into one prompt. Run them as
three separate edits and you'll keep all three.
### Common phrases that misfire
- **"Make it look better"** — there's no signal for what's wrong. Tell it.
- **"Add more detail"** — *where*? On which part? At what scale?
- **"More realistic"** — name the material instead ("rough cast iron",
"polished chrome", "weathered painted wood").
- **"A bit bigger"** — give a number or a multiplier.
- **"Like a [thing] but cooler"** — describe the cool part directly.
---
## Texturing — material vocabulary
When you're dialing in surface appearance (the Texture / Detailer steps),
prompts are usually shorter — 2 to 6 words — and a reference image does most
of the work.
For texture work, **paste or drop a screenshot of what you want before typing
a prompt**. The reference image dominates appearance; the prompt is a tiebreaker
on material vocabulary. Long prompts without a reference image rarely beat
short prompts with one.
These short phrases work better than full sentences:
| Surface | Phrase that works |
| --- | --- |
| Old / aged | `weathered`, `oxidized`, `peeling paint`, `green patina` |
| Smooth metal | `polished chrome`, `brushed steel`, `gold leaf` |
| Rough metal | `cast iron`, `rusted steel`, `corroded bronze` |
| Wood | `rough wood grain`, `dark walnut`, `whitewashed pine` |
| Stone | `polished marble`, `weathered sandstone`, `mossy granite` |
| Plastic / paint | `matte plastic`, `glossy enamel paint`, `flat acrylic` |
| Cloth / leather | `worn leather`, `faded denim`, `quilted cotton` |
| Glass / glow | `frosted glass`, `tinted glass`, `glowing emissive panel` |
"Weathered bronze with green patina, especially in the recessed details."
"Make it look real and old."
---
## Putting it together
A typical newbie-friendly flow:
1. **Create** with a subject + style + scale anchor. Don't sweat materials yet.
2. **Auto-refine** (no prompt) to clean up obvious geometry issues.
3. **Edit** in one or more passes — one focused change per prompt, referencing
part names from the Scene Hierarchy panel, with explicit "keep X unchanged"
for everything you're not touching.
4. **Texture** with a short material descriptor and (ideally) a reference image.
If something misfires, the fastest fix is almost always to **shrink the
prompt**, not lengthen it.
---
# Generation
Source: https://docs.thrixel.com/workflow
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Callout } from 'fumadocs-ui/components/callout';
Generate a model from a text prompt, images, or an existing mesh. All four endpoints
return a `submission_id` that you track via the endpoints on
[Track Progress](/docs/progress).
---
## Create a model
Requires API key.
Start a new generation from a text prompt, a reference image, or up to six images. At
least one of `task`, `image`, or `images` is required.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `task` | string | Natural-language description of the model to create. |
| `image` | string | Single reference image — base64 data URL or HTTPS URL. |
| `images` | string[] | Up to 6 reference images (base64 or HTTPS URLs). Mutually exclusive with `image`. |
| `model` | string | LLM model override. Leave unset to use the default. |
| `project_id` | string | Group the submission under a project. |
| `adaptive_thinking` | boolean | Enable deeper reasoning pass. Slower but higher quality. Default: `false`. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/phase1/submit \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{"task": "a small stone gargoyle holding a lantern"}'
```
```python
import requests
requests.post(
"https://api.thrixel.com/api/v1/phase1/submit",
headers={"Authorization": "Bearer sk-thrixel-"},
json={"task": "a small stone gargoyle holding a lantern"},
)
```
### Example Response
```json
{
"submission_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "queued",
"created_at": "2026-04-20T14:32:11Z"
}
```
---
## Auto-refine
Requires API key.
Run a vision-critique pass on a completed Phase 1 or Phase 2 submission. The backend
inspects the result and produces an improved version.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `parent_submission_id` | string (required) | ID of a completed Phase 1 or Phase 2 submission. |
| `project_id` | string | Inherit or override the parent's project. |
| `adaptive_thinking` | boolean | Enable deeper reasoning pass. Default: `false`. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/phase2/submit \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{"parent_submission_id": "a1b2c3d4-..."}'
```
### Example Response
```json
{
"submission_id": "b2c3d4e5-...",
"status": "queued",
"created_at": "2026-04-20T14:35:22Z"
}
```
---
## Modify a model
Requires API key.
Apply a natural-language edit to an existing model. Example: "make the lantern glow blue".
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `parent_submission_id` | string (required) | ID of the submission to modify. |
| `modification_request` | string (required) | Description of the change to apply. |
| `project_id` | string | Inherit or override the parent's project. |
| `adaptive_thinking` | boolean | Enable deeper reasoning pass. Default: `false`. |
| `focus_on_node_names` | string[] | Part names from the parent mesh to scope the edit to. Every part outside the list is held bit-identical. Empty/omitted = whole-mesh edit. Get part names from `/api/v1/{submission_id}/hierarchy`. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/phase3/submit \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{
"parent_submission_id": "a1b2c3d4-...",
"modification_request": "make the lantern glow blue"
}'
```
```bash
# Edit scoped to specific parts — the LLM is told to leave every
# other part bit-identical to the input.
curl -X POST https://api.thrixel.com/api/v1/phase3/submit \
-H "Authorization: Bearer sk-thrixel-" \
-H "Content-Type: application/json" \
-d '{
"parent_submission_id": "a1b2c3d4-...",
"modification_request": "make it black",
"focus_on_node_names": ["Seat_Group"]
}'
```
### Example Response
```json
{
"submission_id": "c3d4e5f6-...",
"status": "queued",
"created_at": "2026-04-20T14:38:01Z"
}
```
---
## Upload an existing model
Requires API key.
Bring your own GLB file into Thrixel so you can run Phase 2, 3, or 4 operations on it.
Multipart form upload, max 100 MB.
### Request Body
| Name | Type | Description |
| --- | --- | --- |
| `glb_file` | file (required) | Binary `.glb` file (`Content-Type: multipart/form-data`). |
| `task` | string | Optional label describing the uploaded model. Max 2000 chars. |
| `project_id` | string | Assign to a project. |
### Example Request
```bash
curl -X POST https://api.thrixel.com/api/v1/phase5/submit \
-H "Authorization: Bearer sk-thrixel-" \
-F "glb_file=@./my_model.glb" \
-F "task=hand-sculpted base mesh"
```
```python
import requests
requests.post(
"https://api.thrixel.com/api/v1/phase5/submit",
headers={"Authorization": "Bearer sk-thrixel-"},
files={"glb_file": open("my_model.glb", "rb")},
data={"task": "hand-sculpted base mesh"},
)
```
### Example Response
```json
{
"submission_id": "d4e5f6a7-...",
"status": "completed",
"original_filename": "my_model.glb",
"file_size_bytes": 4528192,
"created_at": "2026-04-20T14:40:15Z"
}
```
---