GlossiDocs
Open Glossi

Run a job through one workflow

The Jobs endpoint combines the entire workflow into fewer steps. Instead of managing each step individually, you create a single job that orchestrates everything.

Why Use Jobs?

Traditional Flow (8+ steps)Jobs Flow (4 steps)
Create model → Upload → Confirm → Poll status → Create project → Render → Poll renderCreate job → Upload → Confirm → Poll until done

Step 1: Create a Job

Endpoint:

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

Headers:

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

Body (without rendering):

{
  "models": [
    { "fileName": "chair.glb", "fileType": "glb" },
    { "fileName": "table.glb", "fileType": "glb" }
  ],
  "templateId": "your-template-uuid",
  "createProjects": true
}

Body (with rendering):

{
  "models": [
    { "fileName": "chair.glb", "fileType": "glb" }
  ],
  "templateId": "your-template-uuid",
  "renderSettings": {
    "renderBookmarks": true,
    "imageQuality": 1
  }
}

Response:

{
  "jobId": "job-uuid",
  "status": "AWAITING_UPLOADS",
  "models": [
    {
      "id": "model-1-uuid",
      "fileName": "chair.glb",
      "uploadUrl": "https://s3.../signed-url-1",
      "uploadKey": "workspaces/.../model.glb"
    },
    {
      "id": "model-2-uuid",
      "fileName": "table.glb",
      "uploadUrl": "https://s3.../signed-url-2",
      "uploadKey": "workspaces/.../model.glb"
    }
  ]
}

Step 2: Upload Files to S3

Upload each file directly to its signed URL. This request goes directly to S3, not to Glossi's API.

Endpoint:

PUT <uploadUrl from Step 1 response>

Headers:

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

Body: Your binary file data

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.

Repeat this for each model in your job.


Step 3: Confirm Uploads

After uploading all files, tell the job that uploads are complete. This sets the file paths and advances the job to processing.

Endpoint:

POST https://api.glossi.io/api/v1/jobs/{jobId}/confirm-uploads

Headers:

HeaderValue
X-API-KeyYour API key

Body: None required

Response:

{
  "jobId": "job-uuid",
  "status": "PROCESSING",
  "modelsConfirmed": 2
}

Step 4: Poll for Completion

Poll this endpoint to check status. Polling is what moves the job through its phases — each status check advances the workflow to the next step when the previous one has finished, so keep polling until you reach a terminal status.

Endpoint:

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

Headers:

HeaderValue
X-API-KeyYour API key

Response (in progress):

{
  "jobId": "job-uuid",
  "status": "PROCESSING",
  "progress": {
    "modelsUploaded": 2,
    "modelsReady": 1,
    "modelsTotal": 2,
    "projectsCreated": 0,
    "projectsTotal": 2,
    "rendersComplete": 0,
    "rendersTotal": 0
  }
}

Response (complete):

{
  "jobId": "job-uuid",
  "status": "COMPLETE",
  "batchProject": {
    "id": "batch-uuid",
    "title": "API Job - 2026-07-28"
  },
  "progress": {
    "modelsUploaded": 2,
    "modelsReady": 2,
    "modelsTotal": 2,
    "projectsCreated": 2,
    "projectsTotal": 2,
    "rendersComplete": 0,
    "rendersTotal": 0
  },
  "results": [
    {
      "model": { "id": "model-1-uuid", "name": "chair.glb" },
      "project": { "id": "project-1-uuid", "name": "chair.glb" }
    },
    {
      "model": { "id": "model-2-uuid", "name": "table.glb" },
      "project": { "id": "project-2-uuid", "name": "table.glb" }
    }
  ]
}

Note Project names in results are derived from each model's file name. When renders were requested, each result also includes a renders array of { id, imageUrl, fileType } entries.


Job Status Flow

AWAITING_UPLOADS → PROCESSING → CREATING_PROJECTS → RENDERING → COMPLETE
                       ↓              ↓                 ↓
                    FAILED         FAILED            FAILED
StatusDescription
AWAITING_UPLOADSWaiting for files to be uploaded and confirmed
PROCESSINGModels are being processed
CREATING_PROJECTSProjects are being created
RENDERINGRenders in progress (if rendering requested)
COMPLETEAll done
FAILEDSomething went wrong (check error field)

Job Options

FieldTypeDefaultDescription
modelsarrayRequiredModels to upload (fileName + fileType). Maximum 50 per job
templateIdstring-Template to use for projects (required if rendering)
createProjectsbooleantrueCreate projects after models are ready
renderSettingsobject-Render settings. Include renderBookmarks: true or renderShots: true to trigger rendering
webhookUrlstring-URL to receive completion notification

Tip To render images, include "renderSettings": { "renderBookmarks": true }. To skip rendering, omit renderSettings entirely.


BatchProject

When you upload multiple models with a templateId, the resulting projects are automatically grouped into a BatchProject. The batchProject field in the response contains the ID you can use to manage them together. Jobs with a single model, or without a template, are not grouped and won't include this field.


Complete Example with cURL

# 1. Create job
JOB_RESPONSE=$(curl -s -X POST https://api.glossi.io/api/v1/jobs \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "models": [
      { "fileName": "chair.glb", "fileType": "glb" }
    ],
    "templateId": "your-template-uuid",
    "createProjects": true
  }')

JOB_ID=$(echo $JOB_RESPONSE | jq -r '.jobId')
UPLOAD_URL=$(echo $JOB_RESPONSE | jq -r '.models[0].uploadUrl')

# 2. Upload file to S3
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: model/gltf-binary" \
  --data-binary @chair.glb

# 3. Confirm uploads
curl -X POST "https://api.glossi.io/api/v1/jobs/$JOB_ID/confirm-uploads" \
  -H "X-API-Key: $API_KEY"

# 4. Poll for completion
while true; do
  STATUS_RESPONSE=$(curl -s "https://api.glossi.io/api/v1/jobs/$JOB_ID" \
    -H "X-API-Key: $API_KEY")

  STATUS=$(echo $STATUS_RESPONSE | jq -r '.status')
  echo "Status: $STATUS"

  if [ "$STATUS" = "COMPLETE" ] || [ "$STATUS" = "FAILED" ]; then
    echo "Final response:"
    echo $STATUS_RESPONSE | jq
    break
  fi

  sleep 5
done

Using Webhooks with Jobs

You can provide a webhookUrl when creating the job to receive a notification when it completes or fails:

{
  "models": [
    { "fileName": "chair.glb", "fileType": "glb" }
  ],
  "templateId": "your-template-uuid",
  "webhookUrl": "https://your-app.com/webhooks/glossi"
}

Important Webhooks reduce polling frequency but do not replace polling. A job's state only advances when its status is checked, so you must keep polling GET /jobs/{jobId} (a slow interval like every 30-60 seconds is fine) even when using a webhookUrl. Treat the webhook as a low-latency signal and the polled status as the source of truth.

The per-job webhookUrl notification is different from your workspace webhook configuration:

  • It is not signed — there is no X-Glossi-Signature header to verify. Use an unguessable URL if you rely on it.
  • The payload is flat, not wrapped in the standard envelope:
{
  "event": "job.complete",
  "jobId": "job-uuid",
  "status": "COMPLETE",
  "results": [ ... ]
}

On failure, event is "job.failed" and an error string replaces results. If you also have a workspace webhook subscribed to job.complete / job.failed, you'll receive both: the signed workspace webhook (see Webhooks) and this unsigned per-job notification.

On this page