GlossiDocs
Open Glossi
API

Populate the library

Import models from URLs, upload props and decals, and bring in PBR materials from texture files through the API.

The rendering workflow starts from a model, but a workspace library holds more than models: props to dress scenes, decals to apply to surfaces, and materials to swap onto products. Each has its own API surface, and they all follow the same shape as models: create a record and get a signed upload URL, PUT the file, confirm, then poll status until it is ready.

Everything on this page is also available to AI agents through the Glossi MCP connector; the same server code backs both.


Import a model from a URL

When a model already lives at a public URL (a CDN, a signed cloud-storage link, a supplier's download page), Glossi can fetch it for you instead of you streaming the bytes through your integration.

POST https://api.glossi.io/api/v1/models/import
curl -X POST https://api.glossi.io/api/v1/models/import \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://cdn.example.com/models/chair.glb", "name": "Chair"}'

Response (202 Accepted):

{
  "modelId": "abc123-uuid",
  "name": "Chair",
  "fileType": "glb",
  "status": "UPLOADING"
}
FieldRequiredDescription
urlYesPublic http(s) URL. Private network addresses are refused, and every redirect is checked the same way
nameNoLibrary name. Defaults to the file name in the URL
fileTypeNoFile extension. Needed when the URL path has no extension, which signed links often lack

The download and confirm run in the background. Poll POST /models/status as in the complete workflow: the model moves UPLOADING → PENDING → ANALYZING → READY. If the download fails, the model is left FAILED and its status entry carries an error field with the reason. Files are capped at 1 GB.


Upload props

Props are 3D files that dress a scene: furniture, plants, packaging. They accept the same formats as models and go through the same converter.

Create props and get upload URLs

POST https://api.glossi.io/api/v1/props
curl -X POST https://api.glossi.io/api/v1/props \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "props": [
      { "fileName": "wooden_stool.glb", "collection": "Furniture" },
      { "fileName": "fern.fbx", "collection": "Plants" }
    ]
  }'

Response (201 Created):

{
  "props": [
    {
      "propId": "prop-uuid-1",
      "name": "Wooden Stool",
      "uploadUrl": "https://s3.amazonaws.com/...",
      "uploadKey": "workspaces/.../PROPS/prop-uuid-1/model.glb"
    },
    {
      "propId": "prop-uuid-2",
      "name": "Fern",
      "uploadUrl": "https://s3.amazonaws.com/...",
      "uploadKey": "workspaces/.../PROPS/prop-uuid-2/model.fbx"
    }
  ]
}

Up to 50 props per request. collection is a name, not an id: the collection is created in your workspace the first time you use it and matched by name after that. name is optional and defaults to a cleaned-up file name.

Upload and confirm

PUT each file to its uploadUrl within one hour, then confirm each prop:

curl -X PUT "https://s3.amazonaws.com/..." --data-binary @wooden_stool.glb

curl -X POST https://api.glossi.io/api/v1/props/prop-uuid-1/confirm \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

Wait until ready

curl -X POST https://api.glossi.io/api/v1/props/status \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"propIds": ["prop-uuid-1", "prop-uuid-2"]}'
{
  "props": [
    { "propId": "prop-uuid-1", "name": "Wooden Stool", "status": "READY", "ready": true },
    { "propId": "prop-uuid-2", "name": "Fern", "status": "ANALYZING", "ready": false }
  ],
  "allReady": false,
  "notFound": []
}

Poll every few seconds until allReady is true. GET /props lists the library (filter by status, name, or collectionId) and GET /props/{propId} returns one prop.


Upload decals

Decals are images (logos, labels, graphics) applied to model surfaces. They need no conversion, so they are ready as soon as you confirm.

Create decals and get upload URLs

POST https://api.glossi.io/api/v1/decals
curl -X POST https://api.glossi.io/api/v1/decals \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"decals": [{ "fileName": "brand_logo.png", "collection": "Logos" }]}'

Response (201 Created):

{
  "decals": [
    {
      "decalId": "decal-uuid",
      "name": "Brand Logo",
      "uploadUrl": "https://s3.amazonaws.com/...",
      "uploadKey": "workspaces/.../DECALS/decal-uuid/source.png",
      "contentType": "image/png"
    }
  ]
}

Accepted formats: png, jpg, jpeg, webp, gif, svg, tif, tiff. Up to 50 per request.

Upload and confirm

The upload must send the returned contentType. The signed URL is bound to it, so a PUT without that exact Content-Type header is refused by storage.

curl -X PUT "https://s3.amazonaws.com/..." \
  -H "Content-Type: image/png" \
  --data-binary @brand_logo.png

