Wispr AI Sync WS API

The Sync WS API from Wispr AI — 3 operation(s) for sync ws.

OpenAPI Specification

wispr-ai-sync-ws-api-openapi.yml Raw ↑
openapi: 3.0.3
info:
  title: Wispr Backend Analytics Sync WS API
  description: Wispr Backend API
  version: 0.5.2
tags:
- name: Sync WS
paths:
  /api/v1/sync/ws/admin/test-publish:
    post:
      tags:
      - Sync WS
      summary: Admin Test Publish
      description: 'Synthesize one or more sync events for ``user_id``.


        ``burst > 1`` is the gap-too-large drill: publishing more than the replay

        buffer size (``publisher.REPLAY_MAXLEN``) forces the oldest entries out so

        a reconnecting client with a stale ``since`` falls through to

        ``/sync/check``.'
      operationId: admin_test_publish
      security:
      - ApiKeyHeaderPatched: []
      parameters:
      - name: burst
        in: query
        required: false
        schema:
          type: integer
          maximum: 2000
          minimum: 1
          default: 1
          title: Burst
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdminTestPublishRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminTestPublishResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/v1/sync/ws/schema:
    get:
      tags:
      - Sync WS
      summary: Sync Ws Schema
      description: 'Return a stub envelope. Exists so the ``SyncEvent`` discriminated

        union is reachable from the OpenAPI spec (and thus the generated TS

        client). Admin-gating isn''t required — it returns no user data.'
      operationId: sync_ws_schema
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncEventEnvelope'
  /api/v1/sync/ws/client-schema:
    get:
      tags:
      - Sync WS
      summary: Sync Ws Client Schema
      description: Companion to ``/ws/schema`` for the client-to-server frame union.
      operationId: sync_ws_client_schema
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncClientFrameEnvelope'
components:
  schemas:
    AdminTestPublishOptions:
      properties:
        meeting_id:
          anyOf:
          - type: string
            format: uuid
          - type: 'null'
          title: Meeting Id
      type: object
      title: AdminTestPublishOptions
    SyncEventUnrecognized:
      properties:
        type:
          type: string
          const: unrecognized
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        requested_event_type:
          type: string
          title: Requested Event Type
      type: object
      required:
      - type
      - seq
      - ts
      - requested_event_type
      title: SyncEventUnrecognized
      description: 'Escape hatch for the admin test-publish endpoint.


        Lets the server emit a well-formed envelope whose ``type`` the client

        doesn''t know yet, so the desktop harness can verify the unknown-event-type

        path end-to-end without a server-side branch fabricating a real event.'
    NotificationPlatformType:
      type: string
      enum:
      - desktop
      - ios
      - both
      title: NotificationPlatformType
      description: Client platform(s) a notification targets.
    PolishInstructions:
      properties:
        default:
          additionalProperties:
            type: boolean
          type: object
          title: Default
          default: {}
        custom:
          additionalProperties:
            type: boolean
          type: object
          title: Custom
          default: {}
      type: object
      title: PolishInstructions
      description: Saved toggle rules for Polish transforms (built-in defaults + custom).
    SyncEventMeetingChanged:
      properties:
        type:
          type: string
          const: meeting.changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        meeting_id:
          type: string
          format: uuid
          title: Meeting Id
        refine_status:
          anyOf:
          - $ref: '#/components/schemas/RefineJobStatus'
          - type: 'null'
      type: object
      required:
      - type
      - seq
      - ts
      - meeting_id
      title: SyncEventMeetingChanged
      description: 'A meeting row''s user-visible state changed.


        Thin-ticket — client refetches the meeting row. A resource-level ``changed``

        shape (matching ``notes.changed`` / ``calendar.changed``) is preferred over

        ``ready`` variants that inline a payload:

        the row''s fields can be edited cross-device or regenerated, so an inlined value

        rots the moment it can change again.


        ``refine_status`` is an advisory hint carrying the refine pipeline''s current stage

        (emitted on each transition so a client can render progress); the pulled row stays

        authoritative. Optional-with-default so pre-hint payloads still parse.'
    FlowStateProcessingStatus:
      type: string
      enum:
      - not_started
      - processing
      - complete
      - failed
      title: FlowStateProcessingStatus
    FlowState2025Response:
      properties:
        user_id:
          type: string
          title: User Id
        processing_status:
          $ref: '#/components/schemas/FlowStateProcessingStatus'
        total_words:
          anyOf:
          - type: integer
          - type: 'null'
          title: Total Words
        total_words_percentile:
          anyOf:
          - type: number
          - type: 'null'
          title: Total Words Percentile
        last_active:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Last Active
        total_dictations_desktop:
          anyOf:
          - type: integer
          - type: 'null'
          title: Total Dictations Desktop
        total_dictations_mobile:
          anyOf:
          - type: integer
          - type: 'null'
          title: Total Dictations Mobile
        wpm_desktop:
          anyOf:
          - type: number
          - type: 'null'
          title: Wpm Desktop
        wpm_mobile:
          anyOf:
          - type: number
          - type: 'null'
          title: Wpm Mobile
        longest_streak_days:
          anyOf:
          - type: integer
          - type: 'null'
          title: Longest Streak Days
        top_app:
          anyOf:
          - type: string
          - type: 'null'
          title: Top App
        top_app_day_of_week:
          anyOf:
          - type: string
          - type: 'null'
          title: Top App Day Of Week
        top_app_hour_of_day:
          anyOf:
          - type: integer
          - type: 'null'
          title: Top App Hour Of Day
        top_app_category:
          anyOf:
          - type: string
          - type: 'null'
          title: Top App Category
        secondary_app_category:
          anyOf:
          - type: string
          - type: 'null'
          title: Secondary App Category
        signature_phrase:
          anyOf:
          - type: string
          - type: 'null'
          title: Signature Phrase
        signature_phrase_app:
          anyOf:
          - type: string
          - type: 'null'
          title: Signature Phrase App
        most_used_phrase:
          anyOf:
          - type: string
          - type: 'null'
          title: Most Used Phrase
        dictation_strength:
          anyOf:
          - type: string
          - type: 'null'
          title: Dictation Strength
        alter_ego:
          anyOf:
          - type: string
          - type: 'null'
          title: Alter Ego
        alter_ego_description:
          anyOf:
          - type: string
          - type: 'null'
          title: Alter Ego Description
        avg_session_duration_desktop:
          anyOf:
          - type: number
          - type: 'null'
          title: Avg Session Duration Desktop
        avg_session_duration_mobile:
          anyOf:
          - type: number
          - type: 'null'
          title: Avg Session Duration Mobile
        goals:
          anyOf:
          - items:
              type: string
            type: array
          - type: 'null'
          title: Goals
      type: object
      required:
      - user_id
      - processing_status
      title: FlowState2025Response
    SyncEventReconnectResync:
      properties:
        type:
          type: string
          const: reconnect_resync
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        reason:
          type: string
          title: Reason
      type: object
      required:
      - type
      - seq
      - ts
      - reason
      title: SyncEventReconnectResync
      description: 'Server-initiated reconnect signal.


        Client should drop its last_seq, reconnect, and call ``/sync/check`` to

        bootstrap from snapshot.'
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    UserPreferences:
      properties:
        onboarding_role:
          anyOf:
          - type: string
          - type: 'null'
          title: Onboarding Role
        onboarding_industry:
          anyOf:
          - type: string
          - type: 'null'
          title: Onboarding Industry
        privacy_mode:
          type: boolean
          title: Privacy Mode
          default: false
        share_data:
          type: boolean
          title: Share Data
          default: true
        cloud_sync:
          type: boolean
          title: Cloud Sync
          default: true
        output_languages:
          items:
            type: string
            enum:
            - ''
            - en
            - engb
            - enca
            - zh
            - zhcn
            - de
            - dech
            - es
            - ru
            - ko
            - fr
            - ja
            - pt
            - tr
            - pl
            - ca
            - nl
            - ar
            - sv
            - it
            - id
            - hi
            - hien
            - fi
            - vi
            - he
            - uk
            - el
            - ms
            - cs
            - ro
            - da
            - hu
            - ta
            - 'no'
            - th
            - ur
            - hr
            - bg
            - lt
            - la
            - mi
            - ml
            - cy
            - sk
            - te
            - fa
            - lv
            - bn
            - sr
            - az
            - sl
            - kn
            - et
            - mk
            - br
            - eu
            - is
            - hy
            - ne
            - mn
            - bs
            - kk
            - sq
            - sw
            - gl
            - mr
            - pa
            - si
            - km
            - sn
            - yo
            - so
            - af
            - oc
            - ka
            - be
            - tg
            - sd
            - gu
            - am
            - yi
            - lo
            - uz
            - fo
            - ht
            - ps
            - tk
            - nn
            - mt
            - sa
            - lb
            - my
            - bo
            - tl
            - mg
            - as
            - tt
            - haw
            - ln
            - ha
            - ba
            - jv
            - su
            - yue
          type: array
          title: Output Languages
          default:
          - en
        personalization_styles:
          $ref: '#/components/schemas/PersonalizationStyles'
          default:
            personal: formal
            work: formal
            email: formal
            other: formal
            unset: default
        personalizationOnboardingCompleted:
          type: boolean
          title: Personalizationonboardingcompleted
          default: false
        polish_instructions:
          $ref: '#/components/schemas/PolishInstructions'
          default:
            default: {}
            custom: {}
        auto_cleanup_level:
          anyOf:
          - type: string
            enum:
            - none
            - light
            - medium
            - high
            - full
          - type: 'null'
          title: Auto Cleanup Level
        local_data_policy:
          anyOf:
          - type: string
            enum:
            - store_normally
            - delete_after_24h
            - never_store
          - type: 'null'
          title: Local Data Policy
        notetaker_transcript_retention:
          $ref: '#/components/schemas/NotetakerTranscriptRetention'
          default: never_delete
        fulfilled_intents:
          items:
            $ref: '#/components/schemas/OnboardingIntent'
          type: array
          title: Fulfilled Intents
          default: []
        ui_locale:
          anyOf:
          - type: string
          - type: 'null'
          title: Ui Locale
      type: object
      title: UserPreferences
    AdminTestPublishRequest:
      properties:
        user_id:
          type: string
          title: User Id
        event_type:
          type: string
          enum:
          - heartbeat
          - reconnect_resync
          - gap_too_large
          - notification.created
          - preferences.changed
          - notes.changed
          - meeting.changed
          - meeting.transcript_changed
          - calendar.changed
          title: Event Type
        reason:
          anyOf:
          - type: string
          - type: 'null'
          title: Reason
        options:
          anyOf:
          - $ref: '#/components/schemas/AdminTestPublishOptions'
          - type: 'null'
      type: object
      required:
      - user_id
      - event_type
      title: AdminTestPublishRequest
    SyncClientSubscribe:
      properties:
        type:
          type: string
          const: subscribe
          title: Type
        since:
          anyOf:
          - type: integer
          - type: 'null'
          title: Since
      type: object
      required:
      - type
      title: SyncClientSubscribe
      description: 'First frame after the WS upgrade.


        Establishes the subscription and optionally resumes from ``since`` (the

        client''s last delivered sequence; omitted on a cold start).'
    PersonalizationStyles:
      properties:
        personal:
          $ref: '#/components/schemas/PersonalizationStyle'
          default: formal
        work:
          $ref: '#/components/schemas/PersonalizationStyle'
          default: formal
        email:
          $ref: '#/components/schemas/PersonalizationStyle'
          default: formal
        other:
          $ref: '#/components/schemas/PersonalizationStyle'
          default: formal
        unset:
          $ref: '#/components/schemas/PersonalizationStyle'
          default: default
      type: object
      title: PersonalizationStyles
    SyncEventHeartbeat:
      properties:
        type:
          type: string
          const: heartbeat
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
      type: object
      required:
      - type
      - seq
      - ts
      title: SyncEventHeartbeat
      description: Keepalive frame emitted by the server on a fixed cadence.
    SyncEventEnterpriseMembershipChanged:
      properties:
        type:
          type: string
          const: enterprise.membership_changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        enterprise_id:
          type: string
          title: Enterprise Id
      type: object
      required:
      - type
      - seq
      - ts
      - enterprise_id
      title: SyncEventEnterpriseMembershipChanged
      description: 'An enterprise''s membership roster changed (member added/removed via SCIM or admin action).


        Thin — the client refetches its subscription state through

        ``GET /payment/subscription`` (enterprise role/seat reach the client through

        the billing path; there is no separate enterprise surface on desktop).


        Fanned out to all current members plus the affected/removed user (so a

        removed user''s client drops its enterprise state).'
    SyncClientFrameEnvelope:
      properties:
        sync_frame:
          oneOf:
          - $ref: '#/components/schemas/SyncClientAck'
          - $ref: '#/components/schemas/SyncClientPing'
          - $ref: '#/components/schemas/SyncClientSubscribe'
          title: Sync Frame
          discriminator:
            propertyName: type
            mapping:
              ack: '#/components/schemas/SyncClientAck'
              ping: '#/components/schemas/SyncClientPing'
              subscribe: '#/components/schemas/SyncClientSubscribe'
      type: object
      required:
      - sync_frame
      title: SyncClientFrameEnvelope
      description: Wrapper for the client-frame union — same OpenAPI surfacing trick.
    AdminTestPublishResponse:
      properties:
        published:
          type: integer
          title: Published
        last_entry_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Last Entry Id
      type: object
      required:
      - published
      - last_entry_id
      title: AdminTestPublishResponse
    SyncClientAck:
      properties:
        type:
          type: string
          const: ack
          title: Type
        seq:
          type: integer
          title: Seq
      type: object
      required:
      - type
      - seq
      title: SyncClientAck
      description: 'Client acknowledges receipt up to ``seq``.


        Server uses for stream trimming heuristics.'
    SyncEventSubscriptionChanged:
      properties:
        type:
          type: string
          const: subscription.changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        payload:
          $ref: '#/components/schemas/SubscriptionPayload'
      type: object
      required:
      - type
      - seq
      - ts
      - payload
      title: SyncEventSubscriptionChanged
      description: 'The user''s subscription state changed (Stripe or RevenueCat webhook).


        Fat event — the full rendered ``SubscriptionPayload`` travels on the wire so

        the client applies it directly without a refetch. Built from the primary at

        publish time (read-your-writes), so it is never stale relative to the write

        that triggered it.'
    SyncEventFlowStateJobCompleted:
      properties:
        type:
          type: string
          const: flow_state_job.completed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        payload:
          $ref: '#/components/schemas/FlowState2025Response'
      type: object
      required:
      - type
      - seq
      - ts
      - payload
      title: SyncEventFlowStateJobCompleted
      description: 'A flow-state-2025 background job finished and committed its LLM fields.


        Fat event — the full ``FlowState2025Response`` (same shape the desktop polls

        via ``GET /llm/flow_state_2025``) travels on the wire so the client renders

        immediately without a follow-up REST fetch. Published from the background

        processor''s completion transition (post-commit, read-your-writes).'
    NotificationResponse:
      properties:
        id:
          type: string
          format: uuid
          title: Id
        type:
          $ref: '#/components/schemas/NotificationType'
        key:
          type: string
          title: Key
        title:
          anyOf:
          - type: string
          - type: 'null'
          title: Title
        text:
          anyOf:
          - type: string
          - type: 'null'
          title: Text
        ios_title:
          anyOf:
          - type: string
          - type: 'null'
          title: Ios Title
        ios_text:
          anyOf:
          - type: string
          - type: 'null'
          title: Ios Text
        platform_type:
          $ref: '#/components/schemas/NotificationPlatformType'
        created_at:
          type: string
          format: date-time
          title: Created At
        is_archived:
          type: boolean
          title: Is Archived
      type: object
      required:
      - id
      - type
      - key
      - title
      - text
      - ios_title
      - ios_text
      - platform_type
      - created_at
      - is_archived
      title: NotificationResponse
      description: A single notification as returned to the client.
    RefineJobStatus:
      type: string
      enum:
      - awaiting_audio
      - transcribing
      - summarizing
      - complete
      - failed
      - abandoned
      title: RefineJobStatus
    VoiceProfileProcessingStatus:
      type: string
      enum:
      - NOT_STARTED
      - PROCESSING
      - COMPLETE
      - FAILED
      title: VoiceProfileProcessingStatus
      description: Processing state of a user's voice profile generation.
    SyncEventIncidentBannerUpdated:
      properties:
        type:
          type: string
          const: incident_banner.updated
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        payload:
          anyOf:
          - $ref: '#/components/schemas/IncidentStatusDetail'
          - type: 'null'
      type: object
      required:
      - type
      - seq
      - ts
      title: SyncEventIncidentBannerUpdated
      description: 'The production incident banner changed for ALL users (global fanout).


        Fat event carrying the same ``IncidentStatusDetail`` the bootstrap

        ``/sync/check`` returns. ``payload=None`` means the incident was cleared —

        the client tears down its banner. Fanned out on the single ``sync:global``

        channel every connected client subscribes to, not a per-user channel.'
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    SyncEventMeetingTranscriptChanged:
      properties:
        type:
          type: string
          const: meeting.transcript_changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        meeting_id:
          type: string
          format: uuid
          title: Meeting Id
      type: object
      required:
      - type
      - seq
      - ts
      - meeting_id
      title: SyncEventMeetingTranscriptChanged
      description: 'Refined transcript persisted for ``meeting_id``.


        Thin-ticket — client refetches transcript bytes through the existing inline

        sync path.


        Kept separate from ``meeting.changed`` because transcript bytes are a

        distinct surface (different consumers, different refetch path) from the

        meeting row''s columns.'
    SyncEventVoiceProfileReady:
      properties:
        type:
          type: string
          const: voice_profile.ready
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        payload:
          $ref: '#/components/schemas/VoiceProfileResponse'
      type: object
      required:
      - type
      - seq
      - ts
      - payload
      title: SyncEventVoiceProfileReady
      description: 'A voice-profile background job finished and committed a profile row.


        Fat event — the full ``VoiceProfileResponse`` (same shape the desktop polls

        via ``GET /llm/voice_profile``) travels on the wire so the client renders

        immediately without a follow-up REST fetch. Published from the background

        processor''s completion transition (post-commit, read-your-writes).'
    NotificationType:
      type: string
      enum:
      - achievement
      - referral
      - announcement
      title: NotificationType
      description: Category of a user notification.
    SyncEventNotificationCreated:
      properties:
        type:
          type: string
          const: notification.created
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        payload:
          $ref: '#/components/schemas/NotificationResponse'
      type: object
      required:
      - type
      - seq
      - ts
      - payload
      title: SyncEventNotificationCreated
      description: 'A new notification has been created for the user.


        Inline (fat) event: the entire ``NotificationResponse`` travels on the wire

        so the client can render a toast immediately without a follow-up REST fetch.'
    SyncClientPing:
      properties:
        type:
          type: string
          const: ping
          title: Type
        ts:
          type: string
          format: date-time
          title: Ts
      type: object
      required:
      - type
      - ts
      title: SyncClientPing
      description: Client liveness probe; server replies with the next heartbeat.
    SyncEventNotesChanged:
      properties:
        type:
          type: string
          const: notes.changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
      type: object
      required:
      - type
      - seq
      - ts
      title: SyncEventNotesChanged
      description: 'Notes set changed.


        Thin-ticket — client refetches via the existing notes sync flow.'
    VoiceProfileResponse:
      properties:
        processing_status:
          $ref: '#/components/schemas/VoiceProfileProcessingStatus'
        headline:
          anyOf:
          - type: string
          - type: 'null'
          title: Headline
        superpower_title:
          anyOf:
          - type: string
          - type: 'null'
          title: Superpower Title
        superpower_description:
          anyOf:
          - type: string
          - type: 'null'
          title: Superpower Description
        peak_time_label:
          anyOf:
          - type: string
          - type: 'null'
          title: Peak Time Label
        peak_time_description:
          anyOf:
          - type: string
          - type: 'null'
          title: Peak Time Description
        peak_time_top_app:
          anyOf:
          - type: string
          - type: 'null'
          title: Peak Time Top App
        catch_phrase:
          anyOf:
          - type: string
          - type: 'null'
          title: Catch Phrase
        catch_phrase_description:
          anyOf:
          - type: string
          - type: 'null'
          title: Catch Phrase Description
        persona:
          anyOf:
          - type: string
            enum:
            - Analysis
            - Brainstorm
            - Coding
            - Collaboration
            - Create
            - Deep thought
            - Dreaming
            - Educate
            - Finance
            - Healthcare & Life Sciences
            - Idea generation
            - Leadership
            - Long writing
            - Manage
            - Multi-tasking
            - Multilingual
            - On the go
            - Organize
            - Presenting
            - Reporting
            - Support
            - Thinking
          - type: string
          - type: 'null'
          title: Persona
        most_used_word:
          anyOf:
          - type: string
          - type: 'null'
          title: Most Used Word
        most_removed_word:
          anyOf:
          - type: string
          - type: 'null'
          title: Most Removed Word
        word_count_milestone:
          anyOf:
          - type: integer
            enum:
            - 2000
            - 12000
            - 37000
            - 87000
            - 162000
            - 262000
            - 362000
            - 462000
            - 562000
            - 662000
          - type: 'null'
          title: Word Count Milestone
        created_at:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Created At
      type: object
      required:
      - processing_status
      title: VoiceProfileResponse
      description: The generated voice profile and its processing status for a user.
    SyncEventTodosChanged:
      properties:
        type:
          type: string
          const: todos.changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
      type: object
      required:
      - type
      - seq
      - ts
      title: SyncEventTodosChanged
      description: 'Todos set changed.


        Thin-ticket — client refetches via the existing ``/todos/sync`` flow.

        Coarse (no ``todo_id``): the client re-pulls the whole resource, and a

        per-row event would mint unbounded Redis keys (the 2026-06-01 OOM).'
    SyncEventPreferencesChanged:
      properties:
        type:
          type: string
          const: preferences.changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
        payload:
          $ref: '#/components/schemas/PreferencesResponse'
      type: object
      required:
      - type
      - seq
      - ts
      - payload
      title: SyncEventPreferencesChanged
      description: 'User preferences updated.


        Inline (fat) — full ``PreferencesResponse`` travels on the wire so peers

        can converge via last-writer-wins on the ``modified_at`` echo.'
    SyncEventCalendarChanged:
      properties:
        type:
          type: string
          const: calendar.changed
          title: Type
        seq:
          type: integer
          title: Seq
        ts:
          type: string
          format: date-time
          title: Ts
      type: object
      required:
      - type
      - seq
      - ts
      title: SyncEventCalendarChanged
      description: 'Calendar state changed.


        Thin-ticket — client refetches the calendar list.'
    NotetakerTranscriptRetention:
      type: string
      enum:
      - never_delete
      - 1_day
      - 7_days
      - 30_days
      - 90_days
      - 180_days
      - 365_days
      title: NotetakerTranscriptRetention
    IncidentStatusDetail:
      properties:
        state:
          type: string
          enum:
          - degraded
          - outage
          title: State
        message:
          type: string
          title: Message
        affected_regions:
          items:
            type: string
            enum:
            - us
            - europe
            - apac
          type: array
          title: Affected Regions
        started_at:
          type: string
          format: date-time
          title: Started At
   

# --- truncated at 32 KB (42 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/wispr-ai/refs/heads/main/openapi/wispr-ai-sync-ws-api-openapi.yml