Booking

Calendar scheduling, team bookings & RSVP

Aexy's booking module provides calendar scheduling capabilities for teams. Users can create event types, connect calendars, and allow external users to book meetings.

Features#

  • Event Types: Create meeting types with customizable duration, buffer time, and availability
  • Team Events: Book meetings with entire teams or rotating hosts
  • Calendar Integration: Sync with Google Calendar and Microsoft Outlook
  • RSVP System: Team members can accept/decline meeting invitations
  • Public Booking Pages: Share booking links for external scheduling

Calendar Integration#

Supported Providers#

ProviderFeatures
Google CalendarRead/write events, conflict detection, automatic event creation
Microsoft OutlookRead/write events, conflict detection, automatic event creation

How It Works#

  1. User initiates calendar connection from Booking Settings
  2. OAuth flow redirects to provider for authorization
  3. Backend exchanges auth code for tokens
  4. Calendar events are synced for availability checking
  5. New bookings automatically create calendar events

Google Calendar Setup#

1. Create Google Cloud Project#

  1. Go to https://console.cloud.google.com/
  2. Click "Select a project" → "New Project"
  3. Name it "Aexy" and click "Create"
  4. Select the newly created project

2. Enable Google Calendar API#

  1. Go to APIs & ServicesLibrary
  2. Search for "Google Calendar API"
  3. Click "Enable"
  1. Go to APIs & ServicesOAuth consent screen

  2. Select External (or Internal for Workspace)

  3. Fill in app information:

    • App name: "Aexy"
    • User support email: your email
    • Developer contact: your email
  4. Click "Save and Continue"

  5. Add scopes:

    https://www.googleapis.com/auth/calendar.readonly
    https://www.googleapis.com/auth/calendar.events
    
  6. Add test users (required during development)

  7. Click "Save and Continue"

4. Create OAuth Credentials#

  1. Go to APIs & ServicesCredentials
  2. Click "Create Credentials" → "OAuth client ID"
  3. Application type: Web application
  4. Name: "Aexy Booking"
  5. Add Authorized redirect URIs (backend callback URL):
    http://localhost:8000/api/v1/booking/calendars/callback/google
    
    For production:
    https://your-api-domain.com/api/v1/booking/calendars/callback/google
    
  6. Click "Create"
  7. Copy Client ID and Client Secret

5. Add to Backend Environment#

# Add to backend/.env
GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your_client_secret

6. Restart Services#

docker compose restart backend temporal-worker

Microsoft Calendar Setup#

The booking module shares its Azure App Registration with Aexy sign-in and the CRM Outlook integration. For the full setup — including the second redirect URI, MICROSOFT_TENANT_ID, and CRM scopes — see Microsoft integration. The steps below cover the booking-only minimum.

1. Register Azure AD Application#

  1. Go to https://portal.azure.com/
  2. Navigate to Azure Active DirectoryApp registrations
  3. Click "New registration"
  4. Fill in:
    • Name: "Aexy Booking"
    • Supported account types: "Accounts in any organizational directory and personal Microsoft accounts"
    • Redirect URI: Web - http://localhost:8000/api/v1/booking/calendars/callback/microsoft
    • For production: https://your-api-domain.com/api/v1/booking/calendars/callback/microsoft
  5. Click "Register"

2. Configure API Permissions#

  1. Go to API permissions → "Add a permission"
  2. Select "Microsoft Graph"
  3. Choose "Delegated permissions"
  4. Add:
    • Calendars.ReadWrite
    • User.Read
    • offline_access
  5. Click "Add permissions"

