GlossiDocs
Open Glossi
API

Control every API step

Create models, confirm uploads, build projects, start renders, inspect status, retry failures, and retrieve outputs through individual endpoints.

This guide walks through each API endpoint individually, giving you full control over the model upload, project creation, and rendering process.

Looking for a simpler approach? The Jobs endpoint combines all these steps into a single workflow.


Step 1: Upload a 3D Model

Uploading a model is a two-step process: first you create the model record and get a signed upload URL, then you upload the file directly to our storage.

1.1 Create the Model Record

Request:

POST https://api.glossi.io/api/v1/models

Headers:

HeaderValue
X-API-KeyYour API key
Content-Typeapplication/json

Body:

{
  "models": [
    {
      "name": "My Product",
      "fileName": "product.glb",
      "fileType": "glb"
    }
  ]
}

Example with cURL:

curl -X POST https://api.glossi.io/api/v1/models \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "models": [
      {
        "name": "My Product",
        "fileName": "product.glb",
        "fileType": "glb"
      }
    ]
  }'

Response:

{
  "models": [
    {
      "modelId": "abc123-uuid",
      "name": "My Product",
      "uploadUrl": "https://s3.amazonaws.com/...",
      "uploadKey": "workspaces/.../model.glb"
    }
  ]
}

Save these values from the response:

  • modelId → You'll use this in Steps 1.3 and 4
  • uploadUrl → You'll upload your file to this URL in Step 1.2

You can create up to 50 models per request. Source model files are limited to 1 GB each — larger uploads are rejected at confirmation with a 413 MODEL_FILE_TOO_LARGE error.

Note The uploadUrl expires after 1 hour. If you're uploading many files, make sure to complete the upload within this time window.

1.2 Upload the File to S3

Now upload your 3D file directly to the signed URL. This request goes directly to S3, not to Glossi's API.

Request:

PUT <uploadUrl from previous response>

Headers:

HeaderValue
Content-Typemodel/gltf-binary (for GLB files)

Body: Your binary file data

Example with cURL:

curl -X PUT "<uploadUrl>" \
  -H "Content-Type: model/gltf-binary" \
  --data-binary @/path/to/your/model.glb

In Postman:

  1. Set method to PUT
  2. Paste the uploadUrl as the URL
  3. Go to Headers and add Content-Type: model/gltf-binary
  4. Go to Body, select binary, and choose your file
  5. Click Send

Note Don't include your API key for this request - the signed URL already contains authentication.

A successful upload returns an empty response with status 200 OK.

1.3 Confirm the Upload

After uploading to S3, call this endpoint to finalize the model. This updates the file path in our database and marks the model as ready for use in projects.

Request:

POST https://api.glossi.io/api/v1/models/{modelId}/confirm

Headers:

HeaderValue
X-API-KeyYour API key
Content-Typeapplication/json

Body:

{
  "fileType": "glb"
}

Example with cURL:

curl -X POST https://api.glossi.io/api/v1/models/abc123-uuid/confirm \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"fileType": "glb"}'

Response:

{
  "modelId": "abc123-uuid",
  "name": "My Product",
  "status": "PENDING",
  "filePath": "workspaces/.../model.glb"
}

Your model upload is confirmed, but it still needs to be processed before you can use it in a project.

Note Processing starts as soon as the file lands in storage, so the status in this response may already be ANALYZING or even READY for small models — not just PENDING.


Step 2: Wait for Model Processing (Important!)

After confirming the upload, the model goes through processing. You must wait until it's ready before creating a project.

Request:

POST https://api.glossi.io/api/v1/models/status

Headers:

HeaderValue
X-API-KeyYour API key
Content-Typeapplication/json

Body:

{
  "modelIds": ["abc123-uuid"]
}

Example with cURL:

curl -X POST https://api.glossi.io/api/v1/models/status \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"modelIds": ["abc123-uuid"]}'

Response:

{
  "models": [
    {
      "modelId": "abc123-uuid",
      "name": "My Product",
      "status": "READY",
      "ready": true
    }
  ],
  "allReady": true,
  "notFound": []
}

Key fields:

  • ready: true when the model can be used in a project
  • allReady: true when ALL models in the request are ready
  • status: Current processing status (PENDING, ANALYZING, READY, etc.)

Polling Example

In automation tools like n8n, poll every 5-10 seconds until allReady is true:

