Skip to content

Booth API

All under /booth, all bearer-keyed. List, pick, upload, complete. Photo bytes go straight to R2 on a signed URL and never pass through this API.

The booth never creates a session and never names a guest: the kiosk does that, and the booth picks one already waiting on its set.

  1. Events and sets.

    Terminal window
    curl -sS https://api.theloungebooth.com/booth/events \
    -H "Authorization: Bearer $BOOTH_API_KEY"
    {
    "events": [
    {
    "id": "7Kq2mZ0aBx91Tc",
    "slug": "city-launch",
    "name": "City Launch",
    "startAt": 1758499200000,
    "endAt": null,
    "sets": [
    { "id": "Rd8vN3pLw0Ymqe", "slug": "set-1", "name": "Set 1" },
    { "id": "Jf4bQ7tHs2Xzvi", "slug": "set-2", "name": "Set 2" }
    ]
    }
    ]
    }

    Started, or starting within a day; not ended more than a day ago. Open-ended events stay listed. Ordered by start, then set name.

  2. The queue.

    Terminal window
    curl -sS https://api.theloungebooth.com/booth/sets/Rd8vN3pLw0Ymqe/sessions \
    -H "Authorization: Bearer $BOOTH_API_KEY"
    {
    "sessions": [
    {
    "id": "Mv6xC1kRp8Wdzn",
    "label": "Alex and Sam",
    "createdAt": 1758501330000,
    "guests": [
    { "firstName": "Alex", "lastName": "Reyes" },
    { "firstName": "Sam", "lastName": "Okafor" }
    ]
    }
    ]
    }

    Active only, oldest first. label is the gallery heading, already built from the first names. An unknown set id returns an empty list, not a 404.

  3. One signed PUT per photo.

    Terminal window
    curl -sS -X POST \
    https://api.theloungebooth.com/booth/sessions/Mv6xC1kRp8Wdzn/uploads \
    -H "Authorization: Bearer $BOOTH_API_KEY"
    {
    "key": "sessions/Mv6xC1kRp8Wdzn/Zp3nT8wQ1eLk52.jpg",
    "url": "https://<account>.r2.cloudflarestorage.com/tlb-photos/sessions/...&X-Amz-Expires=600",
    "contentType": "image/jpeg",
    "expiresIn": 600
    }

    No body. Each call mints its own key; reusing one overwrites the object. Ten minutes, so ask when you are ready to send the bytes.

  4. PUT the bytes.

    Terminal window
    curl -sS -X PUT "$url" \
    -H "Content-Type: image/jpeg" \
    --data-binary @frame-01.jpg

    Content-Type is part of the signature: exactly image/jpeg, no charset, or R2 answers 403 and it reads like a broken signature. No bearer key here, different host.

  5. Complete.

    Terminal window
    curl -sS -X POST \
    https://api.theloungebooth.com/booth/sessions/Mv6xC1kRp8Wdzn/complete \
    -H "Authorization: Bearer $BOOTH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "photos": [
    { "key": "sessions/Mv6xC1kRp8Wdzn/Zp3nT8wQ1eLk52.jpg", "width": 2048, "height": 2560 },
    { "key": "sessions/Mv6xC1kRp8Wdzn/Hb9dK4sVu7Rm03.jpg", "width": 2048, "height": 2560 }
    ]
    }'
    {
    "id": "Mv6xC1kRp8Wdzn",
    "status": "completed",
    "completedAt": 1758501902000,
    "photos": 2
    }

    Gallery order is array order. Every key is HEADed and checked against the session’s prefix before anything is written. One call per session, at the end: completing publishes the gallery and sends each guest their link on every channel they gave. bytes comes from R2; width and height are yours, and the gallery lays out the grid from them.

Terminal window
curl -sS -X POST \
https://api.theloungebooth.com/booth/sessions/Mv6xC1kRp8Wdzn/cancel \
-H "Authorization: Bearer $BOOTH_API_KEY"
{ "id": "Mv6xC1kRp8Wdzn", "status": "cancelled" }

Active sessions only, and the only undo: no retake, no reopening a completed session. The group re-registers at the kiosk, or staff add photos from admin.

200 { events: [{ id, slug, name, startAt, endAt, sets: [{ id, slug, name }] }] }. endAt is null when open-ended. An event with no sets does not appear.

200 { sessions: [{ id, label, createdAt, guests: [{ firstName, lastName }] }] }. lastName can be empty: the kiosk takes one name field and splits on the first space.

200 { key, url, contentType, expiresIn } 404 { "error": "no such session" } 409 { "error": "session is completed" } or "session is cancelled"

{ photos: [{ key, width: int > 0, height: int > 0 }] }, 1–200 entries.

200 { id, status: "completed", completedAt, photos } 400 validation, see error shapes 404 { "error": "no such session" } 409 { "error": "session is completed" }, "session is cancelled", or { "error": "session completed concurrently" } when two completes race the status guard 422 { "error": "objects not found", "missing": ["sessions/…/….jpg"] } — key never uploaded, URL expired, or key from another session. Nothing was written; re-upload and call again.

Photos append after any staff already added.

200 { id, status: "cancelled" } 404 { "error": "no active session with that id" }, including already completed or cancelled.

/booth/* is not rate limited; the key is the control. Nothing is idempotent: two uploads calls give two objects, a second complete gives a 409. A failed PUT needs a fresh key and url, not a retry of the expired one.