---
title: "Request an app/site unlock from the teacher"
method: POST
path: "/class-session/{id}/unlock-requests"
tags: ["class-session"]
---

# Request an app/site unlock from the teacher

`POST /class-session/{id}/unlock-requests`

## Request

- Base URL: `https://api.focusbear.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.focusbear.io/class-session/{id}/unlock-requests`
- Auth: HTTP bearer

## Path parameters

- `id` string, uuid, required

## Request body

- SubmitUnlockRequestDto
  - `requested_domain` string
  - `requested_app` string
  - `reason` string

## Response `201`

- ClassSessionUnlockRequest
  - `class_session_id` string, required
  - `student_id` string, required
  - `requested_domain` string
  - `requested_app` string
  - `reason` string
  - `ai_reason` string: The AI's reply on an AI-contest verdict row; null on teacher requests.
  - `status` object, required
  - `resolved_by` string: Teacher who approved/denied the request; null for AI-contest verdicts (the AI resolved them).
  - `resolved_at` string, date-time
  - `class_session` ClassFocusSession
    - `team_id` string, required
    - `initiated_by` string, required: Teacher (team admin) who started the session.
    - `focus_mode_id` string, nullable: The team-shared focus mode behind this session. Every joined student's enforced block points at this ONE row; the mode persists after the session for reuse (school/class-wide). The session's own allow-list fields stay the live source of truth for amendments.
    - `focus_mode` FocusMode
      - `user_id` string
      - `name` string
      - `allowed_apps` string[]
      - `allowed_urls` string[]
      - `metadata` object
      - `categories` object[]: Per-entry categories for this block list: `[{ entry, category }]`, where `entry` mirrors an `allowed_urls` / `allowed_apps` value and `category` is a client taxonomy id. Encrypted like the other list columns, because the entries are the user's own blocked sites. Nullable with no SQL default on purpose: a plaintext `'[]'` default could not be read back through the encryption transformer, so "empty" is applied in code by the response serializer. Deliberately outside `MAX_ALLOWLIST_TOTAL_BYTES`: that budget exists because a shared mode's allow-lists ride verbatim inside Pusher/FCM payloads, and `categories` never does — a class session snapshots only `allowed_apps` / `allowed_urls`. Anything that starts fanning the whole mode out over realtime must bring this column into that budget.
      - `app_identities` object[], nullable: Platform identities for the app entries (`allowed_apps`, and on focus modes the Windows app's `metadata.allowed_apps_win`), keyed by `entry` like `categories`. Clients match the running app by these instead of a renameable name; entries with none are matched by name as before. Filled from the common-apps catalog on every write, so older clients that send names only still get them. Encrypted and nullable with no SQL default for the same reason as `categories`.
      - `is_ai_enabled` boolean, required
      - `focus_mode_template_id` string
      - `team_id` string, nullable: Team-shared focus mode (Teacher Mode): team_id = school (parent team) makes the mode visible school-wide, team_id = class makes it class-wide; NULL = personal mode. `user_id` stays the creator; shared modes are managed by that team's admins.
      - `team` Team
        - `owner_id` string, nullable
        - `team_size` number
        - `team_size_limit` number
        - `is_active` boolean
        - `expires_date` object
        - `stripe_data` object
        - `stripe_subscription_id` string
        - `name` string
        - `payment_type` string
        - `parent_team_id` string
        - `parent_team` Team (recursive)
        - `is_class` boolean
        - `is_school` boolean
        - `archived_at` string, date-time, nullable
        - `class_session_window_start` string
        - `class_session_window_end` string
        - `concern_focus_threshold_pct` number
        - `concern_lookback_sessions` number
        - `child_teams` Team[]
        - `owner` User
          - `stripe_customer_id` string, nullable
          - `profitwell_id` string
          - `profitwell_registration_date` string, date-time
          - `auth0_id` string
          - `startup_time` string
          - `shutdown_time` string
          - `utc_startup_time` string
          - `utc_shutdown_time` string
          - `routine_notification_times` RoutineNotificationTimes
            - `last_time_notified_of_morning_routine` string, date-time, nullable, required
            - `last_time_notified_of_evening_routine` string, date-time, nullable, required
            - `last_date_notified_of_custom_routines` object
          - `break_after_minutes` number
          - `current_focus_mode_finish_time` string, date-time
          - `password_for_settings` string
          - `is_office_mode_activated` boolean
          - `current_activity_sequence_id` string
          - `current_focus_mode_id` string
          - `current_completing_focus_block_id` string
          - `current_activity_id` string
          - `current_activity_assigned_at` string, date-time
          - `last_completed_sequence_id` string
          - `current_completing_sequence_log_id` string
          - `last_completed_sequence_at` string, date-time
          - `last_completed_sequence_started_at` string, date-time
          - `current_sequence_started_at` string, date-time
          - `local_device_settings` UpdateLocalDeviceSettingsDto
            - `MacOS` object
            - `Windows` object
            - `Android` object
            - `iOS` object
            - `Web` WebDeviceSettingsDto
              - …
          - `user_type` 'STANDARD' | 'ADMIN'
          - `signed_up_via_habit_pack` string
          - `signed_up_via_focus_mode` string
          - `current_sequence_skipped_activities` string[]
          - `timezone` string
          - `iana_timezone` string, nullable: The user's explicitly declared IANA zone (for example `Australia/Melbourne`), or null when unset. Distinct from `timezone`, which is a device-synced UTC offset continuously overwritten by client settings sync. Written only by PATCH /user/timezone.
          - `has_edited_settings` boolean
          - `cutoff_time_for_non_high_priority_activities` string
          - `metadata` UserMetadata
            - `profile_image` string
            - `description` string
            - `name` string
            - `last_email_sent` string
            - `email_preferences` object
          - `user_job_details` string
          - `user_typical_distractions` string
          - `habit_theme` string, nullable: The habit theme chosen during onboarding (e.g. "Elmo"), reused for routine notifications (#2617).
          - `themed_routine_notification_titles` object, nullable: Written once when the theme is saved, so sending a routine notification never calls the AI (#2617).
          - `onboarding_progress` UserOnboardingProgress
            - `level` number
            - `has_edited_focus_mode` boolean
            - `has_edited_settings` boolean
            - `has_edited_always_blocked_urls` boolean
            - `has_installed_desktop_app` boolean
            - `has_installed_mobile_app` boolean
            - `has_chatted_with_focus_bear` boolean
          - `onboarding_completed_at` string, date-time, nullable
          - `last_time_stats_updated` string, date-time
          - `last_top_score_refreshed_for_date` string, nullable: @brief Last user-local date for which `to_do.top_score` has been refreshed. @details Stored as a SQL `date` (no time component) holding the user's local calendar date at the time of the last refresh. The off-thread top-score cron compares this against the user's current local "today" (computed via the stored `timezone`) and enqueues a recompute job when the local clock has crossed midnight. Updated by the consumer on successful refresh.
          - `last_completed_focus_mode_at` string, date-time
          - `morning_routines_streak` number
          - `evening_routines_streak` number
          - `focus_modes_streak` number
          - `micro_breaks_streak` number
          - `morning_percent_number_day_of_stats_completed` number
          - `evening_percent_number_day_of_stats_completed` number
          - `micro_percent_number_day_of_stats_completed` number
          - `morning_number_days_completed` number
          - `morning_num_days_of_stats` number
          - `evening_number_days_completed` number
          - `evening_num_days_of_stats` number
          - `micro_breaks_number_days_completed` number
          - `micro_breaks_num_days_of_stats` number
          - `focus_modes_number_days_completed` number
          - `focus_modes_num_days_of_stats` number
          - `num_days_of_stats` number
          - `number_days_completed` number
          - `has_consented_to_terms_of_service` boolean
          - `has_consented_to_privacy_policy` boolean
          - `long_term_goals` string[]
          - `verbose_logging` boolean
          - `last_time_user_settings_modified` string, date-time
          - `username` string
          - `has_received_inactivity_warning` boolean
          - `language` string
          - `email_frequency` 'daily' | 'weekly' | 'monthly' | 'unsubscribed'
          - `feature_flags` string[]
          - `revenue_cat_data` SubscriptionStatus
            - `hasActiveSubscription` boolean, required
            - `activeEntitlements` string[], required
            - `expirations` object
            - `entitlements` object
            - `subscriptions` object
          - `revenue_cat_status` string
          - `last_date_revenue_cat_data_synced` string, date-time
          - `last_status_synced_with_profitwell` string
          - `last_date_gave_feedback` string, date-time
          - `is_relax_activity_generated` boolean
          - `is_internal_tester` boolean: Set to true when the mobile app reports this install as an internal tester build (see mobile-app#4848). Write-once: only ever flips false -> true so a later production reinstall never demotes a known staff account. Used to keep internal testers out of analytics cohorts even when they use real emails.
          - `internal_tester_source` 'build_flag' | 'ios_testflight' | 'android_non_play', nullable
          - `synced_projects` SyncedProject[]
            - `user_id` string
            - `platform` string
            - `external_project_id` string
            - `external_portal_id` string
            - `available_statuses` ExternalTaskStatus[]
              - …
            - `have_tasks_been_synced` boolean
            - `name` string
            - `synced_at` string, date-time
            - `user` User (recursive)
            - `to_dos` ToDo[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `consents` UserConsent[]
            - `user_id` string
            - `consent_type` 'event_tracking' | 'privacy_policy' | 'terms_of_service' | 'data_processing' | 'marketing_emails', required
            - `consent_status` boolean
            - `withdrawal_date` string, date-time
            - `metadata` UserConsentMetadata
              - …
            - `policy_version` string, nullable
            - `consented_at` string, date-time, nullable
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `activity_sequences` ActivitySequence[]
            - `user_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `activity_ids` string[]
            - `generated_sequence_activity_ids` string[]
            - `total_duration_seconds` number
            - `generated_total_duration_seconds` number
            - `pack_id` string
            - `custom_routine_id` string
            - `user` User (recursive)
            - `habit_pack` HabitPack
              - …
            - `activities` Activity[]
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `completed_activity_sequences` CompletedActivitySequence[]
              - …
            - `custom_routine` CustomRoutine
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_activities` CompletedActivity[]
            - `user_id` string
            - `activity_id` string
            - `activity_sequence_id` string
            - `start_time` string, date-time
            - `finish_time` string, date-time
            - `quantity_logged` number
            - `duration_logged` number
            - `completed_sequence_id` string
            - `activity_note` string
            - `metadata` CompletedActivityMetadata
            - `user` User (recursive)
            - `activity` Activity
              - …
            - `activity_sequence` ActivitySequence
              - …
            - `completed_sequence_log` CompletedActivitySequence
              - …
            - `answers` LogQuantityAnswer[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_activity_sequences` CompletedActivitySequence[]
            - `user_id` string
            - `activity_sequence_id` string
            - `start_time` string, date-time
            - `ownership_window_start` string, date-time
            - `finish_time` string, date-time
            - `duration_minutes` number
            - `plan_duration_minutes` number
            - `is_completed` boolean
            - `was_force_completed` boolean
            - `duration_percent_deviation` number
            - `user` User (recursive)
            - `activity_sequence` ActivitySequence
              - …
            - `completed_activity_logs` CompletedActivity[]
              - …
            - `completed_morning_sequence` DailyStats
              - …
            - `completed_evening_sequence` DailyStats
              - …
            - `completed_break_sequence` DailyStats, required
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_focus_blocks` CompletedFocusBlock[]
            - `user_id` string
            - `focus_mode_id` string
            - `start_time` string, date-time
            - `finish_time` string, date-time
            - `scheduled_finish_time` string, date-time
            - `status` 'active' | 'paused' | 'completed'
            - `paused_at` string, date-time
            - `total_paused_seconds` number
            - `intention` string
            - `achievements` string
            - `distractions` string
            - `focus_duration_seconds` number
            - `metadata` object
            - `user` User (recursive)
            - `focus_mode` FocusMode (recursive)
            - `tags` FocusModeTag[]
              - …
            - `to_dos` ToDo[]
              - …
            - `block_level` 'gentle' | 'strict' | 'super-strict', nullable
            - `version` number
            - `class_session_id` string, nullable
            - `project_id` string, nullable
            - `project` Project
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `devices` Device[]
            - `user_id` string
            - `operating_system` 'MacOS' | 'Windows' | 'Android' | 'iOS' | 'Web' | 'Unknown'
            - `is_leader` boolean
            - `app_version` string
            - `metadata` DeviceMetadata
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `to_dos` ToDo[]
            - `user_id` string
            - `focus_type` string
            - `synced_project_id` string
            - `project_id` string
            - `assignee_id` string
            - `assigned_mcp_token_id` string
            - `custom_status_id` string
            - `title` string, required
            - `details` string, required
            - `objective` string, required
            - `external_task_id` string
            - `external_task_metadata` object
            - `subtasks` SubtaskDto[]
              - …
            - `due_date` string, date-time, nullable
            - `eisenhower_quadrant` number, required
            - `outcome` number, required
            - `perspiration_level` number, required
            - `status` string, required
            - `duration` number
            - `icon` string
            - `top_score` number, nullable: @brief Materialised priority score, written by application code. @details Mirrors the SQL expression that previously computed the "top score" inside `ORDER BY` on every page load. Persisting the value allows the planner to satisfy `ORDER BY ... LIMIT` with the `(user_id, top_score DESC)` index instead of a parallel sequential scan + in-memory sort. The score depends on the user's local "today", so it is refreshed daily by the off-thread top-score worker as well as on every write that touches `due_date`, `outcome`, or `perspiration_level`. PostgreSQL's `numeric` type round-trips through node-postgres as a string; the transformer below converts it to `number` on read so the application can treat it as a plain numeric value.
            - `current_external_status_id` string, nullable: @brief Materialised external status identifier from the linked platform. @details Populated by the integration sync worker so list-view rendering no longer needs to decrypt/parse the `external_task_metadata` JSONB on the request path.
            - `current_external_status_label` string, nullable: @brief Materialised external status label from the linked platform. @details See `current_external_status_id`. The label is exposed via the API as the human-readable status text.
            - `planned_date` string, nullable: @brief Day Plan pointer: the day this todo is planned for (YYYY-MM-DD). @details NULL means the todo is not in any Day Plan. A planned task is a pointer into `to_do` by date, not a copy; the Day Plan panel reads todos by `planned_date` and writes planning changes through `PATCH /to-do/:id`.
            - `planned_block_key` string, nullable: @brief Day Plan time-box assignment within the planned day. @details Opaque, never parsed by the backend. One of `event:<id>`, `deepwork:<unix-epoch>`, `habit:<title>`, or NULL when planned but not yet time-boxed onto a block. Encrypted at rest because the `habit:<title>` form can embed a habit name, matching `title`/ `details`. The column stays unbounded `varchar` (ciphertext is longer than plaintext); the DTO caps plaintext input at 256 chars.
            - `planned_order` number, nullable: @brief Manual ordering within the planned day (drag-to-reorder).
            - `recurrence_rule` string, nullable: @brief Recurrence: canonical RFC 5545 RRULE content line, e.g. `RRULE:FREQ=WEEKLY;INTERVAL=2` for a fortnightly chore. @details NULL means the todo is a one-off and takes no recurrence code path, which is every todo that existed before this column. Set together with the other three `recurrence_*` columns (a DB CHECK enforces all-or-none). Validated and canonicalised through the same RFC 5545 subset the Day Plan's event series use.
            - `recurrence_timezone` string, nullable: @brief Recurrence: IANA zone the rule is evaluated in (keeps the local time of day across DST).
            - `recurrence_dtstart` string, date-time, nullable: @brief Recurrence: the series DTSTART — the first occurrence's due date. @details Copied unchanged onto every occurrence so the next due date is always computed from the series anchor. Completing an occurrence late therefore does not shift the schedule.
            - `recurrence_series_id` string, nullable: @brief Recurrence: groups every occurrence of one repeating todo. @details Equal to the first occurrence's own id. Lets history be read per series and `DELETE /to-do?scope=series` remove the whole thing.
            - `user` User (recursive)
            - `synced_project` SyncedProject
              - …
            - `project` Project
              - …
            - `assignee` User (recursive)
            - `focus_mode` FocusMode (recursive)
            - `task_time_logs` TaskTimeLog[]
              - …
            - `tags` FocusModeTag[]
              - …
            - `completedFocusBlocks` CompletedFocusBlock[], required
              - …
            - `attachments` TaskAttachment[]
              - …
            - `attachment_count` number
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `notifications` Notification[]
            - `user_id` string
            - `platform` 'google' | 'microsoft'
            - `platform_account` string
            - `calendar_id` string
            - `external_id` string
            - `summary` string
            - `description` string
            - `event_begins` string, date-time
            - `event_ends` string, date-time
            - `is_dismissed` boolean
            - `dismiss_reason` string
            - `received` boolean
            - `notified_silent_at` string, date-time, nullable
            - `user` User (recursive)
            - `external_metadata` object
            - `notification_type` 'calendar_event' | 'accountability_buddy_invitation' | 'accountability_buddy_invitation_accepted' | 'unlock_request_received' | 'unlock_request_approved' | 'unlock_request_rejected'
            - `action_url` string
            - `related_entity_id` string
            - `related_entity_type` string
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `focus_modes` FocusMode[]
          - `blocking_schedules` BlockingSchedule[]
            - `days_of_week_text` string[]
            - `user_id` string
            - `name` string
            - `start_time` string
            - `end_time` string
            - `days_of_week` number[]
            - `focus_mode_id` string
            - `pause_friction` 'none' | 'timer' | '100_random_chars' | 'password'
            - `block_level` 'gentle' | 'strict' | 'super-strict'
            - `is_ai_blocking_enabled` boolean
            - `emoji` string, nullable
            - `metadata` object
            - `user` User (recursive)
            - `focus_mode` FocusMode (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `focus_mode_templates` FocusModeTemplate[]
            - `author_id` string
            - `author_name` string
            - `name` string
            - `allowed_apps` string[]
            - `allowed_urls` string[]
            - `description` string
            - `description_video_url` string
            - `welcome_message` string
            - `welcome_video_url` string
            - `description_plain_text` string
            - `welcome_message_plain_text` string
            - `marketplace_approval_status` boolean
            - `marketplace_request` 'requested' | 'unrequested'
            - `is_featured` boolean
            - `featured_for_onboarding` boolean
            - `language` string
            - `deleted_at` string, date-time
            - `author` User (recursive)
            - `installs` InstalledFocusModeTemplate[]
              - …
            - `focus_modes` FocusMode[]
            - `tags` FocusModeTag[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `created_habit_packs` HabitPack[]
            - `user_id` string
            - `pack_type` 'routine' | 'standalone'
            - `pack_name` string
            - `creator_name` string
            - `description` string
            - `description_video_url` string
            - `welcome_message` string
            - `welcome_video_url` string
            - `description_plain_text` string
            - `welcome_message_plain_text` string
            - `marketplace_approval_status` boolean
            - `marketplace_request` 'requested' | 'unrequested'
            - `is_featured` boolean
            - `featured_for_onboarding` boolean
            - `language` string
            - `morning_routine_duration_seconds` number
            - `evening_routine_duration_seconds` number
            - `duration` number
            - `breaks_only` boolean
            - `deleted_at` string, date-time
            - `installs` InstalledPack[]
              - …
            - `activity_sequences` ActivitySequence[]
              - …
            - `activity_templates` ActivityTemplate[]
              - …
            - `signed_up_users` User[]
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `installed_packs` InstalledPack[]
            - `user_id` string
            - `pack_id` string
            - `installation_status` boolean
            - `activity_sequence_id` string
            - `user` User (recursive)
            - `habit_pack` HabitPack
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `installed_focus_modes` InstalledFocusModeTemplate[]
            - `user_id` string
            - `focus_mode_template_id` string
            - `installation_status` boolean
            - `user` User (recursive)
            - `focus_mode_template` FocusModeTemplate
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `activity_templates` ActivityTemplate[]
            - `pack_id` string
            - `user_id` string
            - `activity_type` string
            - `log_summary_type` string
            - `log_quantity` boolean
            - `activity_data` ActivityData
              - …
            - `has_choices` boolean
            - `duration_seconds` number
            - `completion_requirements` string
            - `parent_id` string
            - `linked_activity_template_id` string
            - `sequence_index` number
            - `check_list` string[]
            - `impact_category` 'hours_of_sleep' | 'energy_level_upon_awakening' | 'mood' | 'perception_of_productivity' | 'minutes_spent_on_distracting_websites' | 'minutes_spent_postponing_app_blocks' | 'minutes_spent_postponing_habits'
            - `deleted_at` string, date-time
            - `parent_activity` ActivitySequence
              - …
            - `choices` ActivityTemplate[]
            - `linked_activity_template` ActivitySequence
              - …
            - `linked_activity_templates` Activity[]
              - …
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `habit_pack` HabitPack
              - …
            - `user` User (recursive)
            - `activities` Activity[]
              - …
            - `tutorial` Tutorial
              - …
            - `tags` ActivityTemplateTag[]
              - …
            - `embeddings` ActivityTemplateEmbedding[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `activities` Activity[]
            - `user_id` string
            - `parent_id` string
            - `linked_activity_id` string
            - `activity_template_id` string
            - `has_choices` boolean
            - `activity_sequence_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `log_summary_type` string
            - `log_quantity` boolean
            - `duration_seconds` number
            - `completion_requirements` string
            - `activity_data` ActivityData
              - …
            - `is_default` boolean
            - `run_micro_breaks` boolean
            - `days_of_week` string[]
            - `check_list` string[]
            - `impact_category` 'hours_of_sleep' | 'energy_level_upon_awakening' | 'mood' | 'perception_of_productivity' | 'minutes_spent_on_distracting_websites' | 'minutes_spent_postponing_app_blocks' | 'minutes_spent_postponing_habits'
            - `cutoff_time_for_doing_activity` string
            - `geofence_id` string
            - `is_deleted` boolean
            - `geofence` Geofence
              - …
            - `activity_sequence` ActivitySequence
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `parent_activity` ActivitySequence
              - …
            - `linked_activity` ActivitySequence
              - …
            - `linked_activities` Activity[]
            - `choices` Activity[]
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `user` User (recursive)
            - `user_activities` User (recursive)
            - `activity_template` ActivityTemplate
              - …
            - `tutorial` Tutorial
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `log_quantity_questions` LogQuantityQuestion[]
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `log_quantity_answers` LogQuantityAnswer[]
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `saved_websites` SavedWebsite[]
            - `user_id` string, required
            - `url` string, required
            - `title` string
            - `metadata` object, required
            - `note` string
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `impact_events` ImpactEvent[]
            - `user_id` string
            - `impact_category` 'hours_of_sleep' | 'energy_level_upon_awakening' | 'mood' | 'perception_of_productivity' | 'minutes_spent_on_distracting_websites' | 'minutes_spent_postponing_app_blocks' | 'minutes_spent_postponing_habits'
            - `quantity` number
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `feedback` UserFeedback[]
            - `user_id` string
            - `rating` number
            - `feedback` string
            - `metadata` object
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `task_time_logs` TaskTimeLog[]
            - `user_id` string
            - `completed_focus_block_id` string
            - `task_id` string
            - `duration_logged_seconds` number
            - `note` string
            - `user` User (recursive)
            - `to_do` ToDo
              - …
            - `completed_focus_block` CompletedFocusBlock
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `platform_integrations` PlatformIntegration[]
            - `user_id` string
            - `platform` string
            - `external_user_id` string
            - `data` PlatformIntegrationMetadataDto
              - …
            - `only_assigned` boolean
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `owned_teams` Team[]
          - `calendar_keywords` CalendarExcludedKeyword[]
            - `user_id` string
            - `keyword` string
            - `platform` 'google' | 'microsoft'
            - `intitle` boolean
            - `indescription` boolean
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `calendars` Calendar[]
            - `user_id` string
            - `platform` 'google' | 'microsoft'
            - `platform_account` string
            - `calendar_id` string
            - `summary` string
            - `is_selected` boolean
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `teamToMember` TeamToMember[]
            - `team_id` string, required
            - `member_id` string, required
            - `first_name` string
            - `last_name` string
            - `year_level` string, nullable
            - `member_expiry_date` string, date-time
            - `invitation_status` 'failed' | 'pending' | 'accepted' | 'declined' | 'expired', required
            - `invited_role` 'admin' | 'teacher' | 'member', nullable
            - `invitation_sent_at` string, date-time
            - `invitation_responded_at` string, date-time
            - `invitation_send_count` number, required
            - `email` string
            - `member` User, required (recursive)
            - `team` Team, required (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `teamToAdmin` TeamToAdmin[]
            - `team_id` string, required
            - `admin_id` string, required
            - `admin` User, required (recursive)
            - `team` Team, required (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `teamToTeacher` TeamToTeacher[]
            - `team_id` string, required
            - `teacher_id` string, required
            - `teacher` User, required (recursive)
            - `team` Team, required (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `accountability_buddies` AccountabilityBuddy[]
            - `user_id` string, required
            - `buddy_user_id` string
            - `buddy_email` string, required
            - `invitation_status` 'pending' | 'accepted' | 'rejected' | 'expired', required
            - `invitation_sent_at` string, date-time
            - `invitation_responded_at` string, date-time
            - `user` User (recursive)
            - `buddy` User (recursive)
            - `unlock_requests` UnlockRequest[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `sign_up_habit_pack` User (recursive)
          - `current_activity` Activity
            - `user_id` string
            - `parent_id` string
            - `linked_activity_id` string
            - `activity_template_id` string
            - `has_choices` boolean
            - `activity_sequence_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `log_summary_type` string
            - `log_quantity` boolean
            - `duration_seconds` number
            - `completion_requirements` string
            - `activity_data` ActivityData
              - …
            - `is_default` boolean
            - `run_micro_breaks` boolean
            - `days_of_week` string[]
            - `check_list` string[]
            - `impact_category` 'hours_of_sleep' | 'energy_level_upon_awakening' | 'mood' | 'perception_of_productivity' | 'minutes_spent_on_distracting_websites' | 'minutes_spent_postponing_app_blocks' | 'minutes_spent_postponing_habits'
            - `cutoff_time_for_doing_activity` string
            - `geofence_id` string
            - `is_deleted` boolean
            - `geofence` Geofence
              - …
            - `activity_sequence` ActivitySequence
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `parent_activity` ActivitySequence
              - …
            - `linked_activity` ActivitySequence
              - …
            - `linked_activities` Activity[]
            - `choices` Activity[]
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `user` User (recursive)
            - `user_activities` User (recursive)
            - `activity_template` ActivityTemplate
              - …
            - `tutorial` Tutorial
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `current_activity_sequence` ActivitySequence
            - `user_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `activity_ids` string[]
            - `generated_sequence_activity_ids` string[]
            - `total_duration_seconds` number
            - `generated_total_duration_seconds` number
            - `pack_id` string
            - `custom_routine_id` string
            - `user` User (recursive)
            - `habit_pack` HabitPack
              - …
            - `activities` Activity[]
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `completed_activity_sequences` CompletedActivitySequence[]
              - …
            - `custom_routine` CustomRoutine
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `last_completed_sequence` ActivitySequence
            - `user_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `activity_ids` string[]
            - `generated_sequence_activity_ids` string[]
            - `total_duration_seconds` number
            - `generated_total_duration_seconds` number
            - `pack_id` string
            - `custom_routine_id` string
            - `user` User (recursive)
            - `habit_pack` HabitPack
              - …
            - `activities` Activity[]
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `completed_activity_sequences` CompletedActivitySequence[]
              - …
            - `custom_routine` CustomRoutine
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completing_sequence_log` CompletedActivitySequence
            - `user_id` string
            - `activity_sequence_id` string
            - `start_time` string, date-time
            - `ownership_window_start` string, date-time
            - `finish_time` string, date-time
            - `duration_minutes` number
            - `plan_duration_minutes` number
            - `is_completed` boolean
            - `was_force_completed` boolean
            - `duration_percent_deviation` number
            - `user` User (recursive)
            - `activity_sequence` ActivitySequence
              - …
            - `completed_activity_logs` CompletedActivity[]
              - …
            - `completed_morning_sequence` DailyStats
              - …
            - `completed_evening_sequence` DailyStats
              - …
            - `completed_break_sequence` DailyStats, required
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completing_focus_block` CompletedFocusBlock
            - `user_id` string
            - `focus_mode_id` string
            - `start_time` string, date-time
            - `finish_time` string, date-time
            - `scheduled_finish_time` string, date-time
            - `status` 'active' | 'paused' | 'completed'
            - `paused_at` string, date-time
            - `total_paused_seconds` number
            - `intention` string
            - `achievements` string
            - `distractions` string
            - `focus_duration_seconds` number
            - `metadata` object
            - `user` User (recursive)
            - `focus_mode` FocusMode (recursive)
            - `tags` FocusModeTag[]
              - …
            - `to_dos` ToDo[]
              - …
            - `block_level` 'gentle' | 'strict' | 'super-strict', nullable
            - `version` number
            - `class_session_id` string, nullable
            - `project_id` string, nullable
            - `project` Project
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `current_focus_mode` FocusMode (recursive)
          - `tutorials` Tutorial[]
            - `activity_id` string, required
            - `user_id` string, required
            - `activity_template_id` string
            - `activity` Activity
              - …
            - `user` User, required (recursive)
            - `activity_template` ActivityTemplate
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `cancel_subscription_feedback` Feedback
            - `cancel_subscription_reason` string, required
            - `user_id` string, required
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `custom_routines` CustomRoutine[]
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `teamToMember` TeamToMember[]
          - `team_id` string, required
          - `member_id` string, required
          - `first_name` string
          - `last_name` string
          - `year_level` string, nullable
          - `member_expiry_date` string, date-time
          - `invitation_status` 'failed' | 'pending' | 'accepted' | 'declined' | 'expired', required
          - `invited_role` 'admin' | 'teacher' | 'member', nullable
          - `invitation_sent_at` string, date-time
          - `invitation_responded_at` string, date-time
          - `invitation_send_count` number, required
          - `email` string
          - `member` User, required
            - `stripe_customer_id` string, nullable
            - `profitwell_id` string
            - `profitwell_registration_date` string, date-time
            - `auth0_id` string
            - `startup_time` string
            - `shutdown_time` string
            - `utc_startup_time` string
            - `utc_shutdown_time` string
            - `routine_notification_times` RoutineNotificationTimes
              - …
            - `break_after_minutes` number
            - `current_focus_mode_finish_time` string, date-time
            - `password_for_settings` string
            - `is_office_mode_activated` boolean
            - `current_activity_sequence_id` string
            - `current_focus_mode_id` string
            - `current_completing_focus_block_id` string
            - `current_activity_id` string
            - `current_activity_assigned_at` string, date-time
            - `last_completed_sequence_id` string
            - `current_completing_sequence_log_id` string
            - `last_completed_sequence_at` string, date-time
            - `last_completed_sequence_started_at` string, date-time
            - `current_sequence_started_at` string, date-time
            - `local_device_settings` UpdateLocalDeviceSettingsDto
              - …
            - `user_type` 'STANDARD' | 'ADMIN'
            - `signed_up_via_habit_pack` string
            - `signed_up_via_focus_mode` string
            - `current_sequence_skipped_activities` string[]
            - `timezone` string
            - `iana_timezone` string, nullable: The user's explicitly declared IANA zone (for example `Australia/Melbourne`), or null when unset. Distinct from `timezone`, which is a device-synced UTC offset continuously overwritten by client settings sync. Written only by PATCH /user/timezone.
            - `has_edited_settings` boolean
            - `cutoff_time_for_non_high_priority_activities` string
            - `metadata` UserMetadata
              - …
            - `user_job_details` string
            - `user_typical_distractions` string
            - `habit_theme` string, nullable: The habit theme chosen during onboarding (e.g. "Elmo"), reused for routine notifications (#2617).
            - `themed_routine_notification_titles` object, nullable: Written once when the theme is saved, so sending a routine notification never calls the AI (#2617).
            - `onboarding_progress` UserOnboardingProgress
              - …
            - `onboarding_completed_at` string, date-time, nullable
            - `last_time_stats_updated` string, date-time
            - `last_top_score_refreshed_for_date` string, nullable: @brief Last user-local date for which `to_do.top_score` has been refreshed. @details Stored as a SQL `date` (no time component) holding the user's local calendar date at the time of the last refresh. The off-thread top-score cron compares this against the user's current local "today" (computed via the stored `timezone`) and enqueues a recompute job when the local clock has crossed midnight. Updated by the consumer on successful refresh.
            - `last_completed_focus_mode_at` string, date-time
            - `morning_routines_streak` number
            - `evening_routines_streak` number
            - `focus_modes_streak` number
            - `micro_breaks_streak` number
            - `morning_percent_number_day_of_stats_completed` number
            - `evening_percent_number_day_of_stats_completed` number
            - `micro_percent_number_day_of_stats_completed` number
            - `morning_number_days_completed` number
            - `morning_num_days_of_stats` number
            - `evening_number_days_completed` number
            - `evening_num_days_of_stats` number
            - `micro_breaks_number_days_completed` number
            - `micro_breaks_num_days_of_stats` number
            - `focus_modes_number_days_completed` number
            - `focus_modes_num_days_of_stats` number
            - `num_days_of_stats` number
            - `number_days_completed` number
            - `has_consented_to_terms_of_service` boolean
            - `has_consented_to_privacy_policy` boolean
            - `long_term_goals` string[]
            - `verbose_logging` boolean
            - `last_time_user_settings_modified` string, date-time
            - `username` string
            - `has_received_inactivity_warning` boolean
            - `language` string
            - `email_frequency` 'daily' | 'weekly' | 'monthly' | 'unsubscribed'
            - `feature_flags` string[]
            - `revenue_cat_data` SubscriptionStatus
              - …
            - `revenue_cat_status` string
            - `last_date_revenue_cat_data_synced` string, date-time
            - `last_status_synced_with_profitwell` string
            - `last_date_gave_feedback` string, date-time
            - `is_relax_activity_generated` boolean
            - `is_internal_tester` boolean: Set to true when the mobile app reports this install as an internal tester build (see mobile-app#4848). Write-once: only ever flips false -> true so a later production reinstall never demotes a known staff account. Used to keep internal testers out of analytics cohorts even when they use real emails.
            - `internal_tester_source` 'build_flag' | 'ios_testflight' | 'android_non_play', nullable
            - `synced_projects` SyncedProject[]
              - …
            - `consents` UserConsent[]
              - …
            - `activity_sequences` ActivitySequence[]
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `completed_activity_sequences` CompletedActivitySequence[]
              - …
            - `completed_focus_blocks` CompletedFocusBlock[]
              - …
            - `devices` Device[]
              - …
            - `to_dos` ToDo[]
              - …
            - `notifications` Notification[]
              - …
            - `focus_modes` FocusMode[]
            - `blocking_schedules` BlockingSchedule[]
              - …
            - `focus_mode_templates` FocusModeTemplate[]
              - …
            - `created_habit_packs` HabitPack[]
              - …
            - `installed_packs` InstalledPack[]
              - …
            - `installed_focus_modes` InstalledFocusModeTemplate[]
              - …
            - `activity_templates` ActivityTemplate[]
              - …
            - `activities` Activity[]
              - …
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `log_quantity_answers` LogQuantityAnswer[]
              - …
            - `saved_websites` SavedWebsite[]
              - …
            - `impact_events` ImpactEvent[]
              - …
            - `feedback` UserFeedback[]
              - …
            - `task_time_logs` TaskTimeLog[]
              - …
            - `platform_integrations` PlatformIntegration[]
              - …
            - `owned_teams` Team[]
            - `calendar_keywords` CalendarExcludedKeyword[]
              - …
            - `calendars` Calendar[]
              - …
            - `teamToMember` TeamToMember[]
            - `teamToAdmin` TeamToAdmin[]
              - …
            - `teamToTeacher` TeamToTeacher[]
              - …
            - `accountability_buddies` AccountabilityBuddy[]
              - …
            - `sign_up_habit_pack` User (recursive)
            - `current_activity` Activity
              - …
            - `current_activity_sequence` ActivitySequence
              - …
            - `last_completed_sequence` ActivitySequence
              - …
            - `completing_sequence_log` CompletedActivitySequence
              - …
            - `completing_focus_block` CompletedFocusBlock
              - …
            - `current_focus_mode` FocusMode (recursive)
            - `tutorials` Tutorial[]
              - …
            - `cancel_subscription_feedback` Feedback
              - …
            - `custom_routines` CustomRoutine[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `team` Team, required (recursive)
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `teamToAdmin` TeamToAdmin[]
          - `team_id` string, required
          - `admin_id` string, required
          - `admin` User, required
            - `stripe_customer_id` string, nullable
            - `profitwell_id` string
            - `profitwell_registration_date` string, date-time
            - `auth0_id` string
            - `startup_time` string
            - `shutdown_time` string
            - `utc_startup_time` string
            - `utc_shutdown_time` string
            - `routine_notification_times` RoutineNotificationTimes
              - …
            - `break_after_minutes` number
            - `current_focus_mode_finish_time` string, date-time
            - `password_for_settings` string
            - `is_office_mode_activated` boolean
            - `current_activity_sequence_id` string
            - `current_focus_mode_id` string
            - `current_completing_focus_block_id` string
            - `current_activity_id` string
            - `current_activity_assigned_at` string, date-time
            - `last_completed_sequence_id` string
            - `current_completing_sequence_log_id` string
            - `last_completed_sequence_at` string, date-time
            - `last_completed_sequence_started_at` string, date-time
            - `current_sequence_started_at` string, date-time
            - `local_device_settings` UpdateLocalDeviceSettingsDto
              - …
            - `user_type` 'STANDARD' | 'ADMIN'
            - `signed_up_via_habit_pack` string
            - `signed_up_via_focus_mode` string
            - `current_sequence_skipped_activities` string[]
            - `timezone` string
            - `iana_timezone` string, nullable: The user's explicitly declared IANA zone (for example `Australia/Melbourne`), or null when unset. Distinct from `timezone`, which is a device-synced UTC offset continuously overwritten by client settings sync. Written only by PATCH /user/timezone.
            - `has_edited_settings` boolean
            - `cutoff_time_for_non_high_priority_activities` string
            - `metadata` UserMetadata
              - …
            - `user_job_details` string
            - `user_typical_distractions` string
            - `habit_theme` string, nullable: The habit theme chosen during onboarding (e.g. "Elmo"), reused for routine notifications (#2617).
            - `themed_routine_notification_titles` object, nullable: Written once when the theme is saved, so sending a routine notification never calls the AI (#2617).
            - `onboarding_progress` UserOnboardingProgress
              - …
            - `onboarding_completed_at` string, date-time, nullable
            - `last_time_stats_updated` string, date-time
            - `last_top_score_refreshed_for_date` string, nullable: @brief Last user-local date for which `to_do.top_score` has been refreshed. @details Stored as a SQL `date` (no time component) holding the user's local calendar date at the time of the last refresh. The off-thread top-score cron compares this against the user's current local "today" (computed via the stored `timezone`) and enqueues a recompute job when the local clock has crossed midnight. Updated by the consumer on successful refresh.
            - `last_completed_focus_mode_at` string, date-time
            - `morning_routines_streak` number
            - `evening_routines_streak` number
            - `focus_modes_streak` number
            - `micro_breaks_streak` number
            - `morning_percent_number_day_of_stats_completed` number
            - `evening_percent_number_day_of_stats_completed` number
            - `micro_percent_number_day_of_stats_completed` number
            - `morning_number_days_completed` number
            - `morning_num_days_of_stats` number
            - `evening_number_days_completed` number
            - `evening_num_days_of_stats` number
            - `micro_breaks_number_days_completed` number
            - `micro_breaks_num_days_of_stats` number
            - `focus_modes_number_days_completed` number
            - `focus_modes_num_days_of_stats` number
            - `num_days_of_stats` number
            - `number_days_completed` number
            - `has_consented_to_terms_of_service` boolean
            - `has_consented_to_privacy_policy` boolean
            - `long_term_goals` string[]
            - `verbose_logging` boolean
            - `last_time_user_settings_modified` string, date-time
            - `username` string
            - `has_received_inactivity_warning` boolean
            - `language` string
            - `email_frequency` 'daily' | 'weekly' | 'monthly' | 'unsubscribed'
            - `feature_flags` string[]
            - `revenue_cat_data` SubscriptionStatus
              - …
            - `revenue_cat_status` string
            - `last_date_revenue_cat_data_synced` string, date-time
            - `last_status_synced_with_profitwell` string
            - `last_date_gave_feedback` string, date-time
            - `is_relax_activity_generated` boolean
            - `is_internal_tester` boolean: Set to true when the mobile app reports this install as an internal tester build (see mobile-app#4848). Write-once: only ever flips false -> true so a later production reinstall never demotes a known staff account. Used to keep internal testers out of analytics cohorts even when they use real emails.
            - `internal_tester_source` 'build_flag' | 'ios_testflight' | 'android_non_play', nullable
            - `synced_projects` SyncedProject[]
              - …
            - `consents` UserConsent[]
              - …
            - `activity_sequences` ActivitySequence[]
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `completed_activity_sequences` CompletedActivitySequence[]
              - …
            - `completed_focus_blocks` CompletedFocusBlock[]
              - …
            - `devices` Device[]
              - …
            - `to_dos` ToDo[]
              - …
            - `notifications` Notification[]
              - …
            - `focus_modes` FocusMode[]
            - `blocking_schedules` BlockingSchedule[]
              - …
            - `focus_mode_templates` FocusModeTemplate[]
              - …
            - `created_habit_packs` HabitPack[]
              - …
            - `installed_packs` InstalledPack[]
              - …
            - `installed_focus_modes` InstalledFocusModeTemplate[]
              - …
            - `activity_templates` ActivityTemplate[]
              - …
            - `activities` Activity[]
              - …
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `log_quantity_answers` LogQuantityAnswer[]
              - …
            - `saved_websites` SavedWebsite[]
              - …
            - `impact_events` ImpactEvent[]
              - …
            - `feedback` UserFeedback[]
              - …
            - `task_time_logs` TaskTimeLog[]
              - …
            - `platform_integrations` PlatformIntegration[]
              - …
            - `owned_teams` Team[]
            - `calendar_keywords` CalendarExcludedKeyword[]
              - …
            - `calendars` Calendar[]
              - …
            - `teamToMember` TeamToMember[]
              - …
            - `teamToAdmin` TeamToAdmin[]
            - `teamToTeacher` TeamToTeacher[]
              - …
            - `accountability_buddies` AccountabilityBuddy[]
              - …
            - `sign_up_habit_pack` User (recursive)
            - `current_activity` Activity
              - …
            - `current_activity_sequence` ActivitySequence
              - …
            - `last_completed_sequence` ActivitySequence
              - …
            - `completing_sequence_log` CompletedActivitySequence
              - …
            - `completing_focus_block` CompletedFocusBlock
              - …
            - `current_focus_mode` FocusMode (recursive)
            - `tutorials` Tutorial[]
              - …
            - `cancel_subscription_feedback` Feedback
              - …
            - `custom_routines` CustomRoutine[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `team` Team, required (recursive)
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `teamToTeacher` TeamToTeacher[]
          - `team_id` string, required
          - `teacher_id` string, required
          - `teacher` User, required
            - `stripe_customer_id` string, nullable
            - `profitwell_id` string
            - `profitwell_registration_date` string, date-time
            - `auth0_id` string
            - `startup_time` string
            - `shutdown_time` string
            - `utc_startup_time` string
            - `utc_shutdown_time` string
            - `routine_notification_times` RoutineNotificationTimes
              - …
            - `break_after_minutes` number
            - `current_focus_mode_finish_time` string, date-time
            - `password_for_settings` string
            - `is_office_mode_activated` boolean
            - `current_activity_sequence_id` string
            - `current_focus_mode_id` string
            - `current_completing_focus_block_id` string
            - `current_activity_id` string
            - `current_activity_assigned_at` string, date-time
            - `last_completed_sequence_id` string
            - `current_completing_sequence_log_id` string
            - `last_completed_sequence_at` string, date-time
            - `last_completed_sequence_started_at` string, date-time
            - `current_sequence_started_at` string, date-time
            - `local_device_settings` UpdateLocalDeviceSettingsDto
              - …
            - `user_type` 'STANDARD' | 'ADMIN'
            - `signed_up_via_habit_pack` string
            - `signed_up_via_focus_mode` string
            - `current_sequence_skipped_activities` string[]
            - `timezone` string
            - `iana_timezone` string, nullable: The user's explicitly declared IANA zone (for example `Australia/Melbourne`), or null when unset. Distinct from `timezone`, which is a device-synced UTC offset continuously overwritten by client settings sync. Written only by PATCH /user/timezone.
            - `has_edited_settings` boolean
            - `cutoff_time_for_non_high_priority_activities` string
            - `metadata` UserMetadata
              - …
            - `user_job_details` string
            - `user_typical_distractions` string
            - `habit_theme` string, nullable: The habit theme chosen during onboarding (e.g. "Elmo"), reused for routine notifications (#2617).
            - `themed_routine_notification_titles` object, nullable: Written once when the theme is saved, so sending a routine notification never calls the AI (#2617).
            - `onboarding_progress` UserOnboardingProgress
              - …
            - `onboarding_completed_at` string, date-time, nullable
            - `last_time_stats_updated` string, date-time
            - `last_top_score_refreshed_for_date` string, nullable: @brief Last user-local date for which `to_do.top_score` has been refreshed. @details Stored as a SQL `date` (no time component) holding the user's local calendar date at the time of the last refresh. The off-thread top-score cron compares this against the user's current local "today" (computed via the stored `timezone`) and enqueues a recompute job when the local clock has crossed midnight. Updated by the consumer on successful refresh.
            - `last_completed_focus_mode_at` string, date-time
            - `morning_routines_streak` number
            - `evening_routines_streak` number
            - `focus_modes_streak` number
            - `micro_breaks_streak` number
            - `morning_percent_number_day_of_stats_completed` number
            - `evening_percent_number_day_of_stats_completed` number
            - `micro_percent_number_day_of_stats_completed` number
            - `morning_number_days_completed` number
            - `morning_num_days_of_stats` number
            - `evening_number_days_completed` number
            - `evening_num_days_of_stats` number
            - `micro_breaks_number_days_completed` number
            - `micro_breaks_num_days_of_stats` number
            - `focus_modes_number_days_completed` number
            - `focus_modes_num_days_of_stats` number
            - `num_days_of_stats` number
            - `number_days_completed` number
            - `has_consented_to_terms_of_service` boolean
            - `has_consented_to_privacy_policy` boolean
            - `long_term_goals` string[]
            - `verbose_logging` boolean
            - `last_time_user_settings_modified` string, date-time
            - `username` string
            - `has_received_inactivity_warning` boolean
            - `language` string
            - `email_frequency` 'daily' | 'weekly' | 'monthly' | 'unsubscribed'
            - `feature_flags` string[]
            - `revenue_cat_data` SubscriptionStatus
              - …
            - `revenue_cat_status` string
            - `last_date_revenue_cat_data_synced` string, date-time
            - `last_status_synced_with_profitwell` string
            - `last_date_gave_feedback` string, date-time
            - `is_relax_activity_generated` boolean
            - `is_internal_tester` boolean: Set to true when the mobile app reports this install as an internal tester build (see mobile-app#4848). Write-once: only ever flips false -> true so a later production reinstall never demotes a known staff account. Used to keep internal testers out of analytics cohorts even when they use real emails.
            - `internal_tester_source` 'build_flag' | 'ios_testflight' | 'android_non_play', nullable
            - `synced_projects` SyncedProject[]
              - …
            - `consents` UserConsent[]
              - …
            - `activity_sequences` ActivitySequence[]
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `completed_activity_sequences` CompletedActivitySequence[]
              - …
            - `completed_focus_blocks` CompletedFocusBlock[]
              - …
            - `devices` Device[]
              - …
            - `to_dos` ToDo[]
              - …
            - `notifications` Notification[]
              - …
            - `focus_modes` FocusMode[]
            - `blocking_schedules` BlockingSchedule[]
              - …
            - `focus_mode_templates` FocusModeTemplate[]
              - …
            - `created_habit_packs` HabitPack[]
              - …
            - `installed_packs` InstalledPack[]
              - …
            - `installed_focus_modes` InstalledFocusModeTemplate[]
              - …
            - `activity_templates` ActivityTemplate[]
              - …
            - `activities` Activity[]
              - …
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `log_quantity_answers` LogQuantityAnswer[]
              - …
            - `saved_websites` SavedWebsite[]
              - …
            - `impact_events` ImpactEvent[]
              - …
            - `feedback` UserFeedback[]
              - …
            - `task_time_logs` TaskTimeLog[]
              - …
            - `platform_integrations` PlatformIntegration[]
              - …
            - `owned_teams` Team[]
            - `calendar_keywords` CalendarExcludedKeyword[]
              - …
            - `calendars` Calendar[]
              - …
            - `teamToMember` TeamToMember[]
              - …
            - `teamToAdmin` TeamToAdmin[]
              - …
            - `teamToTeacher` TeamToTeacher[]
            - `accountability_buddies` AccountabilityBuddy[]
              - …
            - `sign_up_habit_pack` User (recursive)
            - `current_activity` Activity
              - …
            - `current_activity_sequence` ActivitySequence
              - …
            - `last_completed_sequence` ActivitySequence
              - …
            - `completing_sequence_log` CompletedActivitySequence
              - …
            - `completing_focus_block` CompletedFocusBlock
              - …
            - `current_focus_mode` FocusMode (recursive)
            - `tutorials` Tutorial[]
              - …
            - `cancel_subscription_feedback` Feedback
              - …
            - `custom_routines` CustomRoutine[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `team` Team, required (recursive)
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `id` string
        - `created_at` string
        - `updated_at` string
      - `deleted_at` string, date-time
      - `user` User
        - `stripe_customer_id` string, nullable
        - `profitwell_id` string
        - `profitwell_registration_date` string, date-time
        - `auth0_id` string
        - `startup_time` string
        - `shutdown_time` string
        - `utc_startup_time` string
        - `utc_shutdown_time` string
        - `routine_notification_times` RoutineNotificationTimes
          - `last_time_notified_of_morning_routine` string, date-time, nullable, required
          - `last_time_notified_of_evening_routine` string, date-time, nullable, required
          - `last_date_notified_of_custom_routines` object
        - `break_after_minutes` number
        - `current_focus_mode_finish_time` string, date-time
        - `password_for_settings` string
        - `is_office_mode_activated` boolean
        - `current_activity_sequence_id` string
        - `current_focus_mode_id` string
        - `current_completing_focus_block_id` string
        - `current_activity_id` string
        - `current_activity_assigned_at` string, date-time
        - `last_completed_sequence_id` string
        - `current_completing_sequence_log_id` string
        - `last_completed_sequence_at` string, date-time
        - `last_completed_sequence_started_at` string, date-time
        - `current_sequence_started_at` string, date-time
        - `local_device_settings` UpdateLocalDeviceSettingsDto
          - `MacOS` object
          - `Windows` object
          - `Android` object
          - `iOS` object
          - `Web` WebDeviceSettingsDto
            - `hasEditedSettings` boolean
            - `onboarding` OnboardingDto
        - `user_type` 'STANDARD' | 'ADMIN'
        - `signed_up_via_habit_pack` string
        - `signed_up_via_focus_mode` string
        - `current_sequence_skipped_activities` string[]
        - `timezone` string
        - `iana_timezone` string, nullable: The user's explicitly declared IANA zone (for example `Australia/Melbourne`), or null when unset. Distinct from `timezone`, which is a device-synced UTC offset continuously overwritten by client settings sync. Written only by PATCH /user/timezone.
        - `has_edited_settings` boolean
        - `cutoff_time_for_non_high_priority_activities` string
        - `metadata` UserMetadata
          - `profile_image` string
          - `description` string
          - `name` string
          - `last_email_sent` string
          - `email_preferences` object
        - `user_job_details` string
        - `user_typical_distractions` string
        - `habit_theme` string, nullable: The habit theme chosen during onboarding (e.g. "Elmo"), reused for routine notifications (#2617).
        - `themed_routine_notification_titles` object, nullable: Written once when the theme is saved, so sending a routine notification never calls the AI (#2617).
        - `onboarding_progress` UserOnboardingProgress
          - `level` number
          - `has_edited_focus_mode` boolean
          - `has_edited_settings` boolean
          - `has_edited_always_blocked_urls` boolean
          - `has_installed_desktop_app` boolean
          - `has_installed_mobile_app` boolean
          - `has_chatted_with_focus_bear` boolean
        - `onboarding_completed_at` string, date-time, nullable
        - `last_time_stats_updated` string, date-time
        - `last_top_score_refreshed_for_date` string, nullable: @brief Last user-local date for which `to_do.top_score` has been refreshed. @details Stored as a SQL `date` (no time component) holding the user's local calendar date at the time of the last refresh. The off-thread top-score cron compares this against the user's current local "today" (computed via the stored `timezone`) and enqueues a recompute job when the local clock has crossed midnight. Updated by the consumer on successful refresh.
        - `last_completed_focus_mode_at` string, date-time
        - `morning_routines_streak` number
        - `evening_routines_streak` number
        - `focus_modes_streak` number
        - `micro_breaks_streak` number
        - `morning_percent_number_day_of_stats_completed` number
        - `evening_percent_number_day_of_stats_completed` number
        - `micro_percent_number_day_of_stats_completed` number
        - `morning_number_days_completed` number
        - `morning_num_days_of_stats` number
        - `evening_number_days_completed` number
        - `evening_num_days_of_stats` number
        - `micro_breaks_number_days_completed` number
        - `micro_breaks_num_days_of_stats` number
        - `focus_modes_number_days_completed` number
        - `focus_modes_num_days_of_stats` number
        - `num_days_of_stats` number
        - `number_days_completed` number
        - `has_consented_to_terms_of_service` boolean
        - `has_consented_to_privacy_policy` boolean
        - `long_term_goals` string[]
        - `verbose_logging` boolean
        - `last_time_user_settings_modified` string, date-time
        - `username` string
        - `has_received_inactivity_warning` boolean
        - `language` string
        - `email_frequency` 'daily' | 'weekly' | 'monthly' | 'unsubscribed'
        - `feature_flags` string[]
        - `revenue_cat_data` SubscriptionStatus
          - `hasActiveSubscription` boolean, required
          - `activeEntitlements` string[], required
          - `expirations` object
          - `entitlements` object
          - `subscriptions` object
        - `revenue_cat_status` string
        - `last_date_revenue_cat_data_synced` string, date-time
        - `last_status_synced_with_profitwell` string
        - `last_date_gave_feedback` string, date-time
        - `is_relax_activity_generated` boolean
        - `is_internal_tester` boolean: Set to true when the mobile app reports this install as an internal tester build (see mobile-app#4848). Write-once: only ever flips false -> true so a later production reinstall never demotes a known staff account. Used to keep internal testers out of analytics cohorts even when they use real emails.
        - `internal_tester_source` 'build_flag' | 'ios_testflight' | 'android_non_play', nullable
        - `synced_projects` SyncedProject[]
          - `user_id` string
          - `platform` string
          - `external_project_id` string
          - `external_portal_id` string
          - `available_statuses` ExternalTaskStatus[]
            - `label` string, required
            - `status_id` string, required
            - `should_complete_task` boolean, required
          - `have_tasks_been_synced` boolean
          - `name` string
          - `synced_at` string, date-time
          - `user` User (recursive)
          - `to_dos` ToDo[]
            - `user_id` string
            - `focus_type` string
            - `synced_project_id` string
            - `project_id` string
            - `assignee_id` string
            - `assigned_mcp_token_id` string
            - `custom_status_id` string
            - `title` string, required
            - `details` string, required
            - `objective` string, required
            - `external_task_id` string
            - `external_task_metadata` object
            - `subtasks` SubtaskDto[]
              - …
            - `due_date` string, date-time, nullable
            - `eisenhower_quadrant` number, required
            - `outcome` number, required
            - `perspiration_level` number, required
            - `status` string, required
            - `duration` number
            - `icon` string
            - `top_score` number, nullable: @brief Materialised priority score, written by application code. @details Mirrors the SQL expression that previously computed the "top score" inside `ORDER BY` on every page load. Persisting the value allows the planner to satisfy `ORDER BY ... LIMIT` with the `(user_id, top_score DESC)` index instead of a parallel sequential scan + in-memory sort. The score depends on the user's local "today", so it is refreshed daily by the off-thread top-score worker as well as on every write that touches `due_date`, `outcome`, or `perspiration_level`. PostgreSQL's `numeric` type round-trips through node-postgres as a string; the transformer below converts it to `number` on read so the application can treat it as a plain numeric value.
            - `current_external_status_id` string, nullable: @brief Materialised external status identifier from the linked platform. @details Populated by the integration sync worker so list-view rendering no longer needs to decrypt/parse the `external_task_metadata` JSONB on the request path.
            - `current_external_status_label` string, nullable: @brief Materialised external status label from the linked platform. @details See `current_external_status_id`. The label is exposed via the API as the human-readable status text.
            - `planned_date` string, nullable: @brief Day Plan pointer: the day this todo is planned for (YYYY-MM-DD). @details NULL means the todo is not in any Day Plan. A planned task is a pointer into `to_do` by date, not a copy; the Day Plan panel reads todos by `planned_date` and writes planning changes through `PATCH /to-do/:id`.
            - `planned_block_key` string, nullable: @brief Day Plan time-box assignment within the planned day. @details Opaque, never parsed by the backend. One of `event:<id>`, `deepwork:<unix-epoch>`, `habit:<title>`, or NULL when planned but not yet time-boxed onto a block. Encrypted at rest because the `habit:<title>` form can embed a habit name, matching `title`/ `details`. The column stays unbounded `varchar` (ciphertext is longer than plaintext); the DTO caps plaintext input at 256 chars.
            - `planned_order` number, nullable: @brief Manual ordering within the planned day (drag-to-reorder).
            - `recurrence_rule` string, nullable: @brief Recurrence: canonical RFC 5545 RRULE content line, e.g. `RRULE:FREQ=WEEKLY;INTERVAL=2` for a fortnightly chore. @details NULL means the todo is a one-off and takes no recurrence code path, which is every todo that existed before this column. Set together with the other three `recurrence_*` columns (a DB CHECK enforces all-or-none). Validated and canonicalised through the same RFC 5545 subset the Day Plan's event series use.
            - `recurrence_timezone` string, nullable: @brief Recurrence: IANA zone the rule is evaluated in (keeps the local time of day across DST).
            - `recurrence_dtstart` string, date-time, nullable: @brief Recurrence: the series DTSTART — the first occurrence's due date. @details Copied unchanged onto every occurrence so the next due date is always computed from the series anchor. Completing an occurrence late therefore does not shift the schedule.
            - `recurrence_series_id` string, nullable: @brief Recurrence: groups every occurrence of one repeating todo. @details Equal to the first occurrence's own id. Lets history be read per series and `DELETE /to-do?scope=series` remove the whole thing.
            - `user` User (recursive)
            - `synced_project` SyncedProject (recursive)
            - `project` Project
              - …
            - `assignee` User (recursive)
            - `focus_mode` FocusMode (recursive)
            - `task_time_logs` TaskTimeLog[]
              - …
            - `tags` FocusModeTag[]
              - …
            - `completedFocusBlocks` CompletedFocusBlock[], required
              - …
            - `attachments` TaskAttachment[]
              - …
            - `attachment_count` number
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `consents` UserConsent[]
          - `user_id` string
          - `consent_type` 'event_tracking' | 'privacy_policy' | 'terms_of_service' | 'data_processing' | 'marketing_emails', required
          - `consent_status` boolean
          - `withdrawal_date` string, date-time
          - `metadata` UserConsentMetadata
            - `policy_version` string: The policy version the client showed the user when they gave this consent.
          - `policy_version` string, nullable
          - `consented_at` string, date-time, nullable
          - `user` User (recursive)
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `activity_sequences` ActivitySequence[]
          - `user_id` string
          - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
          - `activity_ids` string[]
          - `generated_sequence_activity_ids` string[]
          - `total_duration_seconds` number
          - `generated_total_duration_seconds` number
          - `pack_id` string
          - `custom_routine_id` string
          - `user` User (recursive)
          - `habit_pack` HabitPack
            - `user_id` string
            - `pack_type` 'routine' | 'standalone'
            - `pack_name` string
            - `creator_name` string
            - `description` string
            - `description_video_url` string
            - `welcome_message` string
            - `welcome_video_url` string
            - `description_plain_text` string
            - `welcome_message_plain_text` string
            - `marketplace_approval_status` boolean
            - `marketplace_request` 'requested' | 'unrequested'
            - `is_featured` boolean
            - `featured_for_onboarding` boolean
            - `language` string
            - `morning_routine_duration_seconds` number
            - `evening_routine_duration_seconds` number
            - `duration` number
            - `breaks_only` boolean
            - `deleted_at` string, date-time
            - `installs` InstalledPack[]
              - …
            - `activity_sequences` ActivitySequence[]
            - `activity_templates` ActivityTemplate[]
              - …
            - `signed_up_users` User[]
            - `user` User (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `activities` Activity[]
            - `user_id` string
            - `parent_id` string
            - `linked_activity_id` string
            - `activity_template_id` string
            - `has_choices` boolean
            - `activity_sequence_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `log_summary_type` string
            - `log_quantity` boolean
            - `duration_seconds` number
            - `completion_requirements` string
            - `activity_data` ActivityData
              - …
            - `is_default` boolean
            - `run_micro_breaks` boolean
            - `days_of_week` string[]
            - `check_list` string[]
            - `impact_category` 'hours_of_sleep' | 'energy_level_upon_awakening' | 'mood' | 'perception_of_productivity' | 'minutes_spent_on_distracting_websites' | 'minutes_spent_postponing_app_blocks' | 'minutes_spent_postponing_habits'
            - `cutoff_time_for_doing_activity` string
            - `geofence_id` string
            - `is_deleted` boolean
            - `geofence` Geofence
              - …
            - `activity_sequence` ActivitySequence (recursive)
            - `completed_activities` CompletedActivity[]
              - …
            - `parent_activity` ActivitySequence (recursive)
            - `linked_activity` ActivitySequence (recursive)
            - `linked_activities` Activity[]
            - `choices` Activity[]
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `user` User (recursive)
            - `user_activities` User (recursive)
            - `activity_template` ActivityTemplate
              - …
            - `tutorial` Tutorial
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_activities` CompletedActivity[]
            - `user_id` string
            - `activity_id` string
            - `activity_sequence_id` string
            - `start_time` string, date-time
            - `finish_time` string, date-time
            - `quantity_logged` number
            - `duration_logged` number
            - `completed_sequence_id` string
            - `activity_note` string
            - `metadata` CompletedActivityMetadata
            - `user` User (recursive)
            - `activity` Activity
              - …
            - `activity_sequence` ActivitySequence (recursive)
            - `completed_sequence_log` CompletedActivitySequence
              - …
            - `answers` LogQuantityAnswer[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_activity_sequences` CompletedActivitySequence[]
            - `user_id` string
            - `activity_sequence_id` string
            - `start_time` string, date-time
            - `ownership_window_start` string, date-time
            - `finish_time` string, date-time
            - `duration_minutes` number
            - `plan_duration_minutes` number
            - `is_completed` boolean
            - `was_force_completed` boolean
            - `duration_percent_deviation` number
            - `user` User (recursive)
            - `activity_sequence` ActivitySequence (recursive)
            - `completed_activity_logs` CompletedActivity[]
              - …
            - `completed_morning_sequence` DailyStats
              - …
            - `completed_evening_sequence` DailyStats
              - …
            - `completed_break_sequence` DailyStats, required
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `custom_routine` CustomRoutine
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `completed_activities` CompletedActivity[]
          - `user_id` string
          - `activity_id` string
          - `activity_sequence_id` string
          - `start_time` string, date-time
          - `finish_time` string, date-time
          - `quantity_logged` number
          - `duration_logged` number
          - `completed_sequence_id` string
          - `activity_note` string
          - `metadata` CompletedActivityMetadata
          - `user` User (recursive)
          - `activity` Activity
            - `user_id` string
            - `parent_id` string
            - `linked_activity_id` string
            - `activity_template_id` string
            - `has_choices` boolean
            - `activity_sequence_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `log_summary_type` string
            - `log_quantity` boolean
            - `duration_seconds` number
            - `completion_requirements` string
            - `activity_data` ActivityData
              - …
            - `is_default` boolean
            - `run_micro_breaks` boolean
            - `days_of_week` string[]
            - `check_list` string[]
            - `impact_category` 'hours_of_sleep' | 'energy_level_upon_awakening' | 'mood' | 'perception_of_productivity' | 'minutes_spent_on_distracting_websites' | 'minutes_spent_postponing_app_blocks' | 'minutes_spent_postponing_habits'
            - `cutoff_time_for_doing_activity` string
            - `geofence_id` string
            - `is_deleted` boolean
            - `geofence` Geofence
              - …
            - `activity_sequence` ActivitySequence
              - …
            - `completed_activities` CompletedActivity[]
            - `parent_activity` ActivitySequence
              - …
            - `linked_activity` ActivitySequence
              - …
            - `linked_activities` Activity[]
            - `choices` Activity[]
            - `log_quantity_questions` LogQuantityQuestion[]
              - …
            - `user` User (recursive)
            - `user_activities` User (recursive)
            - `activity_template` ActivityTemplate
              - …
            - `tutorial` Tutorial
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `activity_sequence` ActivitySequence
            - `user_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `activity_ids` string[]
            - `generated_sequence_activity_ids` string[]
            - `total_duration_seconds` number
            - `generated_total_duration_seconds` number
            - `pack_id` string
            - `custom_routine_id` string
            - `user` User (recursive)
            - `habit_pack` HabitPack
              - …
            - `activities` Activity[]
              - …
            - `completed_activities` CompletedActivity[]
            - `completed_activity_sequences` CompletedActivitySequence[]
              - …
            - `custom_routine` CustomRoutine
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_sequence_log` CompletedActivitySequence
            - `user_id` string
            - `activity_sequence_id` string
            - `start_time` string, date-time
            - `ownership_window_start` string, date-time
            - `finish_time` string, date-time
            - `duration_minutes` number
            - `plan_duration_minutes` number
            - `is_completed` boolean
            - `was_force_completed` boolean
            - `duration_percent_deviation` number
            - `user` User (recursive)
            - `activity_sequence` ActivitySequence
              - …
            - `completed_activity_logs` CompletedActivity[]
            - `completed_morning_sequence` DailyStats
              - …
            - `completed_evening_sequence` DailyStats
              - …
            - `completed_break_sequence` DailyStats, required
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `answers` LogQuantityAnswer[]
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `completed_activity_sequences` CompletedActivitySequence[]
          - `user_id` string
          - `activity_sequence_id` string
          - `start_time` string, date-time
          - `ownership_window_start` string, date-time
          - `finish_time` string, date-time
          - `duration_minutes` number
          - `plan_duration_minutes` number
          - `is_completed` boolean
          - `was_force_completed` boolean
          - `duration_percent_deviation` number
          - `user` User (recursive)
          - `activity_sequence` ActivitySequence
            - `user_id` string
            - `type` 'breaking' | 'morning' | 'evening' | 'standalone' | 'library'
            - `activity_ids` string[]
            - `generated_sequence_activity_ids` string[]
            - `total_duration_seconds` number
            - `generated_total_duration_seconds` number
            - `pack_id` string
            - `custom_routine_id` string
            - `user` User (recursive)
            - `habit_pack` HabitPack
              - …
            - `activities` Activity[]
              - …
            - `completed_activities` CompletedActivity[]
              - …
            - `completed_activity_sequences` CompletedActivitySequence[]
            - `custom_routine` CustomRoutine
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_activity_logs` CompletedActivity[]
            - `user_id` string
            - `activity_id` string
            - `activity_sequence_id` string
            - `start_time` string, date-time
            - `finish_time` string, date-time
            - `quantity_logged` number
            - `duration_logged` number
            - `completed_sequence_id` string
            - `activity_note` string
            - `metadata` CompletedActivityMetadata
            - `user` User (recursive)
            - `activity` Activity
              - …
            - `activity_sequence` ActivitySequence
              - …
            - `completed_sequence_log` CompletedActivitySequence (recursive)
            - `answers` LogQuantityAnswer[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_morning_sequence` DailyStats
            - `user_id` string
            - `date_completed` string, date-time
            - `focus_modes_completed` number
            - `morning_routine_completion_percentage` number
            - `evening_routine_completion_percentage` number
            - `micro_breaks_routine_completion_percentage` number
            - `should_recalculate` boolean
            - `morning_sequence_log_id` string
            - `evening_sequence_log_id` string
            - `number_of_distractions_blocked` number
            - `seconds_spent_doing_breaks` number
            - `seconds_spent_in_focus_sessions` number
            - `break_sequence_log_id` string
            - `morning_sequence_log` CompletedActivitySequence (recursive)
            - `evening_sequence_log` CompletedActivitySequence (recursive)
            - `user` User (recursive)
            - `break_sequence_log` CompletedActivitySequence (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_evening_sequence` DailyStats
            - `user_id` string
            - `date_completed` string, date-time
            - `focus_modes_completed` number
            - `morning_routine_completion_percentage` number
            - `evening_routine_completion_percentage` number
            - `micro_breaks_routine_completion_percentage` number
            - `should_recalculate` boolean
            - `morning_sequence_log_id` string
            - `evening_sequence_log_id` string
            - `number_of_distractions_blocked` number
            - `seconds_spent_doing_breaks` number
            - `seconds_spent_in_focus_sessions` number
            - `break_sequence_log_id` string
            - `morning_sequence_log` CompletedActivitySequence (recursive)
            - `evening_sequence_log` CompletedActivitySequence (recursive)
            - `user` User (recursive)
            - `break_sequence_log` CompletedActivitySequence (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `completed_break_sequence` DailyStats, required
            - `user_id` string
            - `date_completed` string, date-time
            - `focus_modes_completed` number
            - `morning_routine_completion_percentage` number
            - `evening_routine_completion_percentage` number
            - `micro_breaks_routine_completion_percentage` number
            - `should_recalculate` boolean
            - `morning_sequence_log_id` string
            - `evening_sequence_log_id` string
            - `number_of_distractions_blocked` number
            - `seconds_spent_doing_breaks` number
            - `seconds_spent_in_focus_sessions` number
            - `break_sequence_log_id` string
            - `morning_sequence_log` CompletedActivitySequence (recursive)
            - `evening_sequence_log` CompletedActivitySequence (recursive)
            - `user` User (recursive)
            - `break_sequence_log` CompletedActivitySequence (recursive)
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `completed_focus_blocks` CompletedFocusBlock[]
          - `user_id` string
          - `focus_mode_id` string
          - `start_time` string, date-time
          - `finish_time` string, date-time
          - `scheduled_finish_time` string, date-time
          - `status` 'active' | 'paused' | 'completed'
          - `paused_at` string, date-time
          - `total_paused_seconds` number
          - `intention` string
          - `achievements` string
          - `distractions` string
          - `focus_duration_seconds` number
          - `metadata` object
          - `user` User (recursive)
          - `focus_mode` FocusMode (recursive)
          - `tags` FocusModeTag[]
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `to_dos` ToDo[]
            - `user_id` string
            - `focus_type` string
            - `synced_project_id` string
            - `project_id` string
            - `assignee_id` string
            - `assigned_mcp_token_id` string
            - `custom_status_id` string
            - `title` string, required
            - `details` string, required
            - `objective` string, required
            - `external_task_id` string
            - `external_task_metadata` object
            - `subtasks` SubtaskDto[]
              - …
            - `due_date` string, date-time, nullable
            - `eisenhower_quadrant` number, required
            - `outcome` number, required
            - `perspiration_level` number, required
            - `status` string, required
            - `duration` number
            - `icon` string
            - `top_score` number, nullable: @brief Materialised priority score, written by application code. @details Mirrors the SQL expression that previously computed the "top score" inside `ORDER BY` on every page load. Persisting the value allows the planner to satisfy `ORDER BY ... LIMIT` with the `(user_id, top_score DESC)` index instead of a parallel sequential scan + in-memory sort. The score depends on the user's local "today", so it is refreshed daily by the off-thread top-score worker as well as on every write that touches `due_date`, `outcome`, or `perspiration_level`. PostgreSQL's `numeric` type round-trips through node-postgres as a string; the transformer below converts it to `number` on read so the application can treat it as a plain numeric value.
            - `current_external_status_id` string, nullable: @brief Materialised external status identifier from the linked platform. @details Populated by the integration sync worker so list-view rendering no longer needs to decrypt/parse the `external_task_metadata` JSONB on the request path.
            - `current_external_status_label` string, nullable: @brief Materialised external status label from the linked platform. @details See `current_external_status_id`. The label is exposed via the API as the human-readable status text.
            - `planned_date` string, nullable: @brief Day Plan pointer: the day this todo is planned for (YYYY-MM-DD). @details NULL means the todo is not in any Day Plan. A planned task is a pointer into `to_do` by date, not a copy; the Day Plan panel reads todos by `planned_date` and writes planning changes through `PATCH /to-do/:id`.
            - `planned_block_key` string, nullable: @brief Day Plan time-box assignment within the planned day. @details Opaque, never parsed by the backend. One of `event:<id>`, `deepwork:<unix-epoch>`, `habit:<title>`, or NULL when planned but not yet time-boxed onto a block. Encrypted at rest because the `habit:<title>` form can embed a habit name, matching `title`/ `details`. The column stays unbounded `varchar` (ciphertext is longer than plaintext); the DTO caps plaintext input at 256 chars.
            - `planned_order` number, nullable: @brief Manual ordering within the planned day (drag-to-reorder).
            - `recurrence_rule` string, nullable: @brief Recurrence: canonical RFC 5545 RRULE content line, e.g. `RRULE:FREQ=WEEKLY;INTERVAL=2` for a fortnightly chore. @details NULL means the todo is a one-off and takes no recurrence code path, which is every todo that existed before this column. Set together with the other three `recurrence_*` columns (a DB CHECK enforces all-or-none). Validated and canonicalised through the same RFC 5545 subset the Day Plan's event series use.
            - `recurrence_timezone` string, nullable: @brief Recurrence: IANA zone the rule is evaluated in (keeps the local time of day across DST).
            - `recurrence_dtstart` string, date-time, nullable: @brief Recurrence: the series DTSTART — the first occurrence's due date. @details Copied unchanged onto every occurrence so the next due date is always computed from the series anchor. Completing an occurrence late therefore does not shift the schedule.
            - `recurrence_series_id` string, nullable: @brief Recurrence: groups every occurrence of one repeating todo. @details Equal to the first occurrence's own id. Lets history be read per series and `DELETE /to-do?scope=series` remove the whole thing.
            - `user` User (recursive)
            - `synced_project` SyncedProject
              - …
            - `project` Project
              - …
            - `assignee` User (recursive)
            - `focus_mode` FocusMode (recursive)
            - `task_time_logs` TaskTimeLog[]
              - …
            - `tags` FocusModeTag[]
              - …
            - `completedFocusBlocks` CompletedFocusBlock[], required
            - `attachments` TaskAttachment[]
              - …
            - `attachment_count` number
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `block_level` 'gentle' | 'strict' | 'super-strict', nullable
          - `version` number
          - `class_session_id` string, nullable
          - `project_id` string, nullable
          - `project` Project
            - `owner_id` string, required
            - `name` string, required
            - `description` string
            - `custom_statuses` object[]
              - …
            - `icon` string
            - `due_date` string, date-time, nullable
            - `due_date_is_date_only` boolean, required
            - `importance` number, required
            - `outcome` number, nullable
            - `status` 'active' | 'completed' | 'archived', required
            - `metadata` object, nullable: Open jsonb bag for per-project client data; `tags` is the one reserved key (validated, normalized lower-case, GIN-indexed). Deliberately NOT encrypted, unlike `focus_modes.metadata` — an encrypted value could not be indexed or queried, which is the whole point of storing tags here.
            - `deleted_at` string, date-time
            - `owner` User (recursive)
            - `members` ProjectMember[]
              - …
            - `tasks` ToDo[]
              - …
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `devices` Device[]
          - `user_id` string
          - `operating_system` 'MacOS' | 'Windows' | 'Android' | 'iOS' | 'Web' | 'Unknown'
          - `is_leader` boolean
          - `app_version` string
          - `metadata` DeviceMetadata
          - `user` User (recursive)
          - `id` string
          - `created_at` string
          - `updated_at` string
        - `to_dos` ToDo[]
          - `user_id` string
          - `focus_type` string
          - `synced_project_id` string
          - `project_id` string
          - `assignee_id` string
          - `assigned_mcp_token_id` string
          - `custom_status_id` string
          - `title` string, required
          - `details` string, required
          - `objective` string, required
          - `external_task_id` string
          - `external_task_metadata` object
          - `subtasks` SubtaskDto[]
            - `id` string
            - `name` string, required
            - `is_completed` boolean, required
            - `trello_check_item_id` string
          - `due_date` string, date-time, nullable
          - `eisenhower_quadrant` number, required
          - `outcome` number, required
          - `perspiration_level` number, required
          - `status` string, required
          - `duration` number
          - `icon` string
          - `top_score` number, nullable: @brief Materialised priority score, written by application code. @details Mirrors the SQL expression that previously computed the "top score" inside `ORDER BY` on every page load. Persisting the value allows the planner to satisfy `ORDER BY ... LIMIT` with the `(user_id, top_score DESC)` index instead of a parallel sequential scan + in-memory sort. The score depends on the user's local "today", so it is refreshed daily by the off-thread top-score worker as well as on every write that touches `due_date`, `outcome`, or `perspiration_level`. PostgreSQL's `numeric` type round-trips through node-postgres as a string; the transformer below converts it to `number` on read so the application can treat it as a plain numeric value.
          - `current_external_status_id` string, nullable: @brief Materialised external status identifier from the linked platform. @details Populated by the integration sync worker so list-view rendering no longer needs to decrypt/parse the `external_task_metadata` JSONB on the request path.
          - `current_external_status_label` string, nullable: @brief Materialised external status label from the linked platform. @details See `current_external_status_id`. The label is exposed via the API as the human-readable status text.
          - `planned_date` string, nullable: @brief Day Plan pointer: the day this todo is planned for (YYYY-MM-DD). @details NULL means the todo is not in any Day Plan. A planned task is a pointer into `to_do` by date, not a copy; the Day Plan panel reads todos by `planned_date` and writes planning changes through `PATCH /to-do/:id`.
          - `planned_block_key` string, nullable: @brief Day Plan time-box assignment within the planned day. @details Opaque, never parsed by the backend. One of `event:<id>`, `deepwork:<unix-epoch>`, `habit:<title>`, or NULL when planned but not yet time-boxed onto a block. Encrypted at rest because the `habit:<title>` form can embed a habit name, matching `title`/ `details`. The column stays unbounded `varchar` (ciphertext is longer than plaintext); the DTO caps plaintext input at 256 chars.
          - `planned_order` number, nullable: @brief Manual ordering within the planned day (drag-to-reorder).
          - `recurrence_rule` string, nullable: @brief Recurrence: canonical RFC 5545 RRULE content line, e.g. `RRULE:FREQ=WEEKLY;INTERVAL=2` for a fortnightly chore. @details NULL means the todo is a one-off and takes no recurrence code path, which is every todo that existed before this column. Set together with the other three `recurrence_*` columns (a DB CHECK enforces all-or-none). Validated and canonicalised through the same RFC 5545 subset the Day Plan's event series use.
          - `recurrence_timezone` string, nullable: @brief Recurrence: IANA zone the rule is evaluated in (keeps the local time of day across DST).
          - `recurrence_dtstart` string, date-time, nullable: @brief Recurrence: the series DTSTART — the first occurrence's due date. @details Copied unchanged onto every occurrence so the next due date is always computed from the series anchor. Completing an occurrence late therefore does not shift the schedule.
          - `recurrence_series_id` string, nullable: @brief Recurrence: groups every occurrence of one repeating todo. @details Equal to the first occurrence's own id. Lets history be read per series and `DELETE /to-do?scope=series` remove the whole thing.
          - `user` User (recursive)
          - `synced_project` SyncedProject
            - `user_id` string
            - `platform` string
            - `external_project_id` string
            - `external_portal_id` string
            - `available_statuses` ExternalTaskStatus[]
              - …
            - `have_tasks_been_synced` boolean
            - `name` string
            - `synced_at` string, date-time
            - `user` User (recursive)
            - `to_dos` ToDo[]
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `project` Project
            - `owner_id` string, required
            - `name` string, required
            - `description` string
            - `custom_statuses` object[]
              - …
            - `icon` string
            - `due_date` string, date-time, nullable
            - `due_date_is_date_only` boolean, required
            - `importance` number, required
            - `outcome` number, nullable
            - `status` 'active' | 'completed' | 'archived', required
            - `metadata` object, nullable: Open jsonb bag for per-project client data; `tags` is the one reserved key (validated, normalized lower-case, GIN-indexed). Deliberately NOT encrypted, unlike `focus_modes.metadata` — an encrypted value could not be indexed or queried, which is the whole point of storing tags here.
            - `deleted_at` string, date-time
            - `owner` User (recursive)
            - `members` ProjectMember[]
              - …
            - `tasks` ToDo[]
            - `id` string
            - `created_at` string
            - `updated_at` string
          - `assignee` User (recursive)
          - `focus_mode` FocusMode (recursive)
          - `task_time_logs` TaskTimeLog[]
            - `user_id` string
- … truncated; see the full OpenAPI document linked below

---

[API](https://skmtc.dev/focusbear/apis/focusbear-api.md) - [All operations](https://skmtc.dev/focusbear/apis/focusbear-api/llms.txt) - [OpenAPI document](https://skmtc.dev/focusbear/apis/focusbear-api/revisions/3a83a3b9a811?raw)
