Wispr AI History API
The History API from Wispr AI — 8 operation(s) for history.
The History API from Wispr AI — 8 operation(s) for history.
openapi: 3.0.3
info:
title: Wispr Backend Analytics History API
description: Wispr Backend API
version: 0.5.2
tags:
- name: History
paths:
/history/report:
post:
tags:
- History
summary: Feedback History
operationId: feedback_history
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/HistoryFeedbackRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- ApiKeyHeaderPatched: []
/history/upload:
post:
tags:
- History
summary: Upload History
operationId: upload_history
security:
- ApiKeyHeaderPatched: []
parameters:
- name: platform
in: query
required: false
schema:
anyOf:
- $ref: '#/components/schemas/AttributionPlatform'
- type: 'null'
description: Attribution platform (e.g. desktop_mac, desktop_windows)
title: Platform
description: Attribution platform (e.g. desktop_mac, desktop_windows)
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UploadHistoryRequest'
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/history/stats:
get:
tags:
- History
summary: Get History Stats
description: Get user's history statistics
operationId: get_history_stats
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
security:
- ApiKeyHeaderPatched: []
post:
tags:
- History
summary: Overwrite History Stats
description: 'Overwrite user''s history stats. This should only be called by the client
when the remote user stats data are corrupted and should be overwritten
with the local data. Client is responsible for ensuring that the local stats
are preferred before calling this endpoint.'
operationId: overwrite_history_stats
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/OverwriteHistoryStatsRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- ApiKeyHeaderPatched: []
/history/context-stats:
get:
tags:
- History
summary: Get Context Stats
description: 'Get cross-device context stats (email, ai, work, personal_messaging, document, other)
derived from ClickHouse usage_History.'
operationId: get_context_stats
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ContextStatsResponse'
security:
- ApiKeyHeaderPatched: []
/history/stats/enterprise:
get:
tags:
- History
summary: Get Enterprise History Stats
description: 'Get history statistics for all users in the enterprise.
Accessible by any enterprise member (admin or non-admin).
Used for the enterprise usage dashboard and leaderboard.
Podiums and the 4-week historical leaderboard are served by dedicated endpoints:
``GET /history/stats/enterprise/podiums`` and
``GET /history/stats/enterprise/weekly-ranks``.
Note: This endpoint returns all metrics regardless of subscription tier.
The frontend is responsible for controlling access to individual metrics
based on subscription level and admin status:
- Admins can see aggregate metrics (total words, team size, etc.)
- Only Enterprise tier admins can see individual teammate breakdowns
- Non-admin Enterprise members can see the leaderboard with blurred teammate values
Non-Enterprise admins will see the data but the frontend will blur/hide individual
metrics and show an upgrade prompt.'
operationId: get_enterprise_history_stats
security:
- ApiKeyHeaderPatched: []
parameters:
- name: sort_by
in: query
required: false
schema:
anyOf:
- $ref: '#/components/schemas/EnterpriseSortBy'
- type: 'null'
description: Column to sort/paginate by. Defaults to total_words.
title: Sort By
description: Column to sort/paginate by. Defaults to total_words.
- name: order
in: query
required: false
schema:
$ref: '#/components/schemas/EnterpriseSortOrder'
description: Sort direction
default: desc
description: Sort direction
- name: offset
in: query
required: false
schema:
type: integer
minimum: 0
description: Pagination offset. Used only when ``limit`` is set.
default: 0
title: Offset
description: Pagination offset. Used only when ``limit`` is set.
- name: limit
in: query
required: false
schema:
anyOf:
- type: integer
maximum: 1000
minimum: 1
- type: 'null'
description: Page size for leaderboard-style pagination. Omit to return the full enterprise roster (no SQL LIMIT). Pass with offset to fetch a page.
title: Limit
description: Page size for leaderboard-style pagination. Omit to return the full enterprise roster (no SQL LIMIT). Pass with offset to fetch a page.
- name: sort_near_user
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: User ID to center the page around
title: Sort Near User
description: User ID to center the page around
- name: omit
in: query
required: false
schema:
type: array
items:
$ref: '#/components/schemas/EnterpriseUsageOmit'
description: Optional sections to omit for smaller payloads. Repeat the parameter (e.g. omit=apps&omit=daily_word_counts). `apps` skips app ARRAY columns in Postgres and removes app lists from JSON. `daily_word_counts` skips the ClickHouse query and omits that array from JSON.
default: []
title: Omit
description: Optional sections to omit for smaller payloads. Repeat the parameter (e.g. omit=apps&omit=daily_word_counts). `apps` skips app ARRAY columns in Postgres and removes app lists from JSON. `daily_word_counts` skips the ClickHouse query and omits that array from JSON.
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/EnterpriseUsageResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/history/stats/enterprise/weekly-ranks:
get:
tags:
- History
summary: Get Enterprise Weekly Ranks
description: 'Return the 4-week historical leaderboard for the caller''s enterprise.
Backed by the same Redis-cached rankings the dashboard already uses; the cache key
rotates after the Sunday 23:45 UTC ``reset_weekly_words`` cron (TTL 8 days). Like
podiums, membership is not part of the key,
so removed or new members may appear in cached data until the entry expires.
Auth + ``hideLeaderboard`` checks run before Redis; on a cache hit the handler skips the
full-roster Postgres query.
Note: Like ``GET /history/stats/enterprise``, this does not enforce subscription tier;
the client is responsible for tier-based gating of leaderboard features.'
operationId: get_enterprise_weekly_ranks
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/EnterpriseWeeklyRanksResponse'
security:
- ApiKeyHeaderPatched: []
/history/stats/enterprise/podiums:
get:
tags:
- History
summary: Get Enterprise Podiums
description: 'Return the top-6 podiums for every ranked metric for the caller''s enterprise.
Cached in Redis for 5 minutes. Unlike weekly ranks, invalidation is TTL-only (no membership
hash in the key), so removed members can appear until the cache entry expires.
Note: Like ``GET /history/stats/enterprise``, this does not enforce subscription tier;
the client is responsible for tier-based gating of leaderboard features.
Auth + ``hideLeaderboard`` checks run before Redis; on a cache hit the handler skips the
full-roster Postgres query.'
operationId: get_enterprise_podiums
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/EnterprisePodiumsResponse'
security:
- ApiKeyHeaderPatched: []
/history/mobile-words-this-week:
post:
tags:
- History
summary: Write Mobile Words This Week
description: Write mobile words this week to the database.
operationId: write_mobile_words_this_week
security:
- ApiKeyHeaderPatched: []
parameters:
- name: words_this_week
in: query
required: true
schema:
type: integer
title: Words This Week
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
WeeklyLeaderboardEntry:
properties:
user_id:
type: string
title: User Id
first_name:
anyOf:
- type: string
- type: 'null'
title: First Name
last_name:
anyOf:
- type: string
- type: 'null'
title: Last Name
rank:
type: integer
title: Rank
description: 1-based rank. Unique, no ties.
total_words:
type: integer
title: Total Words
type: object
required:
- user_id
- first_name
- last_name
- rank
- total_words
title: WeeklyLeaderboardEntry
description: A single user's rank and word total for one historical week.
ContextStatsItem:
properties:
domain:
type: string
title: Domain
total_words:
type: integer
title: Total Words
first_dictation_at:
anyOf:
- type: string
- type: 'null'
title: First Dictation At
last_dictation_at:
anyOf:
- type: string
- type: 'null'
title: Last Dictation At
type: object
required:
- domain
- total_words
title: ContextStatsItem
PodiumUser:
properties:
user_id:
type: string
title: User Id
email:
anyOf:
- type: string
- type: 'null'
title: Email
first_name:
anyOf:
- type: string
- type: 'null'
title: First Name
last_name:
anyOf:
- type: string
- type: 'null'
title: Last Name
rank:
type: integer
title: Rank
description: 1-based rank within this metric's leaderboard.
value:
anyOf:
- type: integer
- type: number
title: Value
description: The user's value for this metric (e.g. word count, day streak, or words-per-minute). Integer for counts/streaks; float for WPM.
type: object
required:
- user_id
- email
- first_name
- last_name
- rank
- value
title: PodiumUser
description: A single podium entry for a specific leaderboard metric.
AllTimePodiums:
properties:
total_words:
$ref: '#/components/schemas/MetricPodium'
desktop_words:
$ref: '#/components/schemas/MetricPodium'
mobile_words:
$ref: '#/components/schemas/MetricPodium'
day_streak:
$ref: '#/components/schemas/MetricPodium'
words_per_minute:
$ref: '#/components/schemas/MetricPodium'
type: object
title: AllTimePodiums
description: Top 6 users (plus the caller) for each all-time leaderboard metric.
OverwriteHistoryStatsRequest:
properties:
weekStreak:
type: integer
title: Weekstreak
dayStreak:
type: integer
title: Daystreak
totalWords:
type: integer
title: Totalwords
totalDuration:
type: number
title: Totalduration
totalNonEmptyDuration:
type: number
title: Totalnonemptyduration
averageWPM:
type: number
title: Averagewpm
wordsThisWeek:
type: integer
title: Wordsthisweek
lastTranscriptTimestamp:
type: number
title: Lasttranscripttimestamp
totalApps:
items:
type: string
type: array
title: Totalapps
desktopTotalWords:
type: integer
title: Desktoptotalwords
mobileTotalWords:
type: integer
title: Mobiletotalwords
desktopTotalDuration:
type: number
title: Desktoptotalduration
mobileTotalDuration:
type: number
title: Mobiletotalduration
desktopTotalNonEmptyDuration:
type: number
title: Desktoptotalnonemptyduration
mobileTotalNonEmptyDuration:
type: number
title: Mobiletotalnonemptyduration
desktopAverageWPM:
type: number
title: Desktopaveragewpm
mobileAverageWPM:
type: number
title: Mobileaveragewpm
desktopWordsThisWeek:
type: integer
title: Desktopwordsthisweek
mobileWordsThisWeek:
type: integer
title: Mobilewordsthisweek
desktopLastTranscriptTimestamp:
type: number
title: Desktoplasttranscripttimestamp
mobileLastTranscriptTimestamp:
type: number
title: Mobilelasttranscripttimestamp
desktopTotalApps:
items:
type: string
type: array
title: Desktoptotalapps
mobileTotalApps:
items:
type: string
type: array
title: Mobiletotalapps
type: object
required:
- weekStreak
- dayStreak
- totalWords
- totalDuration
- totalNonEmptyDuration
- averageWPM
- wordsThisWeek
- lastTranscriptTimestamp
- totalApps
- desktopTotalWords
- mobileTotalWords
- desktopTotalDuration
- mobileTotalDuration
- desktopTotalNonEmptyDuration
- mobileTotalNonEmptyDuration
- desktopAverageWPM
- mobileAverageWPM
- desktopWordsThisWeek
- mobileWordsThisWeek
- desktopLastTranscriptTimestamp
- mobileLastTranscriptTimestamp
- desktopTotalApps
- mobileTotalApps
title: OverwriteHistoryStatsRequest
EnrichedUserStats:
properties:
user_id:
type: string
title: User Id
email:
anyOf:
- type: string
- type: 'null'
title: Email
first_name:
anyOf:
- type: string
- type: 'null'
title: First Name
last_name:
anyOf:
- type: string
- type: 'null'
title: Last Name
stats:
$ref: '#/components/schemas/UserStats'
daily_word_counts:
items:
$ref: '#/components/schemas/DailyWordCountData'
type: array
title: Daily Word Counts
type: object
required:
- user_id
- email
- first_name
- last_name
- stats
- daily_word_counts
title: EnrichedUserStats
description: User statistics enriched with profile information
EnterpriseUsageResponse:
properties:
users:
items:
$ref: '#/components/schemas/EnrichedUserStats'
type: array
title: Users
total_count:
type: integer
title: Total Count
description: Users counted in the ranked leaderboard query (those with stats rows).
default: 0
team_member_count:
type: integer
title: Team Member Count
description: Total members on the enterprise team (roster size).
default: 0
offset:
anyOf:
- type: integer
- type: 'null'
title: Offset
description: Pagination offset when a page size was requested; null for full-roster responses.
limit:
anyOf:
- type: integer
- type: 'null'
title: Limit
description: Page size when paginated; null when the client omitted limit (full roster).
type: object
required:
- users
title: EnterpriseUsageResponse
description: Response model for enterprise usage statistics
DailyWordCountData:
properties:
day:
type: string
title: Day
wordcount:
type: integer
title: Wordcount
type: object
required:
- day
- wordcount
title: DailyWordCountData
description: Daily word count data for usage charts
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
UploadHistoryRequest:
properties:
history:
items:
$ref: '#/components/schemas/Record'
type: array
title: History
ga4_client_id:
anyOf:
- type: string
- type: 'null'
title: Ga4 Client Id
description: GA4 client identifier (desktop_client_id or app_instance_id)
type: object
required:
- history
title: UploadHistoryRequest
description: 'Request for uploading history records.
The specific attribution platform (e.g. desktop_mac, desktop_windows) is
passed as a query parameter, not in the body. Per-record platform fields
only distinguish desktop/ios/android and lack OS specificity.
The destination user is the authenticated caller — never trust a client-
supplied user identifier. Any extra fields (e.g. legacy clients still
sending ``userId``) are silently ignored by Pydantic.'
MetricPodium:
properties:
top_entries:
items:
$ref: '#/components/schemas/PodiumUser'
type: array
title: Top Entries
description: Top min(6, total_users_ranked) PodiumUser rows by rank (ascending).
requesting_user_entry:
anyOf:
- $ref: '#/components/schemas/PodiumUser'
- type: 'null'
description: The caller's row when they are outside the top 6; null when the caller is already in top_entries or is not ranked for this metric.
type: object
title: MetricPodium
description: Top users for one leaderboard metric, plus the caller's entry when outside the top 6.
HistoryFeedbackRequest:
properties:
userEmail:
type: string
title: Useremail
transcriptEntityId:
type: string
title: Transcriptentityid
status:
anyOf:
- type: string
- type: 'null'
title: Status
asrText:
anyOf:
- type: string
- type: 'null'
title: Asrtext
formattedText:
anyOf:
- type: string
- type: 'null'
title: Formattedtext
serverFinalizedText:
anyOf:
- type: string
- type: 'null'
title: Serverfinalizedtext
pastedText:
anyOf:
- type: string
- type: 'null'
title: Pastedtext
feedback:
type: string
title: Feedback
audio:
anyOf:
- type: string
contentMediaType: application/octet-stream
- type: 'null'
title: Audio
logs:
anyOf:
- additionalProperties:
type: string
type: object
- type: 'null'
title: Logs
usedFallbackAsr:
anyOf:
- type: boolean
- type: 'null'
title: Usedfallbackasr
usedFallbackFormatting:
anyOf:
- type: boolean
- type: 'null'
title: Usedfallbackformatting
architecture:
type: string
title: Architecture
region:
type: string
title: Region
codec:
$ref: '#/components/schemas/Codec'
default: wav
platform:
$ref: '#/components/schemas/src__common__constants__Platform'
default: desktop
imageData:
anyOf:
- type: string
- type: 'null'
title: Imagedata
imageType:
anyOf:
- $ref: '#/components/schemas/ImageType'
- type: 'null'
appVersion:
anyOf:
- type: string
- type: 'null'
title: Appversion
selectedLanguages:
anyOf:
- items:
type: string
type: array
- type: 'null'
title: Selectedlanguages
type: object
required:
- userEmail
- transcriptEntityId
- feedback
title: HistoryFeedbackRequest
ContextStatsResponse:
properties:
contexts:
items:
$ref: '#/components/schemas/ContextStatsItem'
type: array
title: Contexts
type: object
required:
- contexts
title: ContextStatsResponse
EnterprisePodiumsResponse:
properties:
podiums:
$ref: '#/components/schemas/LeaderboardPodiums'
description: Top 6 users for each leaderboard metric plus the caller's entry when they are outside the top 6, grouped by time window (``all_time`` with 5 categories, ``this_week`` with 3).
type: object
required:
- podiums
title: EnterprisePodiumsResponse
description: Response model for GET /history/stats/enterprise/podiums.
EnterpriseWeeklyRanksResponse:
properties:
weekly_ranks:
items:
$ref: '#/components/schemas/WeeklyLeaderboard'
type: array
title: Weekly Ranks
description: 4 historical completed weeks of ranked leaderboards, newest first (index 0 = most recently closed week). Weeks are Sunday–Saturday UTC; a week is treated as closed after the Sunday 23:45 UTC reset_weekly_words cron. Enterprise-scoped. Current-week totals/rank are on each user's stats (words_this_week, words_this_week_rank).
type: object
required:
- weekly_ranks
title: EnterpriseWeeklyRanksResponse
description: Response model for GET /history/stats/enterprise/weekly-ranks.
LeaderboardPodiums:
properties:
all_time:
$ref: '#/components/schemas/AllTimePodiums'
this_week:
$ref: '#/components/schemas/ThisWeekPodiums'
type: object
title: LeaderboardPodiums
description: Top 6 users per leaderboard category (plus the caller), split by time window.
ThisWeekPodiums:
properties:
total_words:
$ref: '#/components/schemas/MetricPodium'
desktop_words:
$ref: '#/components/schemas/MetricPodium'
mobile_words:
$ref: '#/components/schemas/MetricPodium'
type: object
title: ThisWeekPodiums
description: 'Top 6 users (plus the caller) for each this-week leaderboard metric.
Streak and WPM are intentionally excluded — they''re all-time only.'
Record:
properties:
transcriptEntityId:
type: string
title: Transcriptentityid
asrText:
anyOf:
- type: string
- type: 'null'
title: Asrtext
formattedText:
anyOf:
- type: string
- type: 'null'
title: Formattedtext
serverFinalizedText:
anyOf:
- type: string
- type: 'null'
title: Serverfinalizedtext
pastedText:
anyOf:
- type: string
- type: 'null'
title: Pastedtext
formattingDivergenceScore:
anyOf:
- type: number
- type: 'null'
title: Formattingdivergencescore
textboxContents:
anyOf:
- type: string
- type: 'null'
title: Textboxcontents
editedText:
anyOf:
- type: string
- type: 'null'
title: Editedtext
editedTextUnbounded:
anyOf:
- type: string
- type: 'null'
title: Editedtextunbounded
timestamp:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Timestamp
audio:
anyOf:
- type: string
contentMediaType: application/octet-stream
- type: 'null'
title: Audio
opusChunks:
anyOf:
- type: object
- type: 'null'
title: Opuschunks
builtInAudio:
anyOf:
- type: string
contentMediaType: application/octet-stream
- type: 'null'
title: Builtinaudio
screenshot:
anyOf:
- type: string
contentMediaType: application/octet-stream
- type: 'null'
title: Screenshot
additionalContext:
anyOf:
- type: object
- type: 'null'
title: Additionalcontext
status:
anyOf:
- type: string
- type: 'null'
title: Status
app:
anyOf:
- type: string
- type: 'null'
title: App
url:
anyOf:
- type: string
- type: 'null'
title: Url
e2eLatency:
anyOf:
- type: number
- type: 'null'
title: E2Elatency
shareType:
anyOf:
- type: string
- type: 'null'
title: Sharetype
needsUploading:
anyOf:
- type: boolean
- type: 'null'
title: Needsuploading
duration:
anyOf:
- type: number
- type: 'null'
title: Duration
speechDuration:
anyOf:
- type: number
- type: 'null'
title: Speechduration
numWords:
anyOf:
- type: integer
- type: 'null'
title: Numwords
appVersion:
anyOf:
- type: string
- type: 'null'
title: Appversion
toneMatchedText:
anyOf:
- type: string
- type: 'null'
title: Tonematchedtext
toneMatchPairs:
anyOf:
- items:
type: object
type: array
- type: 'null'
title: Tonematchpairs
isArchived:
anyOf:
- type: boolean
- type: 'null'
title: Isarchived
feedback:
anyOf:
- type: string
- type: 'null'
title: Feedback
language:
anyOf:
- type: string
- type: 'null'
title: Language
micDevice:
anyOf:
- type: string
- type: 'null'
title: Micdevice
defaultAsrText:
anyOf:
- type: string
- type: 'null'
title: Defaultasrtext
fallbackAsrText:
anyOf:
- type: string
- type: 'null'
title: Fallbackasrtext
defaultFormattedText:
anyOf:
- type: string
- type: 'null'
title: Defaultformattedtext
fallbackFormattedText:
anyOf:
- type: string
- type: 'null'
title: Fallbackformattedtext
fallbackAsrDivergenceScore:
anyOf:
- type: number
- type: 'null'
title: Fallbackasrdivergencescore
fallbackFormattingDivergenceScore:
anyOf:
- type: number
- type: 'null'
title: Fallbackformattingdivergencescore
detectedLanguage:
anyOf:
- type: string
- type: 'null'
title: Detectedlanguage
averageLogProb:
anyOf:
- type: number
- type: 'null'
title: Averagelogprob
axText:
anyOf:
- type: string
- type: 'null'
title: Axtext
axHTML:
anyOf:
- type: string
- type: 'null'
title: Axhtml
desiredAsr:
anyOf:
- type: string
- type: 'null'
title: Desiredasr
desiredFormatted:
anyOf:
- type: string
- type: 'null'
title: Desiredformatted
userEditMetaData:
anyOf:
- type: object
- type: 'null'
title: Usereditmetadata
calledExternalAsr:
anyOf:
- type: boolean
- type: 'null'
title: Calledexternalasr
transcriptOrigin:
anyOf:
- type: string
- type: 'null'
title: Transcriptorigin
platform:
anyOf:
- type: string
- type: 'null'
title: Platform
default: desktop
transcriptCommand:
anyOf:
- type: string
- type: 'null'
title: Transcriptcommand
personalizationStyleSettings:
anyOf:
- type: object
- type: 'null'
title: Personalizationstylesettings
numWordsCorrected:
anyOf:
- type: integer
- type: 'null'
title: Numwordscorrected
numDictionaryReplacements:
anyOf:
- type: integer
- type: 'null'
title: Numdictionaryreplacements
timezoneOffsetMinutes:
anyOf:
- type: integer
- type: 'null'
title: Timezoneoffsetminutes
contentObservationEndReason:
anyOf:
- type: string
- type: 'null'
title: Contentobservationendreason
contentObservationEndLastKeystroke:
anyOf:
- type: string
- type: 'null'
title: Contentobservationendlastkeystroke
editDistanceToDictated:
anyOf:
- type: number
# --- truncated at 32 KB (41 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/wispr-ai/refs/heads/main/openapi/wispr-ai-history-api-openapi.yml