Wispr AI Calendar API

The Calendar API from Wispr AI — 6 operation(s) for calendar.

OpenAPI Specification

wispr-ai-calendar-api-openapi.yml Raw ↑
openapi: 3.0.3
info:
  title: Wispr Backend Analytics Calendar API
  description: Wispr Backend API
  version: 0.5.2
tags:
- name: Calendar
paths:
  /api/v1/calendar/events/batch:
    get:
      tags:
      - Calendar
      summary: Get Events Batch
      description: 'Return the authenticated user''s calendar events matching the given externalIds.


        Designed for the desktop to re-fetch events linked to past notes whose local

        calendar cache was pruned. No ``start_at_utc`` filter — past events are returned.

        Cancelled events are included so the desktop can render the correct state.

        A stranger''s ``externalId`` matches no row (query is ``user_id``-scoped).

        Event order is unspecified; callers look events up by ``externalId``.'
      operationId: get_events_batch
      security:
      - ApiKeyHeaderPatched: []
      parameters:
      - name: ids
        in: query
        required: true
        schema:
          type: string
          description: Comma-separated calendar ``externalId`` values to look up. At most 50 per request; missing ids are absent from the response.
          title: Ids
        description: Comma-separated calendar ``externalId`` values to look up. At most 50 per request; missing ids are absent from the response.
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CalendarEventBatchResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/v1/calendar/sync:
    get:
      tags:
      - Calendar
      summary: Sync Calendar
      description: "Return calendar events the desktop hasn't seen yet.\n\nTwo modes:\n\n* ``since`` provided — incremental: events with ``updated_at > since``,\n  ordered ascending by ``(updated_at, id)``. Stable across rows that\n  share an ``updated_at`` (e.g. webhook bulk fan-out).\n* ``since`` null — initial sync: all upcoming events\n  (``start_at_utc >= now``). Ordered ascending by ``(start_at_utc, id)``.\n\nThe response anchors on the server clock — desktop uses ``serverTime`` as\nthe next ``since``. Capped at ``SYNC_PAGE_LIMIT`` events per response;\nwhen more remain ``nextCursor`` is non-null and the caller should refetch\nimmediately with the same ``since`` and the new cursor."
      operationId: sync_calendar
      security:
      - ApiKeyHeaderPatched: []
      parameters:
      - name: since
        in: query
        required: false
        schema:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          description: ISO 8601 timestamp from the previous response's `serverTime`. Omit on first sync to get all upcoming events.
          title: Since
        description: ISO 8601 timestamp from the previous response's `serverTime`. Omit on first sync to get all upcoming events.
      - name: cursor
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: Pagination cursor from the previous response's `nextCursor`. Only meaningful when paired with the same `since` value.
          title: Cursor
        description: Pagination cursor from the previous response's `nextCursor`. Only meaningful when paired with the same `since` value.
      - name: launch
        in: query
        required: false
        schema:
          type: boolean
          description: When true, the desktop is signalling a genuine app launch; the server schedules a background incremental sync to catch the user up. Defaults false; routine change-pulls omit it.
          default: false
          title: Launch
        description: When true, the desktop is signalling a genuine app launch; the server schedules a background incremental sync to catch the user up. Defaults false; routine change-pulls omit it.
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/v1/calendar/prereads/agentic_sync:
    get:
      tags:
      - Calendar
      summary: Sync Agentic Prereads
      description: 'Return agentic pre-read deliverables the desktop has not synced yet.


        Read-only: never dispatches a managed-agent session. The webhook writes

        rows to ``preread_generation`` and bumps the ``agentic_prereads`` sync

        resource; this endpoint is how the desktop ingests those rows.


        Incremental mode (``since`` provided): rows with ``generated_at > since``,

        ordered ascending by ``(generated_at, id)``. Initial mode (``since``

        omitted): every successful agentic row for the user, regardless of event age.'
      operationId: sync_agentic_prereads
      security:
      - ApiKeyHeaderPatched: []
      parameters:
      - name: since
        in: query
        required: false
        schema:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          description: ISO 8601 timestamp from the previous response's `serverTime`. Omit on first sync to receive all agentic deliverables for upcoming events.
          title: Since
        description: ISO 8601 timestamp from the previous response's `serverTime`. Omit on first sync to receive all agentic deliverables for upcoming events.
      - name: cursor
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: Pagination cursor from the previous response's `nextCursor`. Only meaningful when paired with the same `since` value.
          title: Cursor
        description: Pagination cursor from the previous response's `nextCursor`. Only meaningful when paired with the same `since` value.
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgenticPrereadSyncResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/v1/calendar/events/{external_id}/preread:
    get:
      tags:
      - Calendar
      summary: Get Event Preread
      description: 'Generate or dispatch a pre-read for an upcoming calendar event.


        The desktop already has Google''s ``external_id`` from the sync payload, so

        callers identify the event by that string rather than the internal UUID.

        The query layer scopes by ``user_id`` — a stranger''s ``external_id``

        matches no row.


        When ``useAgenticPreread`` is false (the default), the handler

        synchronously builds context and calls the LLM; the worst case is bounded

        by the generator''s 30s Anthropic client timeout.


        When ``useAgenticPreread`` is true, the handler dispatches a managed-agent

        session in the background and returns an empty response immediately. The

        desktop picks up the deliverable via the ``agentic_prereads`` sync resource once

        the webhook completes.


        Events that already ended are fetch-only: the latest stored pre-read is

        returned as-is, and nothing is ever generated or dispatched for them.


        Returns 404 only when the event isn''t eligible for a pre-read at all:


        * No CalendarEvent for ``(user_id, external_id)``.

        * The event is cancelled.

        * The event already ended and has no stored pre-read.


        When the event exists but the LLM-side generator fails (rate limits,

        validation errors, transient outages), the response is still 200 with

        a minimal calendar-derived fallback so the desktop can render the

        pre-read card with baseline metadata instead of an empty failure

        state. The generator already logged + counted the failure.'
      operationId: get_event_preread
      security:
      - ApiKeyHeaderPatched: []
      parameters:
      - name: external_id
        in: path
        required: true
        schema:
          type: string
          title: External Id
      - name: useAgenticPreread
        in: query
        required: false
        schema:
          type: boolean
          description: When true, dispatch a managed-agent pre-read session asynchronously and return an empty response. The desktop picks up the result via the ``agentic_prereads`` sync resource.
          default: false
          title: Useagenticpreread
        description: When true, dispatch a managed-agent pre-read session asynchronously and return an empty response. The desktop picks up the result via the ``agentic_prereads`` sync resource.
      - name: trigger
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: 'Optional client-supplied hint describing why the pre-read is being fetched. Allowed values: precache, reminder, debug, sync. Used for telemetry only — unknown values fall through silently.'
          title: Trigger
        description: 'Optional client-supplied hint describing why the pre-read is being fetched. Allowed values: precache, reminder, debug, sync. Used for telemetry only — unknown values fall through silently.'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PrereadResponse'
        '429':
          description: Too Many Requests
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /api/v1/calendar/preread/feedback:
    post:
      tags:
      - Calendar
      summary: Submit Event Preread Feedback
      description: 'Submit internal dogfood feedback on a cached pre-read.


        Posts to a dedicated Slack channel. The server enriches the payload with

        the calendar event''s start time (meeting date), the report timestamp, and

        the latest Claude managed-agent session id when one exists for this event.'
      operationId: submit_event_preread_feedback
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrereadFeedbackRequest'
        required: true
      responses:
        '204':
          description: Successful Response
        '429':
          description: Too Many Requests
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
      - ApiKeyHeaderPatched: []
  /api/v1/calendar/resync:
    post:
      tags:
      - Calendar
      summary: Manual Resync
      description: 'Debug-only: force a Google Calendar backfill for the current user.


        Calls :meth:`CalendarService.backfill` which fetches the next

        ``BACKFILL_WINDOW_DAYS`` of events from Google via the user''s existing

        calendar connection and upserts them into ``calendar_event``. Idempotent

        — re-running just refreshes the rows.


        Surfaced from the desktop Meetings hub as an internal-only "Manual

        Resync" affordance so internal users on a fresh local backend can

        populate calendar data without re-doing the OAuth flow. Requires that

        the user already has an active Google Calendar connection on *this*

        backend.'
      operationId: manual_resync
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResyncResponse'
      security:
      - ApiKeyHeaderPatched: []
