{"openapi":"3.0.3","info":{"title":"Meeting Assistant API","version":"2.0.0","description":"AI-powered meeting assistant providing real-time transcription, speaker diarization,\nand SSE streaming chat with full transcript context.\n\nKey capabilities:\n- SSE streaming chat with reasoning/thinking token support\n  (configured via LLM_MODEL env var)\n- Audio transcription proxy to haiven-transcribe (Canary/Parakeet/Whisper Turbo)\n- Meeting summaries with action items and key decisions\n- Knowledge base search via haiven-knowledge\n- Audio accumulation, re-diarization, and WAV download\n\nThe `/api/chat` endpoint emits Server-Sent Events with separate `reasoning` and `content`\nevent types, enabling collapsible thinking blocks in the UI.\n","contact":{"name":"Haiven Infrastructure","url":"https://meeting.haiven.site"},"license":{"name":"MIT"}},"servers":[{"url":"https://meeting.haiven.site","description":"Production server"},{"url":"http://localhost:3000","description":"Development server"}],"tags":[{"name":"Health","description":"Service and dependency health status"},{"name":"Chat","description":"SSE streaming chat with transcript context"},{"name":"Transcription","description":"Audio transcription proxy"},{"name":"Summary","description":"Meeting summary and question generation"},{"name":"Knowledge","description":"Knowledge base search"},{"name":"Audio","description":"Meeting audio management"}],"paths":{"/api/health":{"get":{"tags":["Health"],"summary":"Health check","description":"Returns service health and dependency status for LiteLLM and haiven-transcribe.","operationId":"getHealth","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"},"example":{"status":"ok","service":"meeting-assistant","dependencies":{"litellm":"ok","haiven-transcribe":"ok","haiven-knowledge":"ok"}}}}}}}},"/api/chat":{"post":{"tags":["Chat"],"summary":"SSE streaming chat","description":"Streams an AI chat response using Server-Sent Events. The full meeting transcript\nis injected as system context on every request.\n\nEvent format:\n```\ndata: {\"type\":\"reasoning\",\"text\":\"...\"}\\n\\n\ndata: {\"type\":\"content\",\"text\":\"...\"}\\n\\n\ndata: {\"type\":\"error\",\"text\":\"...\"}\\n\\n\ndata: [DONE]\\n\\n\n```\n\n- `reasoning` events carry thinking tokens from the configured LLM\n  (shown in collapsible block)\n- `content` events carry the main response text\n- `[DONE]` signals stream end\n- `error` events carry a human-readable error message\n\nSupports AbortController cancellation from the client.\n","operationId":"streamChat","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"},"example":{"messages":[{"role":"user","content":"What were the action items from the meeting?"}],"transcript":"Alice: We need to send the proposal by Friday.\nBob: I'll schedule the follow-up review."}}}},"responses":{"200":{"description":"SSE stream of chat tokens","headers":{"Content-Type":{"schema":{"type":"string","example":"text/event-stream; charset=utf-8"}},"Cache-Control":{"schema":{"type":"string","example":"no-cache"}}},"content":{"text/event-stream":{"schema":{"type":"string","description":"Stream of SSE events. Each event is a JSON object prefixed with `data: `.\nThe stream ends with `data: [DONE]`.\n"}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"LLM gateway error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/transcribe":{"post":{"tags":["Transcription"],"summary":"Transcribe audio chunk","description":"Proxy to haiven-transcribe. Accepts a audio chunk and returns a transcript\nwith optional speaker diarization segments.\n","operationId":"transcribeAudio","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["audio"],"properties":{"audio":{"type":"string","format":"binary","description":"Audio file (webm, wav, mp3, ogg, mp4, flac)"},"engine":{"type":"string","enum":["canary","parakeet","whisper-turbo"],"default":"canary","description":"STT engine to use"},"diarize":{"type":"boolean","default":true,"description":"Enable speaker diarization"}}}}}},"responses":{"200":{"description":"Transcription result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscribeResponse"}}}},"502":{"description":"haiven-transcribe unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/generate-summary":{"post":{"tags":["Summary"],"summary":"Generate meeting summary","description":"Generates an executive summary from the meeting transcript including key points,\naction items with assignees and due dates, and key decisions.\n","operationId":"generateSummary","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SummaryRequest"}}}},"responses":{"200":{"description":"Meeting summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SummaryResponse"}}}},"400":{"description":"Missing transcript","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/generate-questions":{"post":{"tags":["Summary"],"summary":"Generate follow-up questions","description":"Generates suggested follow-up questions based on the meeting transcript.\nUseful for identifying gaps or areas requiring clarification.\n","operationId":"generateQuestions","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SummaryRequest"}}}},"responses":{"200":{"description":"Follow-up questions","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{"type":"string"},"example":["What is the deadline for the proposal review?","Who is the primary contact for the client relationship?"]}}}}}},"400":{"description":"Missing transcript","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/knowledge-search":{"post":{"tags":["Knowledge"],"summary":"Search knowledge base","description":"Searches haiven-knowledge for relevant infrastructure documentation, past lessons\nlearned, and domain knowledge. Returns ranked results with metadata.\n","operationId":"searchKnowledge","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchRequest"},"example":{"query":"GPU memory allocation for vLLM models","limit":5}}}},"responses":{"200":{"description":"Knowledge search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchResponse"}}}},"502":{"description":"haiven-knowledge unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/meeting-audio":{"post":{"tags":["Audio"],"summary":"Save audio chunk","description":"Appends an audio chunk to the accumulated meeting audio buffer.\nChunks are concatenated in order across multiple recording sessions.\n","operationId":"saveMeetingAudio","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["audio"],"properties":{"audio":{"type":"string","format":"binary","description":"Audio chunk (webm format)"}}}}}},"responses":{"200":{"description":"Chunk saved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"saved"},"chunks":{"type":"integer","description":"Total chunks accumulated","example":5}}}}}}}},"get":{"tags":["Audio"],"summary":"Download meeting audio as WAV","description":"Returns the accumulated meeting audio as a WAV file.\nAll saved chunks are merged into a single download.\n","operationId":"downloadMeetingAudio","parameters":[{"name":"action","in":"query","required":true,"schema":{"type":"string","enum":["download"]},"description":"Must be `download`"}],"responses":{"200":{"description":"WAV audio file","headers":{"Content-Type":{"schema":{"type":"string","example":"audio/wav"}},"Content-Disposition":{"schema":{"type":"string","example":"attachment; filename=\"meeting-2026-02-19.wav\""}}},"content":{"audio/wav":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No audio accumulated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/meeting-audio/diarize":{"post":{"tags":["Audio"],"summary":"Re-diarize accumulated audio","description":"Runs pyannote speaker diarization on all accumulated meeting audio.\nReturns updated speaker segments that can be merged with the existing transcript.\n","operationId":"diarizeMeetingAudio","responses":{"200":{"description":"Diarization result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiarizeResponse"}}}},"404":{"description":"No audio to diarize","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"haiven-transcribe diarization unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded","error"],"example":"ok"},"service":{"type":"string","example":"meeting-assistant"},"dependencies":{"type":"object","additionalProperties":{"type":"string","enum":["ok","error","unavailable"]},"example":{"litellm":"ok","haiven-transcribe":"ok","haiven-knowledge":"ok"}}}},"ChatRequest":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","description":"Conversation history","items":{"$ref":"#/components/schemas/ChatMessage"}},"transcript":{"type":"string","description":"Full meeting transcript injected as system context","example":"Alice: We need to finalize the Q1 roadmap.\nBob: Agreed, I'll send the draft by Thursday."},"model":{"type":"string","description":"Override LLM model (defaults to LLM_MODEL env var)","example":"<model-id from LLM_MODEL>"}}},"ChatMessage":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["user","assistant","system"]},"content":{"type":"string"}}},"SseEvent":{"type":"object","description":"A single Server-Sent Event payload (JSON encoded in `data:` field)","required":["type","text"],"properties":{"type":{"type":"string","enum":["reasoning","content","error"],"description":"- `reasoning`: Thinking token from the configured LLM\n  (shown in collapsible block)\n- `content`: Main response text\n- `error`: Error message\n"},"text":{"type":"string","description":"Token or error text"}}},"TranscribeResponse":{"type":"object","properties":{"text":{"type":"string","description":"Full transcript text","example":"We need to send the proposal by Friday."},"segments":{"type":"array","description":"Speaker-segmented transcript (when diarize=true)","items":{"$ref":"#/components/schemas/TranscriptSegment"}},"engine":{"type":"string","description":"STT engine used","example":"canary"}}},"TranscriptSegment":{"type":"object","properties":{"speaker":{"type":"string","example":"Speaker 1"},"start":{"type":"number","format":"float","description":"Segment start time in seconds","example":0.5},"end":{"type":"number","format":"float","description":"Segment end time in seconds","example":4.2},"text":{"type":"string","example":"We need to send the proposal by Friday."}}},"SummaryRequest":{"type":"object","required":["transcript"],"properties":{"transcript":{"type":"string","description":"Full meeting transcript text"},"model":{"type":"string","description":"Override LLM model","example":"<model-id from LLM_MODEL>"}}},"SummaryResponse":{"type":"object","properties":{"summary":{"type":"string","description":"Executive summary paragraph"},"key_points":{"type":"array","items":{"type":"string"},"description":"Bulleted list of main discussion points"},"action_items":{"type":"array","items":{"$ref":"#/components/schemas/ActionItem"}},"decisions":{"type":"array","items":{"type":"string"},"description":"Key decisions made in the meeting"}}},"ActionItem":{"type":"object","properties":{"task":{"type":"string","example":"Send Q1 roadmap draft"},"assignee":{"type":"string","example":"Bob"},"due_date":{"type":"string","example":"Thursday"}}},"KnowledgeSearchRequest":{"type":"object","required":["query"],"properties":{"query":{"type":"string","description":"Natural language search query","example":"GPU memory allocation for vLLM"},"limit":{"type":"integer","default":5,"minimum":1,"maximum":20,"description":"Maximum number of results to return"}}},"KnowledgeSearchResponse":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeResult"}},"count":{"type":"integer","example":3},"duration_ms":{"type":"number","format":"float","example":87.3}}},"KnowledgeResult":{"type":"object","properties":{"content":{"type":"string","description":"Knowledge chunk text (markdown)"},"score":{"type":"number","format":"float","minimum":0,"maximum":1,"description":"Semantic similarity score","example":0.87},"topic":{"type":"string","example":"GPU Layout"},"category":{"type":"string","example":"infrastructure"},"source_file":{"type":"string","example":"MEMORY.md"},"tags":{"type":"array","items":{"type":"string"},"example":["gpu","vllm","memory"]},"doc_type":{"type":"string","example":"memory"},"created_at":{"type":"string","format":"date-time"}}},"DiarizeResponse":{"type":"object","properties":{"segments":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptSegment"}},"speaker_count":{"type":"integer","description":"Number of distinct speakers identified","example":2}}},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message","example":"LiteLLM connection refused"},"detail":{"type":"string","description":"Additional error context (optional)"}}}}}}