Suzanne
Browse docs

POST /v1/uploads

Get a 5-minute presigned PUT URL so the client uploads a JPEG/PNG directly to S3.

Get a 5-minute presigned PUT URL so the client uploads a JPEG/PNG directly to S3 (bypasses the API Gateway 10 MB body limit).

Request

Empty body, just the auth header.

curl -sS -X POST "$BASE/v1/uploads" \
  -H "Authorization: Bearer $KEY"

Response: 201 Created

{
  "upload_id":   "upl_<uuid>",
  "upload_url":  "https://...s3.amazonaws.com/...?AWSAccessKeyId=...",
  "expires_at":  "2026-06-01T07:14:38.006300Z"
}

Then PUT the image to upload_url

curl -X PUT "$UPLOAD_URL" --data-binary @photo.jpg -H "Content-Type:"
#                                                  ^^^^^^^^^^^^^^^^^^^
#                                          Suppress curl's default Content-Type

Important: do NOT send a Content-Type header on the PUT. The presigned URL is signed without it; if your client adds one, S3 returns 403 SignatureDoesNotMatch. curl adds one by default; pass -H "Content-Type:" (with an empty value) to suppress it. Most HTTP libraries let you omit headers explicitly:

  • fetch: omit the headers entry entirely.
  • requests.put: pass data= not files=, and headers={"Content-Type": None} if your session sets a default.
  • axios: pass headers: { 'Content-Type': undefined }.

The object lands at s3://<uploads-bucket>/<customer_id>/<upload_id> and auto-expires after 7 days. Reference it on a job via images_upload_ids: { "front": "upl_..." } (see POST /v1/generations/photo-to-3d).

Errors

HTTPcodeMeaning
401unauthorizedMissing / bad API key.
500internalCould not generate a presigned URL; retry.

The 403 SignatureDoesNotMatch error from S3 is not a Suzanne API error. It's S3 rejecting the PUT because your client added a Content-Type header. See the warning above.