Purpose: Enable venue managers to discover and request artwork for display at their venues, with artist approval and delivery confirmation.
Actors:
- Venue Manager - User with venue management rights via
venueAdminstable - Artist - User with
isArtistcapability and artwork indraftstatus - System - Automated timeout processing and notification delivery
The venue booking flow is a multi-step workflow that allows venues to proactively request artwork from artists, coordinate delivery, and confirm placement. This creates a two-sided marketplace where both artists and venues can initiate placement.
[Venue Manager] [System] [Artist]
| | |
|--Browse Artworks--------------> |
| | |
|--Select & Book Artwork---------->|
| | |
| [Create Booking] |
| [Status: booked] |
| [24hr timeout] |
| | |
| [Send Notification]-->|
| | |
| | [Reviews Request]
| | |
| |<------Accept-----|
| | |
| [Status: accepted] |
| [Generate PDF QR] |
| [7 day timeout] |
| | |
|<-----[Notify: Accepted]----------|
| | |
| | [Delivers Artwork]
| | |
| |<--Mark Delivered-|
| | |
| [Status: pending_ |
| verification] |
| [7 day timeout] |
| | |
|<-----[Notify: Scan QR]-----------|
| | |
|--Scan QR Code------------------->|
| | |
|<--Redirect to Verify-------------|
| | |
|--Confirm Delivery--------------->|
| | |
| [Status: placed] |
| [Booking: completed] |
| | |
|<-----[Artwork Now Purchasable]---|
1.1 Venue Manager Browses Artworks
- URL:
GET /venue-owners/browse - Action: Venue manager views all available (
draftstatus) artworks - Display: Artworks grouped by artist, showing first 4 per artist
- Options:
- Click artist name to see full catalog
- Select individual artworks via checkboxes
- Choose venue from dropdown (if managing multiple)
- Click "Book Selected Artworks"
1.2 View Artist's Full Catalog
- URL:
GET /venue-owners/browse/artist/:artistId - Action: View all artworks by specific artist (any status)
- Display:
- All artworks shown (bookable + unavailable)
- Status badges indicate availability
- Only
draftstatus artworks are selectable
1.3 Submit Booking Request
- URL:
POST /venue-owners/book - Payload:
{ "venueId": "uuid", "artworkIds": ["uuid1", "uuid2", ...] } - Validation:
- User manages selected venue
- All artworks are in
draftstatus - No active bookings exist for these artworks
- Result:
- One booking record per artwork
- Product status →
booked - Expiry set to 24 hours from now
- Notification sent to each artist
2.1 Artist Receives Notification
- URL:
GET /artists/notifications - Display: Unread notification badge
- Content: "New Booking Request: [Venue Name] wants to display '[Artwork Title]'"
- Action: Click to view booking detail
2.2 Artist Reviews Booking
- URL:
GET /artists/bookings/:bookingId - Display:
- Artwork details
- Venue information (name, address, contact)
- Venue manager contact details
- Status and expiry countdown
- Actions: Accept or Reject
2.3a Artist Accepts
- URL:
POST /artists/bookings/:id/accept - Result:
- Booking status →
accepted - Product status →
accepted - Expiry extended to 7 days
- PDF label generation triggered (async)
- Venue manager notified
- Booking status →
- PDF Contents:
- QR code pointing to
/a/:shortCode - Artwork title and artist name
- Price
- Artwalls branding
- QR code pointing to
2.3b Artist Rejects
- URL:
POST /artists/bookings/:id/reject - Payload: Optional rejection reason
- Result:
- Booking status →
rejected - Product status →
draft - Venue manager notified with reason
- Booking status →
2.4 Timeout: No Response
- Trigger: Cron job runs hourly
- Condition:
expires_at < NOW()andstatus = 'pending' - Result:
- Booking status →
expired - Product status →
draft - Artwork becomes available again
- Booking status →
3.1 Artist Delivers Artwork
- Action: Artist physically brings artwork to venue (out-of-band)
- URL:
POST /artists/bookings/:id/delivered - Result:
- Booking
deliveredAttimestamp set - Product status →
pending_verification - Expiry extended to 7 days
- Venue manager notified to scan QR
- Booking
3.2 Timeout: Delivery Window Expired
- Trigger: Cron job
- Condition:
expires_at < NOW()andstatus = 'accepted' - Result:
- Booking status →
expired - Product status →
draft - Booking history preserved
- Booking status →
4.1 Venue Manager Scans QR
-
URL:
GET /a/:shortCode(artwork QR code) -
Logic: System detects user is venue manager with pending booking
-
Detection:
// Check if user manages any venues const userVenues = await getVenuesForUser(userId); // Check if pending booking exists at any of their venues const pendingBooking = await findPendingBooking(productId, userVenues); if (pendingBooking) { redirect to /venue-owners/verify/:shortCode } else { redirect to /shop/products/:id // Customer purchase flow }
-
Result: Redirect to verification endpoint
4.2 Venue Confirms Delivery
- URL:
POST /venue-owners/verify/:shortCode - Result:
- Booking status →
completed - Booking
verifiedAttimestamp set - Product status →
placed - Product
venueIdset - Product
placedAttimestamp set - Artist notified artwork is live
- Booking status →
4.3 Timeout: Verification Window Expired
- Trigger: Cron job
- Condition:
expires_at < NOW()and productstatus = 'pending_verification' - Result:
- Booking status →
expired - Product status →
draft - Manual reconciliation may be needed
- Booking status →
URL: GET /venue-owners/venues/:id/bookings
Filters:
- All - Shows everything
- Booked - Active bookings (
pending,acceptedstatus) - On the Wall - Products with
status = 'placed' - Sold - Products with
status = 'sold'
Actions:
- Cancel pending bookings
- View booking status and expiry
- Access verification QR scanner
Scenario: Two venues try to book same artwork simultaneously Handling:
- First request creates booking and sets status to
booked - Second request checks for active booking
- Returns error: "Artwork already has an active booking"
- Second venue must wait for timeout or rejection
Scenario: Artist ignores booking request Handling:
- 24-hour timeout automatically expires booking
- Product returns to
draft - Venue can attempt to book again
Scenario: Artist accepts but doesn't deliver within 7 days Handling:
- Automatic timeout after 7 days
- Product returns to
draft - Booking marked as
expired - Venue can contact artist or book different artwork
Scenario: Artwork is at venue but QR never scanned Handling:
- 7-day window for confirmation
- Timeout reverts to
draft(conservative) - Manual intervention may be needed to resolve
Scenario: External VPS is unavailable Handling:
- Booking still succeeds (non-blocking)
- PDF can be regenerated later
- Venue/artist can coordinate via contact info
BEGIN;
-- Check artwork is available
SELECT status FROM products WHERE id = $1 FOR UPDATE;
-- Must be 'draft'
-- Check no active booking exists
SELECT id FROM bookings
WHERE product_id = $1
AND status IN ('pending', 'accepted')
LIMIT 1;
-- Must return no rows
-- Create booking
INSERT INTO bookings (product_id, venue_id, requested_by, status, expires_at)
VALUES ($1, $2, $3, 'pending', NOW() + INTERVAL '24 hours')
RETURNING id;
-- Update product status
UPDATE products SET status = 'booked', updated_at = NOW()
WHERE id = $1;
-- Create notification
INSERT INTO notifications (user_id, type, title, body, reference_type, reference_id)
VALUES ($artist_id, 'booking_request', 'New Booking Request', $message, 'booking', $booking_id);
COMMIT;-- Find expired bookings
SELECT id, product_id, status
FROM bookings
WHERE status IN ('pending', 'accepted')
AND expires_at < NOW();
-- For each expired booking:
BEGIN;
UPDATE bookings SET status = 'expired'
WHERE id = $booking_id;
UPDATE products SET status = 'draft', updated_at = NOW()
WHERE id = $product_id;
COMMIT;- Booking Request - When venue books artwork
- Booking Cancelled - If venue cancels before acceptance
- Booking Expired - If no response within 24 hours
- Booking Accepted - Artist confirmed
- Booking Rejected - Artist declined (includes reason)
- Ready for Delivery - Artist marked as delivered
- Artwork Placed - Confirmation after QR scan
-
Authorization Checks:
- Verify user manages venue before allowing booking
- Verify artist owns artwork before allowing accept/reject
- Verify venue manager scans QR at their own venue
-
Race Condition Prevention:
- Database-level locking on product status checks
- Atomic booking creation with status transition
-
Timeout Enforcement:
- Hourly cron ensures stale bookings don't block artwork
- Lazy evaluation checks expiry on read operations
-
Contact Privacy:
- Venue contact info only shared after booking creation
- Artist contact info always visible to venues (marketplace transparency)
Administrators have special powers to manage bookings:
- URL:
POST /admin/bookings/:id/force-complete - Purpose: Bypass QR verification when delivery is confirmed out-of-band
- Result:
- Booking status →
completed - Product status →
placed - Artist notified
- Venue manager notified
- Booking status →
- URL:
POST /admin/bookings/:id/force-cancel - Payload:
{ reason: string } - Purpose: Resolve disputes or stuck bookings
- Result:
- Booking status →
cancelled - Product status →
draft - Both parties notified with reason
- Booking status →
- URL:
POST /admin/bookings/:id/extend - Payload:
{ additionalHours: number } - Purpose: Give more time when needed (holidays, logistics)
- Result:
- Expiry extended by specified hours
- Both parties notified
- URL:
GET /admin/analytics/bookings - Metrics:
- Conversion funnel (pending → accepted → completed)
- Acceptance rate, completion rate
- Average time to accept/deliver/verify
- Expiration breakdown (pending vs delivery)
✅ Batch Booking: Multiple artworks can be booked in single form submission ✅ Email Notifications: All booking state changes trigger email notifications ✅ Booking Analytics: Full funnel tracking and performance metrics ✅ Admin Controls: Force complete, cancel, and extend deadline capabilities ✅ Comprehensive Testing: Full integration test suite covering all flows
- Booking Preferences: Artist auto-accept rules
- Delivery Scheduling: Calendar integration for delivery coordination
- In-App Messaging: Chat between artist and venue manager
- Venue Ratings: Artists rate venues after completed bookings
- Smart Timeouts: Contextual deadlines based on distance/holidays