Skip to main content

API Contracts Documentation

This document describes the API endpoints used by the frontend and E2E tests. It serves as a contract reference to ensure tests match actual API behavior.

Models

List Models

GET /models
GET /models?packId={id}
GET /models?projectId={id}

Response:

[
{
"id": 1,
"name": "test-cube",
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z",
"activeVersionId": 1,
"thumbnailUrl": "/models/1/thumbnail/file?t=..."
}
]

Get Model Details

GET /models/{id}

Response: Returns model details WITHOUT versions array. Use version-specific endpoints for version data.

{
"id": 1,
"name": "test-cube",
"activeVersionId": 1,
"files": [...],
"packs": [...],
"projects": [...],
"textureSets": [...]
}

Note: No versions property exists in this response.

Upload Model (Create)

POST /models
Content-Type: multipart/form-data

Form fields:

  • file: Model file (.glb, .fbx, etc.)

Upload New Version

POST /models/{id}/versions?setAsActive=false
Content-Type: multipart/form-data

Form fields:

  • file: Model file

Required query param: setAsActive=false or setAsActive=true

Set Active Version

POST /models/{id}/active-version/{versionId}

Delete Model (Soft)

DELETE /models/{id}

Delete Version (Soft)

DELETE /models/{modelId}/versions/{versionId}

Sprites

List Sprites

GET /sprites

Get Sprite

GET /sprites/{id}

Create Sprite

POST /sprites
Content-Type: multipart/form-data

Form fields:

  • file: Image file
  • name: Sprite name
  • spriteType: 1 (static), 2 (sprite sheet), 3 (GIF), 4 (APNG), 5 (WebP)
  • categoryId (optional): Category to assign

Update Sprite

PUT /sprites/{id}
Content-Type: application/json

Body:

{
"name": "new-name",
"categoryId": 1
}

Note: Uses PUT, not PATCH.

Delete Sprite (Soft)

DELETE /sprites/{id}

Sprite Categories

List Categories

GET /sprite-categories

Response:

{
"categories": [{ "id": 1, "name": "Test Category", "description": "..." }]
}

Create Category

POST /sprite-categories
Content-Type: application/json

Body:

{
"name": "Category Name",
"description": "Optional description"
}

Update Category

PUT /sprite-categories/{id}

Delete Category

DELETE /sprite-categories/{id}

Packs

List Packs

GET /packs

Get Pack

GET /packs/{id}

Create Pack

POST /packs
Content-Type: application/json

Body:

{
"name": "Pack Name",
"description": "Optional"
}

Add Model to Pack

POST /packs/{packId}/models/{modelId}

Remove Model from Pack

DELETE /packs/{packId}/models/{modelId}

Projects

List Projects

GET /projects

Create Project

POST /projects
Content-Type: application/json

Body:

{
"name": "Project Name",
"description": "Optional"
}

Add Model to Project

POST /projects/{projectId}/models/{modelId}

Remove Model from Project

DELETE /projects/{projectId}/models/{modelId}

Texture Sets

List Texture Sets

GET /texture-sets?kind=0&page=1&pageSize=20

Query params:

  • kind (optional): 0 = ModelSpecific, 1 = Universal
  • page, pageSize: pagination

Response includes thumbnailPath (string, nullable) for each texture set.

Get Texture Set Detail

GET /texture-sets/{id}

Response includes thumbnailPath and pngThumbnailPath (string, nullable).

Create Texture Set

POST /texture-sets
Content-Type: application/json
{
"name": "My Texture Set",
"kind": 0
}
  • kind (optional): 0 = ModelSpecific (default), 1 = Universal

Create Texture Set with File

POST /texture-sets/with-file?kind=1
Content-Type: multipart/form-data
  • kind query param (optional): 0 or 1

Update Texture Set Kind

PUT /texture-sets/{id}/kind
Content-Type: application/json
{
"kind": 1
}
  • kind: 0 = ModelSpecific, 1 = Universal
  • Returns updated texture set response

Update Tiling Scale (Universal sets only)

PUT /texture-sets/{id}/tiling-scale
Content-Type: application/json
{
"tilingScaleX": 2.0,
"tilingScaleY": 2.0
}

Returns 400 if the texture set is not Universal kind.

Associate with Model Version

POST /texture-sets/{packId}/model-versions/{modelVersionId}?materialName={optional}

Optional materialName and variantName query parameters link the texture set to a specific material slot and preset/variant. When omitted, both default to empty string (applies to all materials / Default preset). Composite PK: (ModelVersionId, TextureSetId, MaterialName, VariantName).

Disassociate from Model Version

DELETE /texture-sets/{packId}/model-versions/{modelVersionId}?materialName={optional}

When materialName is provided, removes only the specific material mapping. When omitted, removes all mappings for that texture set from the model version.

Set Material Names (Worker API)

PUT /model-versions/{versionId}/material-names
X-Api-Key: {workerApiKey}
Content-Type: application/json

{
"materialNames": ["Material.001", "Material.002"]
}

Called by the asset processor after extracting material names from the 3D model file. Material names are stored as a PostgreSQL text[] column on ModelVersion.

Model Version Response DTOs

GET /models/{modelId}/versions now returns additional fields:

{
"id": 1,
"materialNames": ["Material.001", "Material.002"],
"textureMappings": [
{ "materialName": "", "textureSetId": 5, "variantName": "" },
{
"materialName": "Material.001",
"textureSetId": 7,
"variantName": ""
},
{
"materialName": "Material.001",
"textureSetId": 8,
"variantName": "Damaged"
}
],
"textureSetIds": [5, 7]
}

Worker - Dequeue Thumbnail Job

POST /thumbnail-jobs/dequeue

Response includes MainVariantName (string) and TextureMappings (array of { MaterialName, TextureSetId, VariantName }) for per-material texture application during thumbnail generation.


Thumbnails

Get Model Thumbnail

GET /models/{id}/thumbnail/file

Regenerate Model Thumbnail

POST /models/{id}/thumbnail/regenerate

Upload Custom Model Thumbnail

POST /models/{id}/thumbnail/upload
Content-Type: multipart/form-data

Form fields:

  • file: Image file (PNG, JPG, WebP)

Get Texture Set Thumbnail (WebP)

GET /texture-sets/{id}/thumbnail/file

Get Texture Set Thumbnail (PNG)

GET /texture-sets/{id}/thumbnail/png-file

Regenerate Texture Set Thumbnail

POST /texture-sets/{id}/thumbnail/regenerate

Auto-enqueues a sphere-preview render job for Universal texture sets.

Upload Texture Set Thumbnail (WebP)

POST /texture-sets/{id}/thumbnail/upload
Content-Type: multipart/form-data

Form fields:

  • file: WebP thumbnail file

Upload Texture Set Thumbnail (PNG)

POST /texture-sets/{id}/thumbnail/png-upload
Content-Type: multipart/form-data

Form fields:

  • file: PNG poster image

Finish Texture Set Thumbnail Job

POST /thumbnail-jobs/texture-sets/{jobId}/finish
Content-Type: application/json
{
"success": true,
"metadata": { "frames": 30, "format": "webp" },
"errorMessage": null
}

Common Patterns

Error Response

{
"error": "ErrorCode",
"message": "Human-readable message"
}

Soft Delete Pattern

All delete endpoints perform soft deletes. Items move to recycle bin and can be restored.

File URLs

File downloads use the pattern: /files/{fileId}/download