curl -X POST https://api.glossi.io/api/v1/decals/decal-uuid/confirm \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"
{ "decalId": "decal-uuid", "status": "READY" }

Confirm reads the image's dimensions and marks it ready. POST /decals/status, GET /decals, and GET /decals/{decalId} mirror the prop endpoints.


Import materials

A material is a set of texture images, one per PBR channel: diffuse, normal, roughness, metallic, depth, and opacity. You supply the channels you have. Channels you leave out stay empty unless you ask Glossi to generate them when you confirm. To create a material from a description instead, see Generate a material from a description or a photo.

Plan a folder import (optional)

If you have a folder of textures named by convention (Wood_Oak_BaseColor.png, Wood_Oak_Normal.png, ...), this endpoint groups them into materials for you. Nothing is uploaded. For packs that arrive as .zip files, unzip each into its own folder first and include those paths. (In Claude.ai and Claude Desktop, the upload_materials panel opens zips for you.)

curl -X POST https://api.glossi.io/api/v1/materials/plan-import \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"paths": ["Wood_Oak_BaseColor.png", "Wood_Oak_Normal.png", "Wood_Oak_AO.png", "Leather_ORM.png"]}'
{
  "materials": [
    {
      "name": "Wood Oak",
      "textures": { "diffuse": "Wood_Oak_BaseColor.png", "normal": "Wood_Oak_Normal.png" }
    }
  ],
  "ignored": ["Wood_Oak_AO.png"],
  "packed": ["Leather_ORM.png"],
  "unmatched": [],
  "skipped": [],
  "duplicates": []
}

ignored lists maps Glossi has no slot for (ambient occlusion, specular, emissive). packed lists combined maps such as ORM, which need to be exported as separate channels. unmatched lists images whose channel could not be told from the name; pass "unmatchedAsDiffuse": true to treat those as single-texture materials.

Create a material and get one upload URL per channel

POST https://api.glossi.io/api/v1/materials
curl -X POST https://api.glossi.io/api/v1/materials \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Wood Oak",
    "textures": [
      { "channel": "diffuse", "fileType": "png" },
      { "channel": "normal", "fileType": "png" }
    ],
    "collection": "Woods"
  }'

Response (201 Created):

{
  "materialId": "custom_1757930400000_k3j9x2a",
  "name": "Wood Oak",
  "collectionId": "collection-uuid",
  "uploads": [
    { "channel": "diffuse.png", "uploadUrl": "https://s3.amazonaws.com/..." },
    { "channel": "normal.png", "uploadUrl": "https://s3.amazonaws.com/..." }
  ],
  "uploadedTypes": ["diffuse.png", "normal.png"]
}

Texture formats: png, jpg, jpeg, exr. Keep name, uploadedTypes, and collectionId from this response: confirm needs them.

Upload, confirm, and wait

curl -X PUT "https://s3.amazonaws.com/..." --data-binary @Wood_Oak_BaseColor.png
curl -X PUT "https://s3.amazonaws.com/..." --data-binary @Wood_Oak_Normal.png

curl -X POST https://api.glossi.io/api/v1/materials/custom_1757930400000_k3j9x2a/confirm \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "Wood Oak", "uploadedTypes": ["diffuse.png", "normal.png"], "collectionId": "collection-uuid"}'
{ "materialId": "custom_1757930400000_k3j9x2a", "status": "processing" }

Optional fields on confirm:

FieldDefaultDescription
generateMissingMapsfalseGenerate the channels you did not upload. Needs a diffuse. Uses your workspace's usage allowance once for the material, and is not charged if the build fails.
upscaleReferencefalseUpscale your diffuse before processing. Needs a diffuse, and a paid plan.
materialTypenoneWhat the material is: fabric, metal, wood, plastic, leather, or ceramic. Generated maps follow it, so plastic or wood never comes back metallic. Leave it out if none fits.
hasOpacitynonefalse when the material has no see-through or cut-out areas: no opacity map is generated and the material stays opaque. true for lace, mesh, perforation or glass when you did not upload an opacity map. Leave it out to let Glossi decide from the image.

materialType and hasOpacity only apply with generateMissingMaps, and only shape the maps Glossi generates. Maps you upload are used as they are.

Without them, importing never uses your plan's usage. If no usage is left, generateMissingMaps answers 402 with USAGE_LIMIT_REACHED; upscaleReference on a free plan answers 403 with PLAN_LIMIT_REACHED.

Processing runs in the background and usually takes one to two minutes. Poll for it:

curl -X POST https://api.glossi.io/api/v1/materials/status \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"materialIds": ["custom_1757930400000_k3j9x2a"]}'
{
  "materials": [{ "materialId": "custom_1757930400000_k3j9x2a", "status": "ready" }],
  "allReady": true
}

