Skip to main content

SEER Booking Service — Requirements Specification

1. Service Overview

Build the SEER Booking Service (seer-booking) as a new SEER microservice that manages resource reservation (rooms/zones and devices). The service handles conflict detection, schedule queries, recurring bookings, and integrates with SEER's authentication, IAM, webhook, and rule engine systems.

Why a Separate Service

The existing task service does not cover:

  • Conflict / overlap detection for shared resources
  • Schedule status queries with time-range filtering
  • Recurring booking management
  • Available-slot computation
  • Booking-specific policies (max duration, min notice, cancellation rules)

A dedicated booking service addresses these gaps cleanly without overloading the task service.


2. Bookable Resources

Resource TypeSEER Source ServiceIdentifierNotes
Zone / RoomDevice Management (/device/v1/)zoneIdMeeting rooms, labs, shared spaces. Zone has name, hierarchy (parent zone).
DeviceDevice Management (/device/v1/devices)deviceIdShared sensors, equipment, cameras. Device has type, name, extName, workspace.

Rules

  • The booking service MUST NOT duplicate zone/device data. It stores a reference (resourceType + resourceId) and fetches display details from the SEER device service on demand or via cache.
  • When a SEER zone/device is deleted, the booking resource should be soft-deactivated.

3. Functional Requirements

3.1 Resource Registration

  • Admin (LV2/LV3) can register a SEER zone or device as a bookable resource.
  • Each resource has:
    • Operating hours (per weekday)
    • Booking policy (see 3.6)
    • Capacity (for rooms, optional)
    • Active / inactive toggle
  • A resource can only be registered once per workspace.

3.2 Booking Lifecycle

pending ──→ confirmed ──→ completed
│ │
│ └──→ cancelled

└──→ cancelled
StatusDescription
pendingCreated, awaiting confirmation (optional — configurable per resource whether auto-confirm)
confirmedSlot reserved
completedBooking time passed, checked out or auto-completed
cancelledCancelled by booker or admin

3.3 Conflict Detection

This is the critical requirement for the booking service.

On every booking create or reschedule:

  1. Check for overlapping bookings where:
    • Same resourceId
    • status IN (pending, confirmed)
    • (existingStart < newEnd) AND (existingEnd > newStart)
  2. If overlap found → return 409 Conflict with conflicting booking IDs.
  3. If no overlap → proceed with creation.

Database-level enforcement is required (see Section 7.3). Application-level checks are a first line of defense; a PostgreSQL exclusion constraint is the safety net.

3.4 Schedule Query

EndpointDescription
GET /resources/:id/schedule?from=&to=Returns all bookings in a date range
GET /resources/:id/available-slots?date=Returns free time slots for a given day, respecting operating hours and existing bookings
  • Time slots MUST be returned in the requesting user's timezone (passed via tz query parameter, IANA format, e.g. Asia/Hong_Kong).

3.5 Recurring Bookings

  • Support iCal RRULE format for recurrence (e.g., FREQ=WEEKLY;BYDAY=MO,WE,FR).
  • Storage strategy:
    • One parent record with the RRULE stored in recurrenceRule field.
    • Individual occurrence records linked by recurrenceParentId.
  • Operations on recurring bookings:
    • Edit single occurrence: creates an exception (detached from series).
    • Edit series: updates parent and all future occurrences.
    • Cancel series: cancels parent and all future occurrences.
  • Recurring booking creation MUST validate no conflicts across ALL occurrences before committing.

3.6 Booking Policies

Each bookable resource has a configurable policy:

PolicyTypeDescription
maxDurationMinutesnumberMaximum single booking length
maxAdvanceDaysnumberHow far in advance a booking can be made
minNoticeMinutesnumberMinimum notice before booking start
allowRecurringbooleanWhether recurring bookings are allowed
autoConfirmbooleanSkip pending state, go directly to confirmed
maxBookingsPerUsernumberMax active bookings per user per resource
cancellationPolicyobjectHow late a booking can be cancelled (e.g., 30 min before start)
gracePeriodMinutesnumberMinutes after start before no-show is triggered

3.7 No-Show Handling

  • If a confirmed booking is not checked in within gracePeriodMinutes after startTime, status transitions to no_show.
  • The system MUST emit a booking.no_show webhook event so SEER rules can trigger alerts or release the slot.
  • Grace period is configurable per resource.

3.8 Check-In / Check-Out

  • PATCH /bookings/:id/checkin — marks the booking as actively in use.
  • PATCH /bookings/:id/checkout — marks the booking as completed early.
  • If no checkout is performed, the booking auto-completes at endTime.

