| name | openwhispr-api |
| description | Use this skill when building integrations with the OpenWhispr REST API, calling OpenWhispr endpoints, managing notes/folders/transcriptions programmatically, accessing team-space content with workspace keys, or connecting to the OpenWhispr MCP server. Covers authentication, all V1 endpoints, spaces, pagination, rate limits, error handling, and the remote MCP server. |
OpenWhispr API v1
Use this reference when making requests to the OpenWhispr REST API. All endpoints are under the V1 path and require API key authentication.
Authentication
Pass the API key as a Bearer token in the Authorization header on every request.
Authorization: Bearer owk_live_YOUR_KEY
There are two kinds of key:
- Personal keys (
owk_live_) — access your own private notes, folders, and transcriptions. Generated under Settings > API Keys.
- Workspace keys (
ow_wks_live_) — access a workspace's team spaces. Generated by a workspace admin under Settings > Workspace > Developer.
Both are shown once at creation.
Scopes
Each key has scoped permissions. The API rejects requests missing the required scope with 403 Forbidden.
Personal key scopes:
| Scope | Grants |
|---|
notes:read | List, get, and search notes. List folders. |
notes:write | Create, update, and delete notes. Create folders. |
transcriptions:read | List and get transcriptions. |
usage:read | Read usage statistics. |
Workspace key scopes:
| Scope | Grants |
|---|
workspace:notes:read | List, get, and search team-space notes. |
workspace:notes:write | Create, update, and delete team-space notes. |
workspace:folders:read | List team-space folders. |
workspace:folders:write | Create team-space folders. |
workspace:transcriptions:read | Space discovery only for now — no transcription endpoints accept workspace keys yet. |
workspace:* | All of the above (admin). |
Any of the content scopes above also grants GET /spaces/list (space discovery).
Team spaces
A space is a shared container of notes and folders inside a workspace. Personal keys never see team-space content; workspace keys do, and always address one space at a time via a space_id:
- Discover the spaces a key can reach with
GET /spaces/list.
list, create, and search for notes and folders require a space_id (query param or body field) when called with a workspace key, and reject one when called with a personal key.
- Operations addressed by note id (
GET/PATCH/DELETE /notes/{id}, GET /notes/{id}/transcript) resolve the note's space automatically — no space_id needed. A workspace key may act on any note in any of its workspace's spaces.
space_id cannot be changed through the API — a note stays in the space it was created in. Move notes between spaces from the desktop app.
Base URL
https://api.openwhispr.com/api/v1
Response Envelope
Wrap all responses in a consistent envelope.
Single resource:
{ "data": { "id": "uuid", "title": "My note", ... } }
Paginated list:
{
"data": [{ ... }, { ... }],
"has_more": true,
"next_cursor": "opaque-cursor-string"
}
Error:
{ "error": { "code": "not_found", "message": "Note not found" } }
Error Codes
| HTTP Status | Code | Meaning |
|---|
| 400 | validation_error | Invalid request body or query params |
| 401 | invalid_api_key | Missing, malformed, expired, or revoked key |
| 403 | forbidden | Key lacks required scope |
| 404 | not_found | Resource does not exist or belongs to another user |
| 405 | method_not_allowed | Wrong HTTP method |
| 409 | conflict | Duplicate resource (e.g. folder name) |
| 429 | rate_limited | Rate limit exceeded — check Retry-After header |
| 500 | internal_error | Server error |
Rate Limits
Enforced per API key with minute and daily windows. Search requests cost 5x against the rate limit.
| Plan | Per Minute | Per Day |
|---|
| Free | 30 | 1,000 |
| Pro | 120 | 10,000 |
| Business | 300 | 50,000 |
Response headers on every request:
| Header | Description |
|---|
X-RateLimit-Limit | Max requests per minute |
X-RateLimit-Remaining | Remaining in current window |
X-RateLimit-Reset | Unix timestamp when window resets |
Retry-After | Seconds to wait (only on 429) |
Pagination
List endpoints use cursor-based pagination. Treat next_cursor as an opaque string: pass it back verbatim as the cursor query parameter to fetch the next page — never parse it. (Notes cursors are base64url-encoded composites; transcription cursors are timestamps. Timestamp cursors issued before the composite format remain accepted.) When has_more is false, there are no more results. An unparseable cursor returns 400 validation_error.
GET /notes/list?limit=50&cursor=NEXT_CURSOR_FROM_PREVIOUS_RESPONSE
Endpoints
Spaces (workspace keys only)
List Spaces — GET /spaces/list
Scope: any workspace content scope (workspace:notes:read/write, workspace:folders:read/write, or workspace:transcriptions:read). Returns the non-archived team spaces in the key's workspace (id, name, slug, description, emoji, created_at, updated_at). Use a returned id as the space_id on notes/folders requests.
Notes
List Notes — GET /notes/list
| Param | Type | Required | Description |
|---|
limit | integer | No | 1-100, default 50 |
cursor | string | No | Pagination cursor |
folder_id | UUID | No | Filter by folder |
space_id | UUID | Workspace keys | Team space to list. Required for workspace keys; rejected for personal keys. |
Scope: notes:read (personal) / workspace:notes:read (workspace) | | | |
Get Note — GET /notes/{id}
Scope: notes:read / workspace:notes:read. Returns 404 if the note does not exist or is deleted. A workspace key may fetch any note in its workspace's spaces.
Create Note — POST /notes/create
| Field | Type | Required | Description |
|---|
content | string | Yes | Note body text |
title | string | No | Note title |
enhanced_content | string | No | Cleaned/enhanced version |
note_type | enum | No | personal (default), meeting, upload |
folder_id | UUID | No | Target folder |
space_id | UUID | Workspace keys | Team space to create in. Required for workspace keys; rejected for personal keys. |
Scope: notes:write / workspace:notes:write. Returns 201 with the created note. | | | |
Update Note — PATCH /notes/{id}
| Field | Type | Required | Description |
|---|
title | string | No | New title |
content | string | No | New content |
enhanced_content | string | No | New enhanced content |
folder_id | UUID | No | Move to folder |
Scope: notes:write / workspace:notes:write. All fields optional — only provided fields are updated. Cannot change a note's space. | | | |
Delete Note — DELETE /notes/{id}
Scope: notes:write / workspace:notes:write. Soft-deletes the note. Returns 204 No Content.
Search Notes — POST /notes/search
| Field | Type | Required | Description |
|---|
query | string | Yes | Search text (1-500 chars) |
limit | integer | No | 1-50, default 20 |
space_id | UUID | Workspace keys | Team space to search. Required for workspace keys; rejected for personal keys. |
Scope: notes:read / workspace:notes:read. Uses hybrid semantic (vector) + full-text search with relevance scoring. Costs 5x against rate limit. | | | |
Folders
List Folders — GET /folders/list
| Param | Type | Required | Description |
|---|
space_id | UUID | Workspace keys | Team space to list. Required for workspace keys; rejected for personal keys. |
Scope: notes:read / workspace:folders:read. Returns all folders sorted by sort_order then created_at. | | | |
Create Folder — POST /folders/create
| Field | Type | Required | Description |
|---|
name | string | Yes | Folder name (1-100 chars) |
sort_order | integer | No | Sort position |
space_id | UUID | Workspace keys | Team space to create in. Required for workspace keys; rejected for personal keys. |
Scope: notes:write / workspace:folders:write. Max 50 folders per user. Returns 409 if name already exists. | | | |
Transcriptions
List Transcriptions — GET /transcriptions/list
| Param | Type | Required | Description |
|---|
limit | integer | No | 1-100, default 50 |
cursor | string | No | Pagination cursor |
Scope: transcriptions:read. Returns transcription history with text, word_count, source, provider, model, language, audio_duration_ms, processing_ms. | | | |
Get Transcription — GET /transcriptions/{id}
Scope: transcriptions:read.
Usage
Get Usage — GET /usage
Scope: usage:read. Returns:
words_used — Words consumed this period
words_remaining — Words left in quota
limit — Total word quota
plan — Current plan (free, pro, business)
is_subscribed — Whether user has active subscription
current_period_end — End of current billing period
billing_interval — Billing cycle
MCP Server
For AI assistant integration (Claude, Cursor, VS Code), connect to the remote MCP server at:
https://mcp.openwhispr.com/mcp
Pass the API key via Authorization: Bearer header. All V1 endpoints are available as MCP tools. The server uses Streamable HTTP transport (stateless, no sessions).
Claude Code
claude mcp add openwhispr --transport http https://mcp.openwhispr.com/mcp \
--header "Authorization: Bearer owk_live_YOUR_KEY"
Cursor / VS Code
{
"mcpServers": {
"openwhispr": {
"url": "https://mcp.openwhispr.com/mcp",
"headers": { "Authorization": "Bearer owk_live_YOUR_KEY" }
}
}
}
Examples
List recent notes
curl -H "Authorization: Bearer owk_live_YOUR_KEY" \
"https://api.openwhispr.com/api/v1/notes/list?limit=10"
Create a note in a folder
curl -X POST \
-H "Authorization: Bearer owk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Remember to review PR #42", "title": "TODO", "folder_id": "UUID"}' \
https://api.openwhispr.com/api/v1/notes/create
Search notes
curl -X POST \
-H "Authorization: Bearer owk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "quarterly budget discussion"}' \
https://api.openwhispr.com/api/v1/notes/search
Paginate through all notes
cursor=""
while true; do
response=$(curl -s -H "Authorization: Bearer owk_live_YOUR_KEY" \
"https://api.openwhispr.com/api/v1/notes/list?limit=100&cursor=${cursor}")
echo "$response" | jq '.data[]'
has_more=$(echo "$response" | jq -r '.has_more')
[ "$has_more" != "true" ] && break
cursor=$(echo "$response" | jq -r '.next_cursor')
done
Check usage
curl -H "Authorization: Bearer owk_live_YOUR_KEY" \
https://api.openwhispr.com/api/v1/usage
Work with team spaces (workspace key)
curl -H "Authorization: Bearer ow_wks_live_YOUR_KEY" \
https://api.openwhispr.com/api/v1/spaces/list
curl -H "Authorization: Bearer ow_wks_live_YOUR_KEY" \
"https://api.openwhispr.com/api/v1/notes/list?space_id=SPACE_UUID&limit=50"
curl -X POST \
-H "Authorization: Bearer ow_wks_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Kickoff notes", "title": "Kickoff", "space_id": "SPACE_UUID"}' \
https://api.openwhispr.com/api/v1/notes/create