REST API

Direct uploads

Upload images and other files directly to S3 via Active Storage's direct upload protocol, then attach the returned signed_id to other resources.

When an endpoint accepts an image (venue logo, list image, ticket photo, template artwork), you have two options:

  1. Multipart upload — send the file as part of the resource create/update request body. Simple but uploads twice the bytes (client → API → S3).
  2. Direct upload — upload the file straight to S3 first, then reference it by signed_id in the resource create/update request. Faster, especially for large files.

This page covers option 2.

Flow

  1. POST /direct_uploads with the file’s metadata (size, checksum, content type, filename). The API returns a pre-signed S3 URL plus a signed_id.
  2. Upload the file bytes via PUT to the pre-signed URL. The bytes go straight to S3; the API never sees them.
  3. Reference the file in subsequent resource create/update requests by passing the signed_id as 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.