4. Integration Requirements

4.1 Authentication

  • All endpoints require JWT Bearer token in Authorization header.
  • Validate token using SEER's auth service public key or JWKS endpoint.
  • Token contains: sub (profileId), workspace, roles/permissions.

4.2 IAM / Authorization

Use the existing SEER role hierarchy:

ActionLV2 (WS Admin)LV3 (Group Admin)LV4 (User)LV5 (Guest)
Register / manage resources
Create booking
View own bookings
View all bookings (workspace)
Cancel any booking
View schedule (resource)
  • Group-scoped resources: LV3 can only manage resources in their group(s).

4.3 Webhook Events

The service MUST emit webhook events to SEER's webhook system (HTTP POST to configured endpoint). Event payload must follow SEER's webhook variable format.

EventTriggerPayload Variables
booking.createNew booking createdid, resourceId, resourceType, bookerEmail, bookerProfileId, startTime, endTime, status, title, workspace
booking.confirmBooking confirmedabove + confirmedBy, confirmedAt
booking.cancelBooking cancelledabove + cancelledBy, cancelledAt, reason
booking.reminderX minutes before startabove + reminderType
booking.no_showNo check-in detectedabove + gracePeriodExpiredAt
booking.checkinBooker checked inabove + checkedInAt
booking.checkoutBooker checked outabove + checkedOutAt

4.4 SEER Device Service Dependency

  • On resource registration: validate that resourceId exists by calling GET /device/v1/devices/{id} or the zone equivalent.
  • On booking display: enrich with resource name/type from device service (cache with TTL 5 min).
  • Handle device deletion gracefully (webhook listener or periodic reconciliation).

4.5 API Gateway Registration

  • Service must expose an OpenAPI spec at /booking/docs-yaml.
  • Routes prefixed with /booking/v1/.
  • Register in SEER's API Gateway routing configuration.

5. API Contract

5.1 Resource Management

POST   /booking/v1/resources
GET /booking/v1/resources ?workspace=&resourceType=&isActive=&page=&limit=
GET /booking/v1/resources/:id
PATCH /booking/v1/resources/:id
DELETE /booking/v1/resources/:id

GET /booking/v1/resources/:id/schedule ?from=ISO8601&to=ISO8601&tz=IANA
GET /booking/v1/resources/:id/available-slots ?date=YYYY-MM-DD&tz=IANA&slotMinutes=30

5.2 Booking Management

POST   /booking/v1/bookings
GET /booking/v1/bookings ?workspace=&resourceId=&status=&bookerProfileId=&from=&to=&page=&limit=
GET /booking/v1/bookings/:id
PATCH /booking/v1/bookings/:id (reschedule — subject to conflict check)
PATCH /booking/v1/bookings/:id/confirm
PATCH /booking/v1/bookings/:id/cancel { reason? }
PATCH /booking/v1/bookings/:id/checkin
PATCH /booking/v1/bookings/:id/checkout

POST /booking/v1/bookings/bulk (create recurring series)
PATCH /booking/v1/bookings/series/:parentId/cancel

5.3 Response Format

Follow SEER's existing response pattern:

// Single resource
{
"id": "uuid",
"resourceId": "seer-zone-or-device-id",
"resourceType": "zone",
"workspace": "workspace-uuid",
"resourceName": "Meeting Room A",
"capacity": 10,
"isActive": true,
"bookingPolicy": { ... },
"availability": { ... },
"createdAt": "2026-07-01T00:00:00.000Z",
"updatedAt": "2026-07-01T00:00:00.000Z"
}
// Paginated list
{
"data": [ ... ],
"count": 10,
"total": 150,
"page": 1,
"pageCount": 15
}

5.4 Error Responses

// 409 Conflict
{
"statusCode": 409,
"message": "Booking conflicts with existing reservations",
"conflicts": [
{
"bookingId": "uuid",
"startTime": "2026-07-01T09:00:00.000Z",
"endTime": "2026-07-01T10:00:00.000Z"
}
]
}
// 422 Policy Violation
{
"statusCode": 422,
"message": "Booking exceeds maximum duration of 120 minutes",
"policy": "maxDurationMinutes"
}
// 400 Bad Request
{
"statusCode": 400,
"message": "endTime must be after startTime"
}

5.5 Health Check

GET /booking/v1/health

Response:

{
"status": "ok",
"info": {
"database": { "status": "up" }
},
"error": null,
"details": {
"database": { "status": "up" }
}
}

6. Non-Functional Requirements

