When an endpoint accepts an image (venue logo, list image, ticket photo, template artwork), you have two options:
- Multipart upload — send the file as part of the resource create/update request body. Simple but uploads twice the bytes (client → API → S3).
- Direct upload — upload the file straight to S3 first, then reference it by
signed_idin the resource create/update request. Faster, especially for large files.
This page covers option 2.
Flow
POST /direct_uploadswith the file’s metadata (size, checksum, content type, filename). The API returns a pre-signed S3 URL plus asigned_id.- Upload the file bytes via
PUTto the pre-signed URL. The bytes go straight to S3; the API never sees them. - Reference the file in subsequent resource create/update requests by passing the
signed_idas the field value (e.g.,list[image]: <signed_id>).
The signed_id is durable — it doesn’t expire — and can be reused across multiple resources.
Create direct upload
POST /direct_uploads
Request
curl -X POST \
https://app.guestmanager.com/api/public/v2/direct_uploads \
-H 'Authorization: Token abcdefg' \
-H 'Content-Type: application/json' \
-d '{
"blob": {
"filename": "logo.jpg",
"byte_size": 771732,
"checksum": "mvwIbLIPIw2aVjcv0JgBhw==",
"content_type": "image/jpeg"
}
}'
Response
{
"id": 21221,
"key": "bUTAtPCMv9vdYpxmNA9umZ55",
"filename": "logo.jpg",
"content_type": "image/jpeg",
"byte_size": 771732,
"checksum": "mvwIbLIPIw2aVjcv0JgBhw==",
"created_at": "2026-05-07T17:42:10.952089Z",
"metadata": {},
"signed_id": "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBdVZTIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--ba378911553433b3b60d7d6c34e33ca20c4ef2c7",
"direct_upload": {
"url": "https://guestmanager-uploads.s3.amazonaws.com/bUTAtPCMv9vdYpxmNA9umZ55?X-Amz-Algorithm=...",
"headers": {
"Content-Type": "image/jpeg",
"Content-MD5": "mvwIbLIPIw2aVjcv0JgBhw==",
"Content-Disposition": "inline; filename=\"logo.jpg\"; filename*=UTF-8''logo.jpg"
}
}
}
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
blob[filename] |
string |
yes | Original filename. Used for Content-Disposition headers when the file is later served. |
blob[byte_size] |
integer |
yes | Exact size of the file in bytes. Must match what you upload. |
blob[checksum] |
string |
yes | Base64-encoded MD5 hash of the file. Used to verify integrity on upload. |
blob[content_type] |
string |
yes | MIME type (e.g., image/jpeg). |
Response fields
| Field | Type | Description |
|---|---|---|
id |
integer |
Internal blob ID. |
signed_id |
string |
The opaque token to pass back as a field value when attaching this upload to a resource. This is the value you keep. |
direct_upload.url |
string |
Pre-signed S3 URL. Valid for ~5 minutes — upload promptly. |
direct_upload.headers |
object |
Headers to include on the S3 PUT. Pass through as-is. |
| Other fields | — | Mirror of the request metadata, plus key (S3 path), metadata, and timestamps. |
Upload to S3
PUT <direct_upload.url>
curl -X PUT \
'https://guestmanager-uploads.s3.amazonaws.com/bUTAtPCMv9vdYpxmNA9umZ55?X-Amz-Algorithm=...' \
-H 'Content-Type: image/jpeg' \
-H 'Content-MD5: mvwIbLIPIw2aVjcv0JgBhw==' \
-H 'Content-Disposition: inline; filename="logo.jpg"; filename*=UTF-8'\'\''logo.jpg' \
--upload-file logo.jpg
S3 returns 200 OK with an empty body on success. If checksums or sizes don’t match what you declared, S3 returns 400 Bad Request and the upload is rejected.
Attach to a resource
Pass the signed_id as the field value:
curl -X POST \
https://app.guestmanager.com/api/public/v2/events/71339/lists \
-H 'Authorization: Token abcdefg' \
-H 'Content-Type: application/json' \
-d '{
"list": {
"name": "VIP Hospitality",
"image": "eyJfcmFpbHMiOnsibWVzc2FnZSI6...4ef2c7"
}
}'
The signed_id works anywhere a file/image field is accepted as JSON: lists, ticket photos, venue images, template artwork.