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-Typeheader on the PUT. The presigned URL is signed without it; if your client adds one, S3 returns403 SignatureDoesNotMatch.curladds one by default; pass-H "Content-Type:"(with an empty value) to suppress it. Most HTTP libraries let you omit headers explicitly:
fetch: omit theheadersentry entirely.requests.put: passdata=notfiles=, andheaders={"Content-Type": None}if your session sets a default.axios: passheaders: { '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
| HTTP | code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing / bad API key. |
| 500 | internal | Could 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.