File Uploads

File uploads in Brandmachine use REST endpoints with multipart/form-data, separate from the GraphQL endpoint. There's a dedicated upload endpoint per resource type (product studio inputs, fashion models, designer resources, patterns, etc.) rather than a single generic upload.

Authentication

All upload endpoints accept the same API token as the GraphQL endpoint:

Authorization: Bearer bm_your_token_here

See Authentication for token generation.

Upload Endpoints

All endpoints accept POST requests with multipart form data. Replace :id placeholders with the relevant UUID.

Product Studio

EndpointPurpose
POST /api/product-studios/:studioId/inputs/uploadUpload product images to an existing studio
POST /api/product-studios/zip-uploadBulk-import via ZIP (see ZIP Upload)

Campaign / Batch

EndpointPurpose
POST /api/campaign-images/:campaignImageId/products/uploadUpload product reference images for a campaign image
POST /api/batches/:batchId/images/uploadUpload batch input images (Shot Planner)
POST /api/images/upload-editedSubmit an edited version of an existing image

Fashion Models

EndpointPurpose
POST /api/fashion-models/uploadUpload a reference image for the casting flow
POST /api/fashion-models/upload-existingDirect upload of an existing model (skip casting); requires rights consent

Designer

EndpointPurpose
POST /api/designer-instances/resourcesUpload an inspiration resource (image/material/color)
POST /api/designer-revisions/measurementTrigger measurement extraction from a flat sketch

Patterns

EndpointPurpose
POST /api/patterns/uploadUpload a finished pattern image
POST /api/patterns/referencesUpload an inspiration reference for a pattern

Product Video

EndpointPurpose
POST /api/product-videos/:videoId/uploadUpload source media for a product video

Example: Upload a Product Studio Input

cURL

curl -X POST \
  https://production.api.brandmachine.shop/api/product-studios/STUDIO_UUID/inputs/upload \
  -H "Authorization: Bearer bm_your_token_here" \
  -F "file=@/path/to/product.jpg" \
  -F "viewType=front"

JavaScript (browser)

async function uploadProductInput(studioId, file, viewType = 'front') {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('viewType', viewType);

  const response = await fetch(
    `https://production.api.brandmachine.shop/api/product-studios/${studioId}/inputs/upload`,
    {
      method: 'POST',
      headers: {
        Authorization: 'Bearer bm_your_token_here',
      },
      body: formData,
    }
  );

  if (!response.ok) {
    throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
  }

  return response.json();
}

Python

import requests

url = 'https://production.api.brandmachine.shop/api/product-studios/STUDIO_UUID/inputs/upload'
headers = {'Authorization': 'Bearer bm_your_token_here'}

with open('product.jpg', 'rb') as f:
    files = {'file': f}
    data = {'viewType': 'front'}
    response = requests.post(url, headers=headers, files=files, data=data)
    print(response.json())

Example: Bulk ZIP Upload

curl -X POST \
  https://production.api.brandmachine.shop/api/product-studios/zip-upload \
  -H "Authorization: Bearer bm_your_token_here" \
  -F "file=@/path/to/import.zip"

The ZIP must follow the ZIP Upload folder structure.

Example: Upload a Designer Inspiration Resource

curl -X POST \
  https://production.api.brandmachine.shop/api/designer-instances/resources \
  -H "Authorization: Bearer bm_your_token_here" \
  -F "designerInstanceId=DESIGN_UUID" \
  -F "type=image" \
  -F "label=bird chest" \
  -F "file=@/path/to/inspiration.jpg"

Valid type values: image, material, color. For material and color, the file is optional; you can include a text-only description field.

Common Form Fields

Field names vary by endpoint, but the common ones are:

FieldNotes
fileBinary image content. Required for upload endpoints.
viewTypeProduct Studio input view type: front, back, other, cropped.
labelHuman-readable label for designer resources, pattern references.
typeDesigner resource type: image, material, color.
descriptionOptional text on designer/pattern resources.
rightsConsentRequired boolean on /api/fashion-models/upload-existing.

Refer to introspection or the dashboard for exact field requirements per endpoint.

File Specifications

Supported Image Types

  • JPEG (.jpg, .jpeg)
  • PNG (.png)
  • WebP (.webp)

Size and Limits

  • Single image upload: typically a few MB; larger images are resized server-side.
  • ZIP upload: max 20 MB total archive, max 6 images per product. See ZIP Upload for full rules.
  • Server-side timeout for uploads is 120 seconds.

Response Format

Successful uploads return JSON describing the created resource. The exact shape depends on the endpoint, but it typically includes an id and any URLs needed downstream:

{
  "success": true,
  "id": "abc123-...",
  "imageKey": "uploads/studio/STUDIO_UUID/abc123.jpg"
}

Errors return a non-2xx status with an error code:

{
  "success": false,
  "error": {
    "code": "INVALID_FILE_TYPE",
    "message": "Only JPEG, PNG, and WebP are supported."
  }
}

Error Handling

CodeDescriptionSolution
FILE_TOO_LARGEExceeds the endpoint's size limitReduce or split the upload
INVALID_FILE_TYPEUnsupported formatConvert to a supported format
UNAUTHENTICATEDMissing or invalid tokenCheck the Authorization header
FORBIDDENToken lacks scopeUse a token with the right scope
INSUFFICIENT_CREDITSUpload triggers a billable downstream taskTop up via Billing & Credits

Retry Failed Uploads

async function uploadWithRetry(url, formData, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: { Authorization: 'Bearer bm_your_token_here' },
        body: formData,
      });
      if (response.ok) return response.json();
      if (response.status === 401 || response.status === 403) throw new Error('Auth error');
    } catch (error) {
      if (attempt === maxRetries) throw error;
      await new Promise((r) => setTimeout(r, 1000 * attempt));
    }
  }
}

Best Practices

  • Validate client-side (size, type) to avoid round-trips on bad files.
  • Resize large images before uploading. Anything past a couple thousand pixels per side is usually overkill for catalog inputs.
  • Use the right endpoint. A campaign reference image goes through the campaign endpoint; a pattern reference goes through the pattern endpoint; mixing them up will return errors.
  • Handle webhooks downstream. Many uploads trigger asynchronous AI tasks. Watch your task list via activeMediaGenerationTasks to know when results are ready.

Need Help?

Contact support@brandmachine.shop for upload-related issues, with the endpoint and any error code from the response.