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
versionsproperty 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 filename: Sprite namespriteType: 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= Universalpage,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
kindquery param (optional):0or1
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