# Simple polling loop
while true; do
  RESPONSE=$(curl -s -X POST https://api.glossi.io/api/v1/models/status \
    -H "X-API-Key: $API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"modelIds": ["abc123-uuid"]}')

  ALL_READY=$(echo $RESPONSE | jq -r '.allReady')

  if [ "$ALL_READY" = "true" ]; then
    echo "Model ready! Proceeding to create project..."
    break
  fi

  echo "Model still processing..."
  sleep 5
done

Note Processing time depends on source complexity, texture payload, conversion capacity, and format. Poll the model state instead of assuming a fixed duration.


Step 3: Find a Template

Before creating a project, you'll need a template ID. Templates define the lighting, camera angles, and background for your renders. You can list available templates via the API.

Request:

GET https://api.glossi.io/api/v1/templates

Headers:

HeaderValue
X-API-KeyYour API key

Example with cURL:

curl https://api.glossi.io/api/v1/templates \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

Response:

{
  "templates": [
    {
      "templateId": "abc123-template",
      "name": "Studio Light",
      "description": "Clean studio lighting setup"
    },
    {
      "templateId": "def456-template",
      "name": "Outdoor Scene",
      "description": "Natural outdoor lighting"
    }
  ]
}

Save the templateId for the template you want to use in Step 4.

Filtering Templates

By default, you'll see both public templates (available to everyone) and any custom templates in your workspace. You can filter these:

# Only public templates
curl "https://api.glossi.io/api/v1/templates?internal=false" \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

# Only your workspace templates
curl "https://api.glossi.io/api/v1/templates?public=false" \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

Step 4: Create a Project

A project combines your 3D model with a template (lighting, camera angles, background). You'll need the modelId you saved from Step 1.1.

Important Your model must be in READY status before you can create a project with it. See Step 2 for how to check model status.

Request:

POST https://api.glossi.io/api/v1/projects

Headers:

HeaderValue
X-API-KeyYour API key
Content-Typeapplication/json

Body:

{
  "name": "Product Photoshoot",
  "modelIds": ["abc123-uuid"],
  "templateId": "your-template-uuid"
}

Example with cURL:

curl -X POST https://api.glossi.io/api/v1/projects \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Product Photoshoot",
    "modelIds": ["abc123-uuid"],
    "templateId": "template-uuid"
  }'

Response:

{
  "projects": [
    {
      "projectId": "project-uuid",
      "name": "Product Photoshoot",
      "modelIds": ["abc123-uuid"],
      "templateId": "template-uuid"
    }
  ]
}

Save the projectId - you'll use it in Step 5 to start a render.

Creating Multiple Projects at Once

If you have multiple models and want a separate project for each (up to 50 models per request):

{
  "modelIds": ["model-1-uuid", "model-2-uuid", "model-3-uuid"],
  "templateId": "template-uuid",
  "createPerModel": true
}

This creates 3 projects, one for each model.


Step 5: Start a Render

Now you can render your project to generate images and videos. Use the projectId you saved from Step 4.

Request:

POST https://api.glossi.io/api/v1/renders

Headers:

HeaderValue
X-API-KeyYour API key
Content-Typeapplication/json

Body:

{
  "projectIds": ["project-uuid"],
  "settings": {
    "renderBookmarks": true,
    "renderShots": true,
    "renderVariants": true,
    "imageQuality": 1,
    "videoQuality": 1
  }
}

Render Settings Explained

SettingDefaultOptions
renderBookmarkstrueRender all saved camera angles (images)
renderShotstrueRender all video shots
renderVariantstrueRender all material/color variants
imageQuality10=720p, 1=1080p, 2=4K
imageFormat"PNG""PNG" (8-bit) or "TIFF" (16-bit)
videoQuality10=Fast (preview), 1=Best (production)
videoResolution10=720p, 1=1080p, 2=4K

Example - Render images only at 4K:

curl -X POST https://api.glossi.io/api/v1/renders \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "projectIds": ["project-uuid"],
    "settings": {
      "renderBookmarks": true,
      "renderShots": false,
      "imageQuality": 2
    }
  }'

Response:

{
  "batchId": "batch-uuid",
  "jobs": [
    {
      "jobId": "job-uuid",
      "status": "QUEUED",
      "projectId": "project-uuid"
    }
  ],
  "totalProjects": 1
}

Save the jobId - you'll use it in Step 6 to check the render status.

Rendering Multiple Projects

You can render up to 50 projects in a single request (duplicate IDs are de-duplicated):

{
  "projectIds": ["project-1", "project-2", "project-3"],
  "settings": {
    "renderBookmarks": true,
    "imageQuality": 1
  }
}

Each project gets its own render job, all sharing the batchId from the response. To poll them together, use the batch endpoint instead of polling each job:

GET https://api.glossi.io/api/v1/renders/batch/{batchId}
{
  "batchId": "batch-uuid",
  "total": 3,
  "counts": { "QUEUED": 1, "RENDERING": 1, "COMPLETED": 1 },
  "jobs": [
    { "jobId": "job-1", "status": "COMPLETED", "projectId": "project-1" },
    { "jobId": "job-2", "status": "RENDERING", "projectId": "project-2" },
    { "jobId": "job-3", "status": "QUEUED", "projectId": "project-3" }
  ]
}

Step 6: Check Render Status

Renders take time to complete. Use the jobId from Step 5 to poll for progress.

Request:

GET https://api.glossi.io/api/v1/renders/{jobId}

Headers:

HeaderValue
X-API-KeyYour API key

Example with cURL:

curl https://api.glossi.io/api/v1/renders/job-uuid \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

Response (in progress):

{
  "jobId": "job-uuid",
  "status": "RENDERING",
  "projectId": "project-uuid"
}

Response (complete):