RequirementTarget
Response time (CRUD operations)< 200ms p95
Response time (schedule query)< 500ms p95 for 30-day range
Concurrent booking requestsHandle 100 concurrent writes without data corruption
Availability99.5% uptime
Data retentionBookings retained for 2 years, then archived
Timezone correctnessAll storage in UTC; queries accept IANA timezone param

7. Technical Constraints

7.1 Language / Framework

  • NestJS (Node.js) — match existing SEER microservices.
  • TypeORM as ORM.
  • Follow @nestjsx/crud patterns where applicable.

7.2 Database

  • PostgreSQL (required for tstzrange and exclusion constraints).
  • Must use migrations (not auto-sync).
  • Connection pooling required.

7.3 Conflict Prevention at DB Level

PostgreSQL exclusion constraint is mandatory:

-- Required: prevent overlapping bookings at the database level
ALTER TABLE bookings
ADD CONSTRAINT no_overlapping_bookings
EXCLUDE USING gist (
resource_id WITH =,
tstzrange(start_time, end_time) WITH &&
) WHERE (status IN ('pending', 'confirmed'));

This guarantees no overlapping bookings even under concurrent writes.

7.4 Docker Deployment

  • Provide Dockerfile and docker-compose.yml.
  • Environment variables for:
    • DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_DATABASE
    • SEER_AUTH_ENDPOINT (JWT validation)
    • SEER_DEVICE_SERVICE_URL (resource validation)
    • WEBHOOK_ENDPOINT (SEER webhook system)
    • WEBHOOK_SECRET (webhook signing key)
  • Health check endpoint at GET /booking/v1/health.

7.5 Caching

  • Cache zone/device metadata from SEER device service.
  • TTL: 5 minutes.
  • Use Redis or in-memory cache (follow SEER's caching strategy).

7.6 OpenAPI Specification

  • Auto-generate from NestJS decorators.
  • Expose at /booking/docs-yaml (YAML format).
  • Include request/response examples for all endpoints.

8. Development Deliverables

#ItemDetails
1Source codeRepo under the SEER monorepo, .gitignore, consistent branch strategy
2Database migrationsTypeORM migration files, forward + rollback support
3OpenAPI specificationAuto-generated YAML at /booking/docs-yaml
4Docker assetsDockerfile and docker-compose.yml, env-var documented
5Unit tests> 80% coverage on service layer
6Integration testsConflict detection, IAM validation, webhook emission
7API documentationGenerated from OpenAPI + README
8Deployment runbookEnv vars, DB setup, API Gateway route registration steps

9. Out of Scope

  • Frontend / UI development.
  • Modifications to other SEER services (API Gateway route registration is part of deployment setup, not a code change to the Gateway service itself).
  • Mobile push notification delivery (webhook emission only; delivery is handled by the existing webhook system).

10. Open Design Questions

Decisions to finalize before or during development:

  1. Recurring booking storage strategy — one row per occurrence vs. on-the-fly expansion. Recommendation: generate occurrences for the next 90 days, lazy-generate the rest.
  2. Timezone edge cases — DST transitions that cause ambiguous or non-existent times. Recommendation: store UTC, convert on display only.
  3. Calendar sync scope — Google/Outlook sync is deferred to a later phase. Should we add an abstraction layer now to minimize rework?
  4. Monitoring — Prometheus metrics endpoint or health check only?
  5. Webhook retry policy — retry count, backoff strategy, dead-letter handling.

Appendix A: SEER Platform Reference

Existing Services

ServicePath PrefixPurpose
Auth Service/auth/JWT authentication, token validation
API Gateway/api/Request routing, rate limiting
Device Management/device/Devices, zones, realtime data
Workspace IAM/iam/Roles, permissions, workspace/group membership
Rule Management/rule/Rule engine (time-triggered, data-triggered)
Task & Location/task/Task CRUD, state transitions
Control & Health/control/Device control, alarmbox
Connector Service/connector/External system integration
MinIO Storage/minio/File/object storage
App Store/appstore/Application marketplace

Role Hierarchy

RoleLevelDescription
System AdminLV1Platform internal use, not for customer
Workspace AdminLV2Full access within workspace
Group AdminLV3Full access within group
UserLV4Limited access
GuestLV5Read-only access

Webhook Event Types (Existing)

Event TypeDescription
infer.ruleNew infer data from rule service
infer.linkOnline/offline status change
alert.triggerAlert triggered
new_data.httpNew data from HTTP source
new_data.mqttNew data from MQTT source
new_data.wsNew data from WebSocket source
rule.state_changeDevice field state changed by rule
task.createNew task created
task.ackTask acknowledged
task.closeTask closed
task.reopenTask reopened

The booking service should integrate with this existing webhook infrastructure.