3. Create Client Secret#

  1. Go to Certificates & secrets
  2. Click "New client secret"
  3. Add description and expiration
  4. Copy the secret value immediately (it won't be shown again)

4. Get Application ID#

From the Overview page, copy:

  • Application (client) ID

5. Add to Backend Environment#

# Add to backend/.env
MICROSOFT_CLIENT_ID=your_application_id
MICROSOFT_CLIENT_SECRET=your_client_secret

6. Restart Services#

docker compose restart backend temporal-worker

Connecting Calendars (User Flow)#

From Booking Settings#

  1. Navigate to BookingCalendars in the app
  2. Click "Connect Calendar"
  3. Choose provider (Google or Microsoft)
  4. Authorize in the provider's OAuth popup
  5. Return to Aexy with calendar connected

Calendar Settings#

Once connected, configure:

  • Primary Calendar: Which calendar to use by default
  • Sync Enabled: Toggle calendar sync on/off
  • Check Conflicts: Use this calendar for availability checking
  • Create Events: Automatically create events for new bookings

Team Booking#

Assignment Types#

TypeBehavior
Round RobinRotates between team members for each booking
CollectiveBooks whichever team member is available
All HandsBooks entire team - all members attend the meeting

All Hands Meetings#

When ALL_HANDS assignment type is used:

  1. All team members are added as attendees
  2. Each attendee receives an RSVP invitation
  3. Attendees can accept or decline
  4. Meeting status depends on attendee responses

Custom Team Selection#

Booking links support multiple team selection modes:

  1. Event Type Members: Members assigned to the event type

    /book/{workspace}/{event-slug}
    
  2. Workspace Team: All members of a specific team

    /book/{workspace}/{event-slug}/team/{team-slug}
    
  3. Custom Members: Specific user IDs via query params

    /book/{workspace}/{event-slug}/team/{team-slug}?members=id1,id2,id3
    

RSVP System#

How It Works#

  1. When a booking is created with multiple attendees (ALL_HANDS mode):

    • Each attendee gets a unique response_token
    • Notification sent with RSVP link
  2. Attendee clicks RSVP link:

    • Views booking details
    • Can accept or decline
  3. Status updates:

    • pending - No response yet
    • confirmed - Attendee accepted
    • declined - Attendee declined

RSVP Page#

Public RSVP page at /rsvp/{token} shows:

  • Event details (name, date, time, duration)
  • Organizer information
  • Invitee details
  • Accept/Decline buttons

Team Calendar View#

The Team Calendar (/booking/team-calendar) provides a visual overview of team availability:

Features#

  • Week View: Navigate between weeks
  • Member Availability: See each team member's available/busy times
  • Overlapping Slots: Highlighted times when all members are free
  • Existing Bookings: View scheduled meetings
  • Quick Book: Click slots to initiate booking

Filtering Options#

  • Select specific team event type
  • Filter by workspace team
  • View all assigned members

Public Booking URLs#

URL Patterns#

PatternDescription
/book/{workspace}Workspace landing - lists all event types
/book/{workspace}/{event}Book specific event type
/book/{workspace}/{event}/team/{team}Book with specific team

Examples#

# Workspace landing page
https://app.aexy.io/book/acme-corp

# Standard booking
https://app.aexy.io/book/acme-corp/30-min-meeting

# Team-specific booking
https://app.aexy.io/book/acme-corp/team-consultation/team/engineering

# Custom team members
https://app.aexy.io/book/acme-corp/team-consultation/team/engineering?members=user1,user2

API Endpoints#

Calendar Connections#

GET  /workspaces/{id}/booking/calendars                    # List connected calendars
GET  /workspaces/{id}/booking/calendars/connect/{provider} # Get OAuth URL
POST /workspaces/{id}/booking/calendars/connect/google     # Exchange Google auth code
POST /workspaces/{id}/booking/calendars/connect/microsoft  # Exchange Microsoft auth code
GET  /workspaces/{id}/booking/calendars/{calendar_id}      # Get calendar details
PATCH /workspaces/{id}/booking/calendars/{calendar_id}     # Update calendar settings
DELETE /workspaces/{id}/booking/calendars/{calendar_id}    # Disconnect calendar
POST /workspaces/{id}/booking/calendars/{calendar_id}/sync # Force sync

Event Types#

GET  /workspaces/{id}/booking/event-types                  # List event types
POST /workspaces/{id}/booking/event-types                  # Create event type
GET  /workspaces/{id}/booking/event-types/{event_id}       # Get event type
PATCH /workspaces/{id}/booking/event-types/{event_id}      # Update event type
DELETE /workspaces/{id}/booking/event-types/{event_id}     # Delete event type

Bookings#

GET  /workspaces/{id}/booking/bookings                     # List bookings
GET  /workspaces/{id}/booking/bookings/{booking_id}        # Get booking details
PATCH /workspaces/{id}/booking/bookings/{booking_id}       # Update booking
DELETE /workspaces/{id}/booking/bookings/{booking_id}      # Cancel booking

Team Availability#

GET /workspaces/{id}/booking/availability/team/{event_type_id}  # Team availability
GET /workspaces/{id}/booking/availability/team-calendar         # Team calendar data

RSVP#

GET  /booking/rsvp/{token}          # Get booking details for RSVP
POST /booking/rsvp/{token}/respond  # Submit RSVP response (accept/decline)

Public Booking#

GET  /public/book/{workspace}                              # List public event types
GET  /public/book/{workspace}/{event}/slots                # Get available slots
POST /public/book/{workspace}/{event}/book                 # Create booking
GET  /public/book/{workspace}/team/{team_id}               # Get team info
GET  /public/book/{workspace}/teams                        # List workspace teams

Database Tables#

booking_event_types#

Stores event type configurations.

ColumnTypeDescription
idUUIDPrimary key
workspace_idUUIDFK to workspaces
owner_idUUIDFK to developers
nameVARCHAREvent type name
slugVARCHARURL-friendly identifier
descriptionTEXTEvent description
duration_minutesINTEGERMeeting duration
buffer_beforeINTEGERBuffer time before meeting
buffer_afterINTEGERBuffer time after meeting
is_team_eventBOOLEANTeam event flag
team_idUUIDOptional FK to teams
is_activeBOOLEANPublished status

calendar_connections#

Stores OAuth tokens for calendar providers.

ColumnTypeDescription
idUUIDPrimary key
user_idUUIDFK to developers
workspace_idUUIDFK to workspaces
providerVARCHARgoogle/microsoft
calendar_idVARCHARProvider's calendar ID
calendar_nameVARCHARDisplay name
account_emailVARCHARConnected account email
access_tokenTEXTEncrypted access token
refresh_tokenTEXTEncrypted refresh token
token_expires_atTIMESTAMPToken expiration
is_primaryBOOLEANPrimary calendar flag
sync_enabledBOOLEANSync toggle
check_conflictsBOOLEANUse for availability
create_eventsBOOLEANAuto-create events

bookings#

Stores booking records.

ColumnTypeDescription
idUUIDPrimary key
workspace_idUUIDFK to workspaces
event_type_idUUIDFK to event types
host_idUUIDPrimary host (FK to developers)
invitee_nameVARCHARBooker's name
invitee_emailVARCHARBooker's email
start_timeTIMESTAMPMeeting start
end_timeTIMESTAMPMeeting end
timezoneVARCHARBooking timezone
statusVARCHARconfirmed/cancelled/pending
notesTEXTAdditional notes
meeting_urlVARCHARVideo conference link

booking_attendees#

Stores team meeting attendees with RSVP status.

ColumnTypeDescription
idUUIDPrimary key
booking_idUUIDFK to bookings
user_idUUIDFK to developers
statusVARCHARpending/confirmed/declined
response_tokenVARCHARUnique RSVP token
responded_atTIMESTAMPResponse timestamp

Troubleshooting#

"Method Not Allowed" on Calendar Connect#

Ensure you're using the correct HTTP method:

  • GET /calendars/connect/{provider} - Returns OAuth URL
  • POST /calendars/connect/{provider} - Exchanges auth code

"redirect_uri_mismatch" Error#

The redirect URI in Google/Microsoft console must exactly match the backend callback URL:

For Google:

http://localhost:8000/api/v1/booking/calendars/callback/google
https://your-api-domain.com/api/v1/booking/calendars/callback/google

For Microsoft:

http://localhost:8000/api/v1/booking/calendars/callback/microsoft
https://your-api-domain.com/api/v1/booking/calendars/callback/microsoft

Calendar Not Syncing#

  1. Check calendar connection status in settings
  2. Verify token hasn't expired
  3. Check the Temporal worker is running:
    docker compose logs temporal-worker
    

Team Availability Not Showing#

  1. Ensure team members have connected calendars
  2. Verify "Check Conflicts" is enabled for their calendars
  3. Check team members are assigned to the event type

RSVP Token Invalid#

Tokens are single-use and expire after response. Users who need to change their response should contact the organizer.


Frontend Pages#

PathDescription
/bookingBooking dashboard
/booking/event-typesManage event types
/booking/bookingsView all bookings
/booking/calendarsCalendar connections
/booking/calendars/callbackOAuth callback handler
/booking/team-calendarTeam availability view
/book/{workspace}Public workspace landing
/book/{workspace}/{event}Public booking page
/rsvp/{token}Public RSVP page

File Structure#

frontend/src/
├── app/
│   ├── (app)/booking/
│   │   ├── page.tsx                    # Booking dashboard
│   │   ├── event-types/page.tsx        # Event types list
│   │   ├── bookings/page.tsx           # Bookings list
│   │   ├── calendars/
│   │   │   ├── page.tsx                # Calendar settings
│   │   │   └── callback/page.tsx       # OAuth callback
│   │   └── team-calendar/page.tsx      # Team calendar view
│   └── public/
│       ├── book/
│       │   ├── [workspaceSlug]/
│       │   │   ├── page.tsx            # Workspace landing
│       │   │   └── [eventSlug]/
│       │   │       ├── page.tsx        # Public booking
│       │   │       └── team/
│       │   │           └── [teamId]/page.tsx  # Team booking
│       └── rsvp/
│           └── [token]/page.tsx        # RSVP response page
├── components/booking/
│   ├── EventTypeCard.tsx               # Event type display
│   ├── BookingCard.tsx                 # Booking display
│   └── TeamCalendarView.tsx            # Team calendar component
└── lib/
    └── booking-api.ts                  # Booking API client

backend/src/aexy/
├── models/booking/
│   ├── event_type.py                   # EventType model
│   ├── booking.py                      # Booking model
│   ├── booking_attendee.py             # BookingAttendee model
│   ├── calendar_connection.py          # CalendarConnection model
│   └── team_event_member.py            # TeamEventMember model
├── services/booking/
│   ├── booking_service.py              # Booking logic
│   ├── availability_service.py         # Availability calculation
│   ├── calendar_sync_service.py        # Calendar sync
│   └── notification_service.py         # Email notifications
├── api/booking/
│   ├── event_types.py                  # Event type endpoints
│   ├── bookings.py                     # Booking endpoints
│   ├── calendars.py                    # Calendar endpoints
│   ├── availability.py                 # Availability endpoints
│   ├── rsvp.py                         # RSVP endpoints
│   └── public.py                       # Public booking endpoints
└── schemas/booking/
    ├── event_type.py                   # Event type schemas
    ├── booking.py                      # Booking schemas
    ├── calendar.py                     # Calendar schemas
    └── team_availability.py            # Team availability schemas