status is processing, ready, or failed (with an error field). Once ready, the material can be used in material swaps and by the Studio.

Generate a material from a description or a photo

Describe a surface, give a photo of a real one, or both, and Glossi creates a tileable PBR material, with base colour, normal, roughness, metallic, and height maps, and adds it to your library. With a photo, the material reproduces that surface and your description only adjusts it ("warmer", "more worn").

curl -X POST https://api.glossi.io/api/v1/materials/generate \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 00000000-0000-4000-8000-000000000000" \
  -d '{"prompt": "worn tan saddle leather with visible pores", "materialType": "fabric", "collection": "Leathers"}'

Response (202 Accepted):

{ "materialId": "ai_1757930400000_k3j9x2a", "status": "processing", "collectionId": "collection-uuid" }
FieldDescription
promptThe surface to create, up to 1000 characters. Optional when you send a photo.
referenceImageUrlA public link to a photo of the surface: PNG, JPEG, or WEBP, up to 25 MB.
referenceKeyA photo you uploaded (see below). Send this or referenceImageUrl, not both.
nameName in the library. Defaults to a short name taken from the prompt.
materialTypefabric, metal, wood, plastic, leather, or ceramic, to tune how shiny it is. Leave it out to let Glossi decide.
collectionCollection name. Created if it does not exist.

Send a prompt, a photo, or both.

To use a photo on your computer, get an upload URL first, upload the photo with the Content-Type it returns, then pass referenceKey:

curl -X POST https://api.glossi.io/api/v1/materials/generate/reference-upload \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"fileType": "jpg"}'
{
  "referenceKey": "AI_EDIT_UPLOADS/.../reference.jpg",
  "uploadUrl": "https://s3.amazonaws.com/...",
  "contentType": "image/jpeg"
}
curl -X PUT "https://s3.amazonaws.com/..." -H "Content-Type: image/jpeg" --data-binary @leather.jpg

Uploaded photos are kept for 7 days.

Generating a material uses your workspace's usage allowance, and a generation that fails is not charged. If no usage is left the request answers 402 with USAGE_LIMIT_REACHED; a photo that cannot be used answers 400 before anything is charged. It usually takes a few minutes; poll POST /materials/status as for an import. Send an Idempotency-Key so a retried request does not generate a second material.

Find materials

GET /materials lists the workspace's materials newest first. Add ?search= for a fuzzy match on name, keywords, and colour (the same search the Studio assistant uses):

curl "https://api.glossi.io/api/v1/materials?search=brushed%20steel" \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

Manage what is in the library

Every kind supports rename and delete, and props, decals, and materials can be moved between collections. The shape is the same across kinds.

Rename or move to a collection

PATCH https://api.glossi.io/api/v1/props/{propId}
PATCH https://api.glossi.io/api/v1/decals/{decalId}
PATCH https://api.glossi.io/api/v1/materials/{materialId}
PATCH https://api.glossi.io/api/v1/models/{modelId}
curl -X PATCH https://api.glossi.io/api/v1/props/prop-uuid-1 \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "Oak Stool", "collection": "Furniture"}'
FieldDescription
nameNew library name
collectionCollection name, created if it does not exist yet. Send null to take the item out of its collection
collectionIdA collection id instead of a name. Ignored when collection is given

Models accept name only. The response is the updated item, in the same shape the GET endpoint returns.

Delete

DELETE https://api.glossi.io/api/v1/props/{propId}
DELETE https://api.glossi.io/api/v1/decals/{decalId}
DELETE https://api.glossi.io/api/v1/materials/{materialId}
DELETE https://api.glossi.io/api/v1/models/{modelId}

Deletion is permanent and removes the stored files too. Deleting a material also drops it from any surface, colorway, or showroom price list that referenced it. Deleting a model removes it from the projects that use it, so check GET /models/{modelId} first if that matters.

{ "deleted": true, "propId": "prop-uuid-1" }

Download a model file

GET https://api.glossi.io/api/v1/models/{modelId}/download?format=glb|usdz|source

Redirects to a one-hour signed URL for the converted GLB (default), the USDZ derivative, or the original upload. See the Models API for details.


Limits and errors

  • Batches: 50 items per create request, 100 ids per status request.
  • Signed upload URLs expire after one hour.
  • Files: 1 GB per model or prop.
  • 403 STORAGE_LIMIT_REACHED when the workspace's storage allowance is used up (props and models).
  • 400 VALIDATION_ERROR for a malformed request; the message says which item and why.
  • 404 NOT_FOUND when an id does not exist in your workspace.

All creation requests accept an Idempotency-Key header, as described in Getting Started.

On this page