{
  "openapi": "3.1.0",
  "info": {
    "title": "Wave API",
    "version": "1.0.0",
    "description": "API for accessing Wave AI session data, transcripts, and managing webhooks. Build AI integrations, CRM syncs, analytics dashboards, and productivity tools.",
    "contact": {
      "name": "Wave Developer Support",
      "url": "https://wave.co/developers",
      "email": "support@wave.co"
    }
  },
  "servers": [
    {
      "url": "https://api.wave.co",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Sessions",
      "description": "List, search, update, and delete sessions. PATCH supports structured action items with optimistic concurrency."
    },
    {
      "name": "Folders",
      "description": "Create folders and manage non-exclusive session membership. Use with `GET /v1/sessions?folder=…` for filtered listing."
    },
    {
      "name": "Transcripts",
      "description": "Access session transcripts with speaker segments"
    },
    {
      "name": "Media",
      "description": "Get signed URLs for audio and video files"
    },
    {
      "name": "Account",
      "description": "User profile and subscription info"
    },
    {
      "name": "Events",
      "description": "Per-token cursor feed. Poll and ack instead of running a webhook receiver; same event shapes."
    },
    {
      "name": "Webhooks",
      "description": "Register and manage webhook endpoints"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Wave API token (wave_api_...), minted at https://app.wave.co/settings/integrations. Tokens carry only the scopes granted at creation; each operation lists the ones it requires. The scope vocabulary is machine-readable as scopes_supported in RFC 9728 protected-resource metadata at https://api.wave.co/.well-known/oauth-protected-resource. Available scopes: `sessions:read` — List and read session metadata, summaries, and action items. `sessions:write` — Update session titles, notes, tags, and structured action items. `sessions:delete` — Delete a session and its recording. `sessions:search` — Run semantic search across the account's sessions. `transcripts:read` — Read full transcripts with speaker segments. `media:read` — Mint signed URLs for session audio and video. `account:read` — Read the token owner's profile and subscription state. `folders:write` — Create folders and manage session membership. `events:read` — Poll the per-token event cursor feed and acknowledge events. `webhooks:manage` — Register webhook endpoints, rotate secrets, and send test deliveries."
      }
    },
    "schemas": {
      "APIError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ],
        "example": {
          "error": {
            "code": "not_found",
            "message": "Session not found"
          }
        }
      },
      "WebhookEventPayload": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "event": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "created_at": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "properties": {
              "session": {
                "$ref": "#/components/schemas/SessionDetail"
              }
            },
            "required": [
              "session"
            ]
          }
        },
        "required": [
          "id",
          "event",
          "created_at",
          "data"
        ],
        "example": {
          "id": "evt_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "event": "session.completed",
          "created_at": "2025-04-21T17:14:08.120Z",
          "data": {
            "session": {
              "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "title": "Weekly product sync",
              "timestamp": "2025-04-18T16:00:00.000Z",
              "duration_seconds": 1812,
              "type": "meeting",
              "platform": "zoom",
              "language": "en",
              "summary": "## Overview\nQuarterly roadmap review…",
              "notes": null,
              "tags": [
                "work",
                "roadmap"
              ],
              "favorite": false
            }
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "enum": [
          "session.completed",
          "session.updated",
          "session.deleted",
          "session.action_items.updated"
        ]
      },
      "SessionDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          },
          "duration_seconds": {
            "type": "number"
          },
          "type": {
            "type": "string"
          },
          "platform": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "favorite": {
            "type": "boolean"
          },
          "folder_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "action_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActionItem"
            }
          },
          "action_items_version": {
            "type": "number"
          },
          "phone": {
            "$ref": "#/components/schemas/PhoneMetadata"
          }
        },
        "required": [
          "id",
          "title",
          "timestamp",
          "duration_seconds",
          "type",
          "platform",
          "language",
          "summary",
          "notes"
        ],
        "example": {
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "title": "Weekly product sync",
          "timestamp": "2025-04-18T16:00:00.000Z",
          "duration_seconds": 1812,
          "type": "meeting",
          "platform": "zoom",
          "language": "en",
          "summary": "## Overview\nQuarterly roadmap review with the retail-media pod.\n\n## Key points\n- Shifting Q3 priorities to Instacart rollout\n- Blocked on legal review for sponsored SKUs\n\n## Action items\n- [ ] JR: send legal the draft spec (Fri)\n- [ ] Sam: mock up the new placement UI",
          "notes": "Pre-read doc in Notion; follow up on legal timeline.",
          "tags": [
            "work",
            "roadmap"
          ],
          "favorite": false
        }
      },
      "ActionItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "completed": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "agent",
              "user"
            ]
          },
          "assignee": {
            "type": [
              "string",
              "null"
            ]
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "number"
          },
          "updated_at": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "text",
          "completed",
          "source",
          "created_at",
          "updated_at"
        ],
        "example": {
          "id": "ai_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "text": "Send legal the draft spec by Friday",
          "completed": false,
          "source": "agent",
          "assignee": "JR",
          "due_date": "2025-04-25",
          "created_at": 1745258048120,
          "updated_at": 1745258048120
        }
      },
      "PhoneMetadata": {
        "type": "object",
        "properties": {
          "direction": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "inbound",
              "outbound",
              null
            ]
          },
          "to_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "from_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "ringing",
              "in_progress",
              "completed",
              "missed",
              "failed",
              null
            ]
          },
          "ongoing": {
            "type": "boolean"
          },
          "provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "contact_name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "direction",
          "to_number",
          "from_number",
          "status",
          "ongoing",
          "provider",
          "contact_name"
        ],
        "description": "Call metadata, present only on sessions of type \"phone\". For inbound (bridge) calls the other party is unknown: to_number is the user’s verified caller id and from_number is null.",
        "example": {
          "direction": "outbound",
          "to_number": "+15551234567",
          "from_number": "+15559876543",
          "status": "completed",
          "ongoing": false,
          "provider": "telnyx",
          "contact_name": "Acme Co."
        }
      },
      "EventEnvelope": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "seq": {
            "type": "number"
          },
          "event": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "created_at": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "properties": {
              "session": {
                "$ref": "#/components/schemas/SessionDetail"
              }
            },
            "required": [
              "session"
            ]
          }
        },
        "required": [
          "id",
          "seq",
          "event",
          "created_at",
          "data"
        ],
        "example": {
          "id": "evt_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "seq": 4281,
          "event": "session.completed",
          "created_at": "2025-04-21T17:14:08.120Z",
          "data": {
            "session": {
              "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "title": "Weekly product sync",
              "timestamp": "2025-04-18T16:00:00.000Z",
              "duration_seconds": 1812,
              "type": "meeting",
              "platform": "zoom",
              "language": "en",
              "summary": "## Overview\nQuarterly roadmap review…",
              "notes": null,
              "tags": [
                "work",
                "roadmap"
              ],
              "favorite": false
            }
          }
        }
      },
      "SessionListResponse": {
        "type": "object",
        "properties": {
          "sessions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionListItem"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "sessions",
          "next_cursor",
          "has_more"
        ],
        "example": {
          "sessions": [
            {
              "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "title": "Weekly product sync",
              "timestamp": "2025-04-18T16:00:00.000Z",
              "duration_seconds": 1812,
              "type": "meeting",
              "platform": "zoom"
            },
            {
              "id": "sess_01HX3P8YVJ9WX1MKBQ2NTD5VGH",
              "title": "Customer call — Acme Co.",
              "timestamp": "2025-04-17T20:30:00.000Z",
              "duration_seconds": 2455,
              "type": "meeting",
              "platform": "google_meet"
            }
          ],
          "next_cursor": "2025-04-17T20:30:00.000Z",
          "has_more": true
        }
      },
      "SessionListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          },
          "duration_seconds": {
            "type": "number"
          },
          "type": {
            "type": "string"
          },
          "platform": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "$ref": "#/components/schemas/PhoneMetadata"
          }
        },
        "required": [
          "id",
          "title",
          "timestamp",
          "duration_seconds",
          "type",
          "platform"
        ],
        "example": {
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "title": "Weekly product sync",
          "timestamp": "2025-04-18T16:00:00.000Z",
          "duration_seconds": 1812,
          "type": "meeting",
          "platform": "zoom"
        }
      },
      "FolderListResponse": {
        "type": "object",
        "properties": {
          "folders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Folder"
            }
          }
        },
        "required": [
          "folders"
        ],
        "example": {
          "folders": [
            {
              "id": "fld_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "name": "work",
              "color": "#6D28D9",
              "session_count": 82
            },
            {
              "id": "fld_01HX5S8NV9QR0XDJ2KBC7ZG3HT",
              "name": "personal",
              "color": "#F59E0B",
              "session_count": 14
            }
          ]
        }
      },
      "Folder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "session_count": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "name",
          "color",
          "session_count"
        ],
        "example": {
          "id": "fld_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "name": "work",
          "color": "#6D28D9",
          "session_count": 82
        }
      },
      "CreateFolderRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "color": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          }
        },
        "required": [
          "name"
        ],
        "example": {
          "name": "client-calls",
          "color": "#6D28D9"
        }
      },
      "FolderMembershipResponse": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string"
          },
          "folder_id": {
            "type": "string"
          },
          "folder_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "session_id",
          "folder_id",
          "folder_ids"
        ],
        "example": {
          "session_id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "folder_id": "fld_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "folder_ids": [
            "fld_01HX4R7MT8PQZ6VCN9ABYF2KJM",
            "fld_01HX5S8NV9QR0XDJ2KBC7ZG3HT"
          ]
        }
      },
      "PatchSessionResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "updated_fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "updated_at": {
            "type": "string"
          },
          "action_items_version": {
            "type": "number"
          }
        },
        "required": [
          "id",
          "updated_fields",
          "updated_at"
        ],
        "example": {
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "updated_fields": [
            "tags",
            "favorite",
            "action_items"
          ],
          "updated_at": "2025-04-21T17:14:08.120Z",
          "action_items_version": 3
        }
      },
      "PatchSessionRequest": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 500
          },
          "notes": {
            "type": "string",
            "maxLength": 50000
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 100
            },
            "maxItems": 20
          },
          "favorite": {
            "type": "boolean"
          },
          "action_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActionItemInput"
            },
            "maxItems": 200
          }
        },
        "example": {
          "tags": [
            "work",
            "roadmap"
          ],
          "favorite": true,
          "action_items": [
            {
              "text": "Send legal the draft spec by Friday",
              "assignee": "JR",
              "due_date": "2025-04-25"
            },
            {
              "text": "Mock up the new placement UI",
              "assignee": "Sam"
            }
          ]
        }
      },
      "ActionItemInput": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "completed": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "agent",
              "user"
            ]
          },
          "assignee": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50
          }
        },
        "required": [
          "text"
        ],
        "example": {
          "text": "Send legal the draft spec by Friday",
          "assignee": "JR",
          "due_date": "2025-04-25"
        }
      },
      "ActionItemsResponse": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string"
          },
          "action_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActionItem"
            }
          },
          "version": {
            "type": "number"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "session_id",
          "action_items",
          "version",
          "updated_at"
        ],
        "example": {
          "session_id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "action_items": [
            {
              "id": "ai_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "text": "Send legal the draft spec by Friday",
              "completed": false,
              "source": "agent",
              "assignee": "JR",
              "due_date": "2025-04-25",
              "created_at": 1745258048120,
              "updated_at": 1745258048120
            }
          ],
          "version": 3,
          "updated_at": "2025-04-21T17:14:08.120Z"
        }
      },
      "DeleteSessionResponse": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "deleted",
          "id"
        ],
        "example": {
          "deleted": true,
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM"
        }
      },
      "TranscriptResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "transcript": {
            "type": "string"
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Segment"
            }
          }
        },
        "required": [
          "id",
          "transcript"
        ],
        "example": {
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "transcript": "Josh: Alright, let’s kick off the roadmap review.\nSam: Sure — I sent the draft last night.\nJosh: Great, want to start with retail media?",
          "segments": [
            {
              "speaker": "Josh",
              "start": 12.48,
              "end": 15.92,
              "text": "Alright, let’s kick off the roadmap review."
            },
            {
              "speaker": "Sam",
              "start": 16.04,
              "end": 18.31,
              "text": "Sure — I sent the draft last night."
            },
            {
              "speaker": "Josh",
              "start": 18.5,
              "end": 21.02,
              "text": "Great, want to start with retail media?"
            }
          ]
        }
      },
      "Segment": {
        "type": "object",
        "properties": {
          "speaker": {
            "type": "string"
          },
          "start": {
            "type": "number"
          },
          "end": {
            "type": "number"
          },
          "text": {
            "type": "string"
          }
        },
        "required": [
          "speaker",
          "start",
          "end",
          "text"
        ],
        "example": {
          "speaker": "Josh",
          "start": 12.48,
          "end": 15.92,
          "text": "Alright, let’s kick off the roadmap review."
        }
      },
      "MediaResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "audio_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "video_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "expires_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "audio_url",
          "video_url",
          "expires_at"
        ],
        "example": {
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "audio_url": "https://storage.googleapis.com/table-talk-b87f8.appspot.com/Users/uid_.../audio.m4a?X-Goog-Signature=…",
          "video_url": null,
          "expires_at": "2025-04-21T18:14:08.000Z"
        }
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchResult"
            }
          },
          "total": {
            "type": "number"
          }
        },
        "required": [
          "query",
          "results",
          "total"
        ],
        "example": {
          "query": "retail media strategy",
          "results": [
            {
              "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "title": "Weekly product sync",
              "timestamp": "2025-04-18T16:00:00.000Z",
              "type": "meeting",
              "similarity": 0.87,
              "snippet": "…we want to lead with retail media strategy in Q3, starting with the Instacart rollout…"
            }
          ],
          "total": 1
        }
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string"
          },
          "similarity": {
            "type": "number"
          },
          "snippet": {
            "type": "string"
          },
          "phone": {
            "$ref": "#/components/schemas/PhoneMetadata"
          }
        },
        "required": [
          "id",
          "title",
          "timestamp",
          "type",
          "snippet"
        ],
        "example": {
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "title": "Weekly product sync",
          "timestamp": "2025-04-18T16:00:00.000Z",
          "type": "meeting",
          "similarity": 0.87,
          "snippet": "…we want to lead with retail media strategy in Q3, starting with the Instacart rollout…"
        }
      },
      "SearchRequest": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "minLength": 1
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 20
          },
          "tag_mode": {
            "type": "string",
            "enum": [
              "any",
              "all"
            ]
          }
        },
        "required": [
          "query"
        ],
        "example": {
          "query": "retail media strategy",
          "limit": 10,
          "tags": [
            "work"
          ],
          "tag_mode": "any"
        }
      },
      "StatsResponse": {
        "type": "object",
        "properties": {
          "total_sessions": {
            "type": "number"
          },
          "total_duration_seconds": {
            "type": "number"
          },
          "average_duration_seconds": {
            "type": "number"
          },
          "sessions_by_type": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "sessions_by_platform": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "period": {
            "type": "object",
            "properties": {
              "since": {
                "type": "string"
              },
              "until": {
                "type": "string"
              }
            },
            "required": [
              "since",
              "until"
            ]
          }
        },
        "required": [
          "total_sessions",
          "total_duration_seconds",
          "average_duration_seconds",
          "sessions_by_type",
          "sessions_by_platform",
          "period"
        ],
        "example": {
          "total_sessions": 57,
          "total_duration_seconds": 89432,
          "average_duration_seconds": 1569,
          "sessions_by_type": {
            "meeting": 38,
            "phone": 11,
            "recording": 8
          },
          "sessions_by_platform": {
            "zoom": 22,
            "google_meet": 12,
            "phone": 11,
            "desktop": 8,
            "none": 4
          },
          "period": {
            "since": "2025-03-22T00:00:00.000Z",
            "until": "2025-04-21T00:00:00.000Z"
          }
        }
      },
      "BulkExportResponse": {
        "type": "object",
        "properties": {
          "sessions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BulkExportSession"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "error": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "error"
              ]
            }
          }
        },
        "required": [
          "sessions",
          "errors"
        ],
        "example": {
          "sessions": [
            {
              "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "title": "Weekly product sync",
              "timestamp": "2025-04-18T16:00:00.000Z",
              "duration_seconds": 1812,
              "type": "meeting",
              "platform": "zoom",
              "language": "en",
              "summary": "## Overview\nQuarterly roadmap review…",
              "transcript": "Josh: Alright, let’s kick off the roadmap review.…"
            }
          ],
          "errors": [
            {
              "id": "sess_does_not_exist",
              "error": "not_found"
            }
          ]
        }
      },
      "BulkExportSession": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ]
          },
          "duration_seconds": {
            "type": "number"
          },
          "type": {
            "type": "string"
          },
          "platform": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "transcript": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "$ref": "#/components/schemas/PhoneMetadata"
          }
        },
        "required": [
          "id",
          "title",
          "timestamp",
          "duration_seconds",
          "type",
          "platform",
          "language"
        ],
        "example": {
          "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "title": "Weekly product sync",
          "timestamp": "2025-04-18T16:00:00.000Z",
          "duration_seconds": 1812,
          "type": "meeting",
          "platform": "zoom",
          "language": "en",
          "summary": "## Overview\nQuarterly roadmap review…",
          "transcript": "Josh: Alright, let’s kick off the roadmap review.\nSam: Sure — I sent the draft last night.…"
        }
      },
      "BulkExportRequest": {
        "type": "object",
        "properties": {
          "session_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 50
          },
          "include_transcript": {
            "type": "boolean"
          },
          "include_summary": {
            "type": "boolean"
          }
        },
        "required": [
          "session_ids"
        ],
        "example": {
          "session_ids": [
            "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
            "sess_01HX3P8YVJ9WX1MKBQ2NTD5VGH"
          ],
          "include_transcript": true,
          "include_summary": true
        }
      },
      "AccountResponse": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string"
          },
          "subscription_active": {
            "type": "boolean"
          },
          "session_count": {
            "type": "number"
          }
        },
        "required": [
          "user_id",
          "subscription_active",
          "session_count"
        ],
        "example": {
          "user_id": "uid_V7hTqM3aRe5xJ8pL2nKbS6wY",
          "subscription_active": true,
          "session_count": 421
        }
      },
      "EventsListResponse": {
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventEnvelope"
            }
          },
          "next_cursor": {
            "type": [
              "number",
              "null"
            ]
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "events",
          "next_cursor",
          "has_more"
        ],
        "example": {
          "events": [
            {
              "id": "evt_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "seq": 4281,
              "event": "session.completed",
              "created_at": "2025-04-21T17:14:08.120Z",
              "data": {
                "session": {
                  "id": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM",
                  "title": "Weekly product sync",
                  "timestamp": "2025-04-18T16:00:00.000Z",
                  "duration_seconds": 1812,
                  "type": "meeting",
                  "platform": "zoom",
                  "language": "en",
                  "summary": "## Overview\n…",
                  "notes": null,
                  "tags": [
                    "work"
                  ],
                  "favorite": false
                }
              }
            }
          ],
          "next_cursor": 4281,
          "has_more": false
        }
      },
      "AckEventsResponse": {
        "type": "object",
        "properties": {
          "acknowledged_cursor": {
            "type": "number"
          }
        },
        "required": [
          "acknowledged_cursor"
        ],
        "example": {
          "acknowledged_cursor": 4281
        }
      },
      "AckEventsRequest": {
        "type": "object",
        "properties": {
          "cursor": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "cursor"
        ],
        "example": {
          "cursor": 4281
        }
      },
      "WebhookListResponse": {
        "type": "object",
        "properties": {
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookResponse"
            }
          }
        },
        "required": [
          "webhooks"
        ],
        "example": {
          "webhooks": [
            {
              "id": "wh_01HX4R7MT8PQZ6VCN9ABYF2KJM",
              "url": "https://example.com/wave/webhook",
              "events": [
                "session.completed"
              ],
              "active": true,
              "failure_count": 0,
              "last_delivery_at": "2025-04-21T15:02:11.000Z",
              "last_delivery_status": 200,
              "created_at": "2025-04-20T18:32:14.000Z",
              "updated_at": "2025-04-21T15:02:11.000Z"
            }
          ]
        }
      },
      "WebhookResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            }
          },
          "active": {
            "type": "boolean"
          },
          "failure_count": {
            "type": "number"
          },
          "last_delivery_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_delivery_status": {
            "type": [
              "number",
              "null"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          },
          "secret": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "active",
          "failure_count",
          "last_delivery_at",
          "last_delivery_status",
          "created_at",
          "updated_at"
        ],
        "example": {
          "id": "wh_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "url": "https://example.com/wave/webhook",
          "events": [
            "session.completed",
            "session.updated"
          ],
          "active": true,
          "failure_count": 0,
          "last_delivery_at": "2025-04-21T15:02:11.000Z",
          "last_delivery_status": 200,
          "created_at": "2025-04-20T18:32:14.000Z",
          "updated_at": "2025-04-21T15:02:11.000Z",
          "secret": "whsec_7j3LxGf9qA2pRn5bXcV1MdKt"
        }
      },
      "CreateWebhookRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "minItems": 1
          }
        },
        "required": [
          "url",
          "events"
        ],
        "example": {
          "url": "https://example.com/wave/webhook",
          "events": [
            "session.completed",
            "session.updated"
          ]
        }
      },
      "PatchWebhookRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "minItems": 1
          },
          "active": {
            "type": "boolean"
          }
        },
        "example": {
          "events": [
            "session.completed",
            "session.updated",
            "session.deleted"
          ],
          "active": true
        }
      },
      "WebhookTestResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "event": {
            "type": "string"
          },
          "webhook_id": {
            "type": "string"
          },
          "delivered_at": {
            "type": "string"
          }
        },
        "required": [
          "success",
          "event",
          "webhook_id",
          "delivered_at"
        ],
        "example": {
          "success": true,
          "event": "session.completed",
          "webhook_id": "wh_01HX4R7MT8PQZ6VCN9ABYF2KJM",
          "delivered_at": "2025-04-21T17:14:08.120Z"
        }
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v1/sessions": {
      "get": {
        "summary": "List sessions",
        "description": "List sessions with cursor-based pagination. Only returns completed sessions (with summaries). Response shape is stable; every session includes `id`, `title`, `timestamp`, `duration_seconds`, `type`, and `platform`.",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Max results (default 20, max 100)",
              "example": "20"
            },
            "required": false,
            "description": "Max results (default 20, max 100)",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "ISO 8601 date for pagination (sessions older than this)",
              "example": "2025-04-17T20:30:00.000Z"
            },
            "required": false,
            "description": "ISO 8601 date for pagination (sessions older than this)",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "ISO 8601 date (sessions newer than this)",
              "example": "2025-04-01T00:00:00.000Z"
            },
            "required": false,
            "description": "ISO 8601 date (sessions newer than this)",
            "name": "since",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter by session type (meeting, recording, phone, desktop, import, youtube, podcast)",
              "example": "meeting"
            },
            "required": false,
            "description": "Filter by session type (meeting, recording, phone, desktop, import, youtube, podcast)",
            "name": "type",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter by folder id or case-insensitive name. Restricts results to sessions in that folder. Ideal for default-deny work/personal separation on a single account.",
              "example": "work"
            },
            "required": false,
            "description": "Filter by folder id or case-insensitive name. Restricts results to sessions in that folder. Ideal for default-deny work/personal separation on a single account.",
            "name": "folder",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter by tag (exact, case-sensitive). Repeatable (`?tag=a&tag=b`) or comma-separated (`?tag=a,b`). Pair with `tag_mode=all` to require every tag.",
              "example": "roadmap"
            },
            "required": false,
            "description": "Filter by tag (exact, case-sensitive). Repeatable (`?tag=a&tag=b`) or comma-separated (`?tag=a,b`). Pair with `tag_mode=all` to require every tag.",
            "name": "tag",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "`any` (default) returns sessions matching at least one of the listed tags; `all` requires every listed tag.",
              "example": "any"
            },
            "required": false,
            "description": "`any` (default) returns sessions matching at least one of the listed tags; `all` requires every listed tag.",
            "name": "tag_mode",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Session list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (bad tag_mode, etc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Folder not found (only when `folder` param is passed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "getSessions"
      }
    },
    "/v1/folders": {
      "get": {
        "summary": "List folders",
        "description": "List all folders for the authenticated user. Each folder carries the sessions the user has organized into it in the Wave app. Folders are non-exclusive — a session can belong to many folders at once. Use the folder `id` or `name` with `GET /v1/sessions?folder=…` to restrict a session list to one folder.",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Folder list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FolderListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "getFolders"
      },
      "post": {
        "summary": "Create folder",
        "description": "Create a new folder. Name collisions (case-insensitive) return the existing folder — the endpoint is safe to retry. Requires the `folders:write` scope.",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": [
              "folders:write"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFolderRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Folder created (or returned if it already existed)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Folder"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "postFolders"
      }
    },
    "/v1/sessions/{id}/folders/{folderId}": {
      "post": {
        "summary": "Add session to folder",
        "description": "Link a session to a folder. Folders are non-exclusive — calling this for a session already in the folder is a no-op, and a session can sit in any number of folders simultaneously. Requires the `folders:write` scope.",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": [
              "folders:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Folder ID"
            },
            "required": true,
            "description": "Folder ID",
            "name": "folderId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Session added to folder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FolderMembershipResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session or folder not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "postSessionsByIdFoldersByFolderId"
      },
      "delete": {
        "summary": "Remove session from folder",
        "description": "Unlink a session from a folder. The session itself is not deleted. Requires the `folders:write` scope.",
        "tags": [
          "Folders"
        ],
        "security": [
          {
            "bearerAuth": [
              "folders:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Folder ID"
            },
            "required": true,
            "description": "Folder ID",
            "name": "folderId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Session removed from folder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FolderMembershipResponse"
                }
              }
            }
          },
          "404": {
            "description": "Folder not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "deleteSessionsByIdFoldersByFolderId"
      }
    },
    "/v1/sessions/{id}": {
      "get": {
        "summary": "Get session details",
        "description": "Get detailed information about a specific session, including summary (structured markdown), notes, tags, and favorite flag. Transcript is fetched separately via `/v1/sessions/{id}/transcript`.",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID",
              "example": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Session details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionDetail"
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "getSessionsById"
      },
      "patch": {
        "summary": "Update session",
        "description": "Update session metadata (title, notes, tags, favorite, action_items). At least one field is required. Requires the `sessions:write` scope.\n\n**Action items round-trip.** Pass a full `action_items` array to replace the list. Each item needs `text`; `id`, `source`, `assignee`, `due_date`, and `completed` are optional. Items with a known `id` preserve their original `created_at` and `source`. Omit `id` for new items and the server assigns one.\n\nFor optimistic concurrency on `action_items`, send `If-Match: <action_items_version>`; the request is rejected with `409 conflict` if another writer has bumped the version in between. Skip the header to force-overwrite.",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID",
              "example": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Expected current `action_items_version` (optional). Returns 409 if it disagrees.",
              "example": "2"
            },
            "required": false,
            "description": "Expected current `action_items_version` (optional). Returns 409 if it disagrees.",
            "name": "If-Match",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchSessionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PatchSessionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          },
          "409": {
            "description": "action_items_version conflict (If-Match mismatch)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "patchSessionsById"
      },
      "delete": {
        "summary": "Delete session",
        "description": "Permanently delete a session. Storage files are cleaned up asynchronously.",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:delete"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID",
              "example": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Session deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteSessionResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "deleteSessionsById"
      }
    },
    "/v1/sessions/{id}/action-items": {
      "get": {
        "summary": "Get session action items",
        "description": "Return the structured action_items for a session plus the current `version`. Use the returned `version` as `If-Match` on a subsequent PATCH to reject concurrent edits.",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Action items list + version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionItemsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "getSessionsByIdActionItems"
      }
    },
    "/v1/sessions/{id}/transcript": {
      "get": {
        "summary": "Get session transcript",
        "description": "Returns the full transcript as a flat string plus optional `segments[]` with per-turn speaker labels and start/end times in seconds (floats, not ISO timestamps). Use the flat string for LLM input; use segments for timestamped UIs.",
        "tags": [
          "Transcripts"
        ],
        "security": [
          {
            "bearerAuth": [
              "transcripts:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID",
              "example": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Transcript",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscriptResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "getSessionsByIdTranscript"
      }
    },
    "/v1/sessions/{id}/media": {
      "get": {
        "summary": "Get session media URLs",
        "description": "Get time-limited signed URLs for session audio and video files. URLs expire after 1 hour.",
        "tags": [
          "Media"
        ],
        "security": [
          {
            "bearerAuth": [
              "media:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Session ID",
              "example": "sess_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Session ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Signed media URLs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "getSessionsByIdMedia"
      }
    },
    "/v1/sessions/search": {
      "post": {
        "summary": "Search sessions",
        "description": "Semantic search across all sessions using vector similarity. Each result includes a `snippet` — useful for triage without pulling the full session. Pass `tags` + `tag_mode` to restrict the vector search to sessions carrying specific tags (exact, case-sensitive).",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:search"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          }
        },
        "operationId": "postSessionsSearch"
      }
    },
    "/v1/sessions/stats": {
      "get": {
        "summary": "Get session statistics",
        "description": "Get aggregated session statistics for a date range (default: last 30 days).",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Start date (ISO 8601, default: 30 days ago)",
              "example": "2025-03-22T00:00:00.000Z"
            },
            "required": false,
            "description": "Start date (ISO 8601, default: 30 days ago)",
            "name": "since",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "End date (ISO 8601, default: now)",
              "example": "2025-04-21T00:00:00.000Z"
            },
            "required": false,
            "description": "End date (ISO 8601, default: now)",
            "name": "until",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Session statistics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatsResponse"
                }
              }
            }
          }
        },
        "operationId": "getSessionsStats"
      }
    },
    "/v1/sessions/bulk": {
      "post": {
        "summary": "Bulk export sessions",
        "description": "Fetch multiple sessions by ID in a single request. Max 50 session IDs per request. Set `include_transcript: true` to include full transcripts in the response (requires the `transcripts:read` scope). Ideal for backfilling a knowledge base from history.",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "bearerAuth": [
              "sessions:read"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkExportRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk export results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkExportResponse"
                }
              }
            }
          }
        },
        "operationId": "postSessionsBulk"
      }
    },
    "/v1/account": {
      "get": {
        "summary": "Get account info",
        "description": "Get user profile information including subscription status and session count.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": [
              "account:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Account information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountResponse"
                }
              }
            }
          }
        },
        "operationId": "getAccount"
      }
    },
    "/v1/events": {
      "get": {
        "summary": "List events since cursor",
        "description": "Pull events the caller has not yet acknowledged. The server tracks a per-token cursor so clients do not have to maintain their own dedupe state — the hard-won lesson that motivated this endpoint. Pass `cursor` explicitly to replay from a known point; omit it to resume from the token's last ack.\n\nUse with `POST /v1/events/ack` to advance the cursor. This is a drop-in alternative (or complement) to webhooks for integrators who prefer polling — same event shapes, same ids.\n\nEvent types match the webhooks subscription list: `session.completed`, `session.updated`, `session.deleted`, `session.action_items.updated`.",
        "tags": [
          "Events"
        ],
        "security": [
          {
            "bearerAuth": [
              "events:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Integer seq. Events with seq > cursor are returned. Defaults to the token's last acknowledged position.",
              "example": "4281"
            },
            "required": false,
            "description": "Integer seq. Events with seq > cursor are returned. Defaults to the token's last acknowledged position.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Max events (default 50, max 200)",
              "example": "50"
            },
            "required": false,
            "description": "Max events (default 50, max 200)",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Event list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventsListResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "getEvents"
      }
    },
    "/v1/events/ack": {
      "post": {
        "summary": "Acknowledge events through cursor",
        "description": "Advance the caller's server-side cursor so future `GET /v1/events` calls skip events up to and including `cursor`. Acks are monotonic — a cursor lower than the currently persisted value is a no-op.",
        "tags": [
          "Events"
        ],
        "security": [
          {
            "bearerAuth": [
              "events:read"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AckEventsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cursor advanced",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AckEventsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "postEventsAck"
      }
    },
    "/v1/webhooks": {
      "get": {
        "summary": "List webhooks",
        "description": "List all registered webhooks for the authenticated user.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookListResponse"
                }
              }
            }
          }
        },
        "operationId": "getWebhooks"
      },
      "post": {
        "summary": "Create webhook",
        "description": "Register a new webhook endpoint. The signing secret is only returned once at creation time. Max 5 webhooks per user.\n\n**Events**:\n- `session.completed` — fires once, after a session finishes processing and the summary, notes, and tags are queryable. This is the \"session is ready to use\" event.\n- `session.updated` — session metadata was changed (title, summary, notes, tags, favorite). Fires for edits from any source: the API, mobile app, or web app.\n- `session.action_items.updated` — structured action items on a session were written or edited via PATCH. Carries the full session in `data.session`, including the new `action_items` and `action_items_version`.\n- `session.deleted` — session was deleted (soft-delete or hard-delete of a completed session).",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created (includes secret)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "postWebhooks"
      }
    },
    "/v1/webhooks/{id}": {
      "patch": {
        "summary": "Update webhook",
        "description": "Update webhook URL, events, or active status.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Webhook ID",
              "example": "wh_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Webhook ID",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "patchWebhooksById"
      },
      "delete": {
        "summary": "Delete webhook",
        "description": "Delete a webhook and clean up any pending deliveries.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Webhook ID",
              "example": "wh_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Webhook ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteSessionResponse"
                }
              }
            }
          }
        },
        "operationId": "deleteWebhooksById"
      }
    },
    "/v1/webhooks/{id}/rotate-secret": {
      "post": {
        "summary": "Rotate webhook secret",
        "description": "Regenerate the webhook signing secret. The new secret is returned in the response.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Webhook ID",
              "example": "wh_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Webhook ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Secret rotated (includes new secret)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResponse"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "postWebhooksByIdRotateSecret"
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "summary": "Test webhook",
        "description": "Send a test event to the webhook endpoint and report delivery status.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": [
              "webhooks:manage"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Webhook ID",
              "example": "wh_01HX4R7MT8PQZ6VCN9ABYF2KJM"
            },
            "required": true,
            "description": "Webhook ID",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Test delivery result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResponse"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/APIError"
                }
              }
            }
          }
        },
        "operationId": "postWebhooksByIdTest"
      }
    }
  },
  "webhooks": {},
  "x-scopes": {
    "sessions:read": "List and read session metadata, summaries, and action items.",
    "sessions:write": "Update session titles, notes, tags, and structured action items.",
    "sessions:delete": "Delete a session and its recording.",
    "sessions:search": "Run semantic search across the account's sessions.",
    "transcripts:read": "Read full transcripts with speaker segments.",
    "media:read": "Mint signed URLs for session audio and video.",
    "account:read": "Read the token owner's profile and subscription state.",
    "folders:write": "Create folders and manage session membership.",
    "events:read": "Poll the per-token event cursor feed and acknowledge events.",
    "webhooks:manage": "Register webhook endpoints, rotate secrets, and send test deliveries."
  },
  "externalDocs": {
    "description": "Wave developer portal",
    "url": "https://wave.co/developers"
  }
}