components:
  schemas:
    PrereadResponse:
      properties:
        title:
          type: string
          title: Title
        summary:
          type: string
          title: Summary
        contentMd:
          type: string
          title: Contentmd
        generatedAt:
          type: string
          format: date-time
          title: Generatedat
      type: object
      required:
      - title
      - summary
      - contentMd
      - generatedAt
      title: PrereadResponse
      description: 'Response shape for ``GET /api/v1/calendar/events/{external_id}/preread``.


        The endpoint generates the pre-read on-the-fly per request (no

        persistence), so ``generated_at`` is the response timestamp.


        ``title`` is a very short calendar-event-label suitable for a

        notification card. ``summary`` is a single declarative sentence that

        describes what the meeting is about — surfaced under the calendar

        event title on the desktop hub.'
    CalendarEventBatchResponse:
      properties:
        events:
          items:
            $ref: '#/components/schemas/CalendarEventDTO'
          type: array
          title: Events
      type: object
      required:
      - events
      title: CalendarEventBatchResponse
      description: 'Response shape for ``GET /api/v1/calendar/events/batch``.


        Returns events matching the requested ``externalId`` values. Missing ids

        (not in the DB, or belonging to another user) are simply absent.'
    AgenticPrereadSyncItemDTO:
      properties:
        title:
          type: string
          title: Title
        summary:
          type: string
          title: Summary
        contentMd:
          type: string
          title: Contentmd
        generatedAt:
          type: string
          format: date-time
          title: Generatedat
        externalId:
          type: string
          title: Externalid
      type: object
      required:
      - title
      - summary
      - contentMd
      - generatedAt
      - externalId
      title: AgenticPrereadSyncItemDTO
      description: One agentic pre-read deliverable keyed by the calendar event's ``external_id``.
    AgenticPrereadSyncResponse:
      properties:
        agenticPrereads:
          items:
            $ref: '#/components/schemas/AgenticPrereadSyncItemDTO'
          type: array
          title: Agenticprereads
        serverTime:
          type: string
          format: date-time
          title: Servertime
        nextCursor:
          anyOf:
          - type: string
          - type: 'null'
          title: Nextcursor
      type: object
      required:
      - agenticPrereads
      - serverTime
      title: AgenticPrereadSyncResponse
      description: 'Response shape for ``GET /api/v1/calendar/prereads/agentic_sync``.


        Mirrors the calendar sync cursor contract: desktop advances ``since`` to

        ``serverTime`` once ``nextCursor`` is null.'
    ParticipantDTO:
      properties:
        name:
          type: string
          title: Name
        photoUrl:
          anyOf:
          - type: string
          - type: 'null'
          title: Photourl
        isSelf:
          type: boolean
          title: Isself
          default: false
      type: object
      required:
      - name
      title: ParticipantDTO
      description: 'Per-attendee detail for the desktop: display name, optional real profile photo, and self flag.


        Carried alongside the legacy ``participants: list[str]`` (display-name-only) field so existing

        consumers keep working; the desktop reads ``photoUrl`` / ``isSelf`` to render meeting avatars.'
    PrereadFeedbackRequest:
      properties:
        externalId:
          type: string
          title: Externalid
        rating:
          type: string
          enum:
          - up
          - down
          title: Rating
        feedback:
          anyOf:
          - type: string
          - type: 'null'
          title: Feedback
        title:
          anyOf:
          - type: string
          - type: 'null'
          title: Title
        reporterName:
          anyOf:
          - type: string
          - type: 'null'
          title: Reportername
        preread:
          anyOf:
          - type: string
          - type: 'null'
          title: Preread
      type: object
      required:
      - externalId
      - rating
      title: PrereadFeedbackRequest
      description: Request shape for ``POST /api/v1/calendar/preread/feedback``.
    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
    SyncResponse:
      properties:
        events:
          items:
            $ref: '#/components/schemas/CalendarEventDTO'
          type: array
          title: Events
        serverTime:
          type: string
          format: date-time
          title: Servertime
        nextCursor:
          anyOf:
          - type: string
          - type: 'null'
          title: Nextcursor
      type: object
      required:
      - events
      - serverTime
      title: SyncResponse
      description: 'Response shape for `GET /api/v1/calendar/sync` (Day 2).


        Defined now so the contract is locked. `server_time` is the timestamp at which

        the server produced the response — desktop should use this as the next `since` cursor.


        When `next_cursor` is non-null, the desktop should refetch immediately with

        ``since`` unchanged and ``cursor`` set to this value. When null, the result set

        fits in a single page and the desktop advances to ``server_time`` as the next

        ``since`` value.'
    CalendarEventDTO:
      properties:
        externalId:
          type: string
          title: Externalid
        title:
          type: string
          title: Title
        startAtUtc:
          type: string
          format: date-time
          title: Startatutc
        endAtUtc:
          type: string
          format: date-time
          title: Endatutc
        conferenceUrl:
          anyOf:
          - type: string
          - type: 'null'
          title: Conferenceurl
        participants:
          items:
            type: string
          type: array
          title: Participants
        participantDetails:
          items:
            $ref: '#/components/schemas/ParticipantDTO'
          type: array
          title: Participantdetails
        status:
          type: string
          enum:
          - active
          - cancelled
          title: Status
          default: active
        colorId:
          anyOf:
          - type: string
          - type: 'null'
          title: Colorid
        selfResponseStatus:
          anyOf:
          - type: string
          - type: 'null'
          title: Selfresponsestatus
        updatedAt:
          type: string
          format: date-time
          title: Updatedat
      type: object
      required:
      - externalId
      - title
      - startAtUtc
      - endAtUtc
      - updatedAt
      title: CalendarEventDTO
      description: 'API contract for a calendar event mirrored from Google Calendar.


        Field aliases serialize to camelCase for the desktop client. `populate_by_name=True`

        accepts both snake_case and camelCase on input.'
    ResyncResponse:
      properties:
        eventsCount:
          type: integer
          title: Eventscount
        success:
          type: boolean
          title: Success
      type: object
      required:
      - eventsCount
      - success
      title: ResyncResponse
      description: 'Response shape for ``POST /api/v1/calendar/resync``.


        Returned by the manual-resync debug endpoint. ``events_count`` is the

        number of ``calendar_event`` rows present for the user after the

        backfill completes.'
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
  securitySchemes:
    ApiKeyHeaderPatched:
      type: apiKey
      in: header
      name: Authorization
    APIKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key