{
  "jobId": "job-uuid",
  "status": "COMPLETED",
  "projectId": "project-uuid",
  "renders": [
    {
      "id": "render-uuid-1",
      "name": "Front View - Default",
      "filePath": "workspaces/.../render.png",
      "fileType": "PNG",
      "isVideo": false
    },
    {
      "id": "render-uuid-2",
      "name": "360 Spin - Default",
      "filePath": "workspaces/.../video.mp4",
      "fileType": "MP4",
      "isVideo": true
    }
  ]
}

Key fields in renders:

  • name: Display name of the render (e.g., "Front View - Oak")
  • filePath: Direct (unsigned) URL to the file in storage
  • fileType: Format of the file (PNG, MP4, PRORES, etc.)
  • isVideo: true for videos, false for images

Note The renders array contains all images and videos generated for this job (bookmarks, shots, and variants).

Downloading a Render

For a guaranteed-working download link, use the download endpoint with a render's id. It responds with a 302 redirect to a signed URL that's valid for 1 hour:

curl -L -o render.png "https://api.glossi.io/api/v1/renders/render-uuid-1/download" \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

(The -L flag tells cURL to follow the redirect.)

Job Statuses

StatusMeaning
QUEUEDWaiting to start
RENDERINGCurrently processing
COMPLETEDFinished successfully
FAILEDSomething went wrong (check errorMessage)
CANCELLEDCancelled before rendering started

Cancelling a Render

A render job can be cancelled while it's still QUEUED:

POST https://api.glossi.io/api/v1/renders/{jobId}/cancel

Returns { "jobId": "...", "status": "CANCELLED" } on success, or 400 INVALID_STATUS if the job has already started. In practice the window is short - jobs usually move to RENDERING within seconds of being created - so treat cancellation as best-effort.

Retrying Failed Renders

If a render fails, you can retry it:

Request:

POST https://api.glossi.io/api/v1/renders/{jobId}/retry

Headers:

HeaderValue
X-API-KeyYour API key

Example with cURL:

curl -X POST https://api.glossi.io/api/v1/renders/job-uuid/retry \
  -H "X-API-Key: glsi_xxxxxxxxxx_xxxxx"

Response:

{
  "jobId": "job-uuid",
  "status": "QUEUED",
  "projectId": "project-uuid",
  "batchId": "new-batch-uuid"
}

The job will be re-queued and you can poll for status as before. Note that only jobs with status FAILED can be retried.


Step 7: Set Up Webhooks (Optional)

Instead of polling, you can receive notifications when important events occur.

Full documentation See the Webhooks Guide for complete details on all events, payload formats, and signature verification.

Available Events

EventDescription
model.processedModel upload processing completed successfully
model.failedModel processing failed
project.createdA project was created
job.completeA job workflow completed successfully
job.failedA job workflow failed
render.completeA render completed successfully
render.failedA render failed

Configure Your Webhook

Request:

PUT https://api.glossi.io/api/v1/webhooks

Body:

{
  "url": "https://your-app.com/webhooks/glossi",
  "events": [
    "model.processed",
    "model.failed",
    "project.created",
    "job.complete",
    "job.failed",
    "render.complete",
    "render.failed"
  ],
  "enabled": true
}

Response:

{
  "id": "webhook-uuid",
  "url": "https://your-app.com/webhooks/glossi",
  "secret": "your-webhook-secret",
  "events": ["model.processed", "job.complete", "render.complete", ...],
  "enabled": true
}

Save the secret - you'll use it to verify webhook signatures.

Webhook Payloads

When a model finishes processing:

{
  "event": "model.processed",
  "timestamp": "2026-07-28T10:30:00.000Z",
  "data": {
    "modelId": "model-uuid",
    "name": "Chair Model",
    "status": "READY",
    "glbFilePath": "https://s3.../file.glb",
    "thumbnailUrl": "https://s3.../file.jpg"
  }
}

When a job completes:

{
  "event": "job.complete",
  "timestamp": "2026-07-28T10:30:00.000Z",
  "data": {
    "jobId": "job-uuid",
    "status": "COMPLETE",
    "results": [
      {
        "model": { "id": "model-uuid", "name": "Chair" },
        "project": { "id": "project-uuid", "name": "Chair Project" }
      }
    ]
  }
}

When a render completes:

{
  "event": "render.complete",
  "timestamp": "2026-07-28T10:30:00.000Z",
  "data": {
    "jobId": "render-job-uuid",
    "status": "COMPLETED",
    "projectId": "project-uuid",
    "renderIds": ["render-uuid-1", "render-uuid-2"]
  }
}

Verify Webhook Signatures

Always verify the X-Glossi-Signature header to ensure the request came from Glossi. Compute the HMAC over the raw request body bytes - not a re-serialized copy of the parsed JSON:

const crypto = require("crypto")

// rawBody is a Buffer or string of the exact bytes received
function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex")
  const a = Buffer.from(signature || "")
  const b = Buffer.from(expected)
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

See the Webhooks Guide for complete examples in Node.js, Python, and PHP, including how to access the raw body in each framework.


Next Steps

On this page