# Calendar & Contacts API ## logic.md This document defines the COMPLETE business logic layer of the Calendar & Contacts API. It explains: * Permission enforcement * Ownership rules * Validation rules * Recurrence engine behavior * Reminder processing * Availability calculation * Booking logic * Transaction boundaries * Audit requirements * Edge case handling This file MUST be treated as the authoritative source for backend logic. --- ## GLOBAL INVARIANTS 1. All timestamps stored in UTC. 2. All API timestamps are RFC3339 strings. 3. Ownership is ALWAYS derived from auth context. 4. Soft deletes are enforced everywhere. 5. No endpoint may leak data across users. 6. All mutations must write audit log entries. --- 1. AUTHENTICATION & CONTEXT --- Every authenticated request must result in a RequestContext struct: RequestContext: * user_id (uuid) * auth_method ("jwt" | "api_key") * scopes (if api_key) JWT auth: * Validate signature * Validate expiration * Extract user_id API key auth: * Hash provided key * Lookup in api_keys * Ensure revoked_at is NULL * Load scopes If neither provided → AUTH_REQUIRED If invalid → AUTH_INVALID --- 2. PERMISSION MODEL --- Calendar roles: * owner * editor * viewer Permission matrix: CALENDAR ACTIONS * owner: full access * editor: read calendar, CRUD events * viewer: read-only EVENT ACTIONS * owner/editor: create/update/delete * viewer: read CONTACT ACTIONS * Only owner of contacts can CRUD BOOKING * Only owner can create booking links API KEY SCOPE ENFORCEMENT Each endpoint maps to required scope. Example: * GET /calendars → calendars:read * POST /events → events:write * GET /contacts → contacts:read If scope missing → FORBIDDEN --- 3. USER LOGIC --- REGISTER * Email lowercase * Must be unique * Password >=10 chars * Hash with bcrypt cost >=12 * Create default calendar * Return tokens DELETE USER * Soft delete user * Soft delete calendars * Soft delete events * Soft delete contacts * Revoke API keys --- 4. CALENDAR LOGIC --- CREATE * owner_id = authenticated user * name required (1..80) * color optional hex validation LIST Return calendars where: * owner_id = user_id OR * calendar_members.user_id = user_id Include role in response. DELETE * Only owner * Soft delete calendar * Soft delete all related events SHARING * Only owner can share * Cannot share with self * Upsert membership row REMOVE MEMBER * Only owner * Cannot remove owner --- 5. EVENT LOGIC --- CREATE EVENT Validation: * calendar exists * user has editor or owner role * title 1..140 * end_time > start_time * timezone valid IANA Time handling: * Convert start_time to UTC * Convert end_time to UTC * Store original timezone string Overlap rule: * Overlap allowed by default * Booking system enforces no-overlap If recurrence_rule provided: * Validate via rrule-go * Store string If reminders provided: * Insert reminders * Schedule jobs UPDATE EVENT * Same permission check * Re-validate time constraints * If recurrence changed → validate again * Reschedule reminders DELETE EVENT * Soft delete --- 6. RECURRENCE ENGINE --- Recurring events are NOT pre-expanded. Storage: * recurrence_rule string on master event Expansion occurs ONLY during: * GET /events * GET /events/{id}/occurrences Algorithm: For each event in DB where: * event.start_time <= range_end If no recurrence_rule: * Include if intersects range If recurrence_rule present: * Initialize rrule with DTSTART = event.start_time * Generate occurrences within [range_start, range_end] * For each occurrence: * Check against exceptions * Create virtual occurrence object Occurrence response must include: * is_occurrence = true * occurrence_start_time * occurrence_end_time Exceptions table: * event_id * exception_date * action ("skip") If occurrence matches exception_date → skip --- 7. REMINDER PROCESSING --- Reminder scheduling rule: For each reminder (minutes_before): trigger_time = event.start_time - minutes_before If trigger_time > now: * Enqueue Asynq job Job payload: * event_id * reminder_id * user_id Worker logic: * Load event * If event.deleted_at != NULL → abort * Send notification (webhook/email placeholder) * Retry on failure with exponential backoff On event update: * Cancel old reminder jobs (if supported) * Recompute schedule --- 8. CONTACT LOGIC --- Contacts are strictly per-user. CREATE * Must have at least one identifying field * email validated if present SEARCH * Case-insensitive match on: * first_name * last_name * email * company DELETE * Soft delete --- 9. AVAILABILITY LOGIC --- Endpoint requires: * calendar_id * start * end Permission: * viewer or higher Busy condition: An event is busy if: (event.start_time < range_end) AND (event.end_time > range_start) Recurring events: * Expand occurrences * Apply same intersection rule Return busy blocks sorted by start. --- 10. BOOKING SYSTEM LOGIC --- CREATE BOOKING LINK * Only owner * Generate secure random token * Store configuration PUBLIC AVAILABILITY * No auth * Load booking link * Validate active * Compute working-hour windows * Subtract busy blocks * Apply buffer_minutes before/after events RESERVATION * Begin DB transaction * Re-check slot availability with overlap query * If conflict → ROLLBACK + CONFLICT * Insert event * Commit Overlap query inside transaction: SELECT 1 FROM events WHERE calendar_id = ? AND deleted_at IS NULL AND start_time < slot_end AND end_time > slot_start FOR UPDATE This ensures race safety. --- 11. CURSOR PAGINATION --- Cursor format: base64url(last_sort_time|last_id) Decoding: * Split by pipe * Use as tuple comparison Events sorting: ORDER BY start_time ASC, id ASC Contacts sorting: ORDER BY created_at ASC, id ASC Limit enforcement: * Default 50 * Max 200 --- 12. AUDIT LOG LOGIC --- Every mutation writes: entity_type entity_id action user_id timestamp Actions examples: * CREATE_EVENT * UPDATE_EVENT * DELETE_EVENT * SHARE_CALENDAR * DELETE_CONTACT Audit writes MUST NOT fail main transaction. If audit insert fails: * Log error * Continue response --- 13. ERROR RULES --- AUTH_REQUIRED → no credentials AUTH_INVALID → invalid token FORBIDDEN → permission denied NOT_FOUND → entity not found or not accessible VALIDATION_ERROR → invalid input CONFLICT → overlap or duplicate INTERNAL → unexpected error Never expose internal DB errors directly. --- 14. TRANSACTION RULES --- Transactions REQUIRED for: * Booking reservation * Event creation with reminders * Event update affecting reminders * Deleting calendar (cascade soft delete) Service layer must manage transactions. Repository layer must not auto-commit. --- 15. PERFORMANCE REQUIREMENTS --- All list endpoints: * Must use indexed columns * Must use pagination * Must avoid N+1 queries Recurring expansion must be bounded by requested range. Maximum recurrence expansion window allowed per request: * 1 year (recommended safeguard) If range exceeds safeguard → VALIDATION_ERROR --- This is the authoritative backend logic specification.