{"openapi":"3.1.0","info":{"title":"MeetingAudio sync server","description":"Accounts, devices and the metadata-tier sync protocol. This document is the wire contract the C# desktop client is generated from.","version":"0.1.0"},"components":{"schemas":{"ma.settingsSection":{"type":"object","additionalProperties":true},"ma.session":{"type":"object","additionalProperties":false,"required":["id","appDisplayName","startedAt","status","mode"],"properties":{"id":{"type":"string","minLength":1},"appDisplayName":{"type":"string"},"displayName":{"type":["string","null"]},"appProcessName":{"type":["string","null"]},"isManual":{"type":"boolean"},"startedAt":{"type":"string","minLength":1},"endedAt":{"type":["string","null"]},"status":{"type":"string","enum":["recording","captured","processing","completed","failed","discarded"]},"mode":{"type":"string","enum":["recordOnly","post","both"]},"tracks":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["track","fileName"],"properties":{"track":{"type":"string","enum":["microphone","system"]},"fileName":{"type":"string"},"format":{"type":"object","additionalProperties":false,"properties":{"sampleRate":{"type":"integer"},"channels":{"type":"integer"},"bitsPerSample":{"type":"integer"},"isFloat":{"type":"boolean"}}},"durationSeconds":{"type":"number"}}}},"mixdownFileName":{"type":["string","null"]},"transcriptFileName":{"type":["string","null"]},"notesFileName":{"type":["string","null"]},"transcriptionProviderId":{"type":["string","null"]},"notesProviderId":{"type":["string","null"]},"speakerNames":{"type":"object","additionalProperties":{"type":"string"}},"error":{"type":["string","null"]}}},"ma.transcript":{"type":"object","additionalProperties":true},"ma.notes":{"type":"object","additionalProperties":false,"required":["markdown"],"properties":{"markdown":{"type":"string"}}},"ma.speaker":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"aliases":{"type":"array","items":{"type":"string"},"default":[]},"aliasIds":{"type":"array","items":{"type":"string"},"default":[]},"notes":{"type":["string","null"]},"createdAt":{"type":["string","null"]},"voiceprint":{"type":["array","null"],"items":{"type":"number"}},"voiceprintSeconds":{"type":["number","null"]},"voiceprintModel":{"type":["string","null"]}}},"ma.sessionVoiceprints":{"type":"object","additionalProperties":true}}},"paths":{"/auth/me":{"get":{"tags":["auth"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"email":{"type":"string"},"displayName":{"type":["null","string"]},"isAdmin":{"type":"boolean"}}}}}}}}},"/auth/logout":{"post":{"tags":["auth"],"responses":{"200":{"description":"Default Response"}}}},"/auth/google":{"get":{"tags":["auth"],"responses":{"200":{"description":"Default Response"}}}},"/auth/google/callback":{"get":{"tags":["auth"],"responses":{"200":{"description":"Default Response"}}}},"/auth/link/exchange":{"post":{"tags":["auth"],"description":"Exchanges a one-time approval code plus the PKCE verifier it was bound to for a long-lived device token.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["code","verifier"],"properties":{"code":{"type":"string","minLength":8,"maxLength":16},"verifier":{"type":"string","minLength":43,"maxLength":128}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["deviceId","deviceToken"],"properties":{"deviceId":{"type":"string"},"deviceToken":{"type":"string"},"accountEmail":{"type":"string"}}}}}}}}},"/admin/allowlist":{"get":{"tags":["admin"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"emails":{"type":"array","items":{"type":"string"}}}}}}}}},"post":{"tags":["admin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","minLength":3}}}}},"required":true},"responses":{"200":{"description":"Default Response"}}}},"/admin/allowlist/{email}":{"delete":{"tags":["admin"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"email","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/devices":{"get":{"tags":["devices"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"devices":{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string"},"name":{"type":"string"},"createdAt":{"type":"string"},"lastSeenAt":{"type":"string"},"revoked":{"type":"boolean"}}}}}}}}}}}},"/devices/{id}/revoke":{"post":{"tags":["devices"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/sync/changes":{"get":{"tags":["sync"],"description":"Every record and tombstone after the cursor, in change order. Calling with `since` also acknowledges that the device has durably consumed everything up to it.","parameters":[{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"since","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":200},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["changes","cursor","hasMore"],"properties":{"changes":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["seq","entityType","entityId","op"],"properties":{"seq":{"type":"integer"},"entityType":{"type":"string","enum":["settings","session","transcript","notes","speaker","sessionVoiceprints"]},"entityId":{"type":"string"},"op":{"type":"string","enum":["upsert","delete"]},"revision":{"type":"integer"},"data":{},"deletedAt":{"type":"string"}}}},"cursor":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}}}}},"/sync/push":{"post":{"tags":["sync"],"description":"Applies changed records. Per record the server accepts and bumps the revision, answers a conflict with its current copy, or — for speakers — performs the directory merge itself.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["records"],"properties":{"records":{"type":"array","maxItems":500,"items":{"type":"object","additionalProperties":false,"required":["entityType","entityId","op","baseRevision"],"properties":{"entityType":{"type":"string","enum":["settings","session","transcript","notes","speaker","sessionVoiceprints"]},"entityId":{"type":"string","minLength":1},"op":{"type":"string","enum":["upsert","delete"]},"baseRevision":{"type":"integer","minimum":0},"data":{"type":"object"}}}}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["results"],"properties":{"results":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["entityType","entityId","status"],"properties":{"entityType":{"type":"string","enum":["settings","session","transcript","notes","speaker","sessionVoiceprints"]},"entityId":{"type":"string"},"status":{"type":"string","enum":["ok","conflict","merged","merge-refused","deleted","invalid"]},"revision":{"type":"integer"},"current":{"type":"object","additionalProperties":false,"properties":{"revision":{"type":"integer"},"data":{}}},"mergedInto":{"type":"string"},"record":{"type":"object","additionalProperties":true},"conflictingSpeakerId":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/sync/sessions/{id}/claim-processing":{"post":{"tags":["sync"],"description":"Claims (or renews) the post-processing lease on a session. Succeeds for the current owner, for an unowned session, or once the previous owner's 24 h lease has expired.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"owningDeviceId":{"type":"string"},"leaseExpiresAt":{"type":"string"},"error":{"type":"string"}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"owningDeviceId":{"type":"string"},"leaseExpiresAt":{"type":"string"},"error":{"type":"string"}}}}}}}}},"/search":{"get":{"tags":["search"],"description":"Full-text search over transcript segments, session titles and notes. Accepts either a device bearer token or a browser session cookie. Results with segment -1 are hits in a session document (title/notes) rather than a spoken line.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":500},"in":"query","name":"q","required":true},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":false,"description":"Only sessions started at or after this instant."},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":false,"description":"Only sessions started at or before this instant."},{"schema":{"type":"string"},"in":"query","name":"speakerId","required":false,"description":"Speaker-directory id; restricts to segments spoken by them."},{"schema":{"type":"string"},"in":"query","name":"app","required":false,"description":"Exact appDisplayName, e.g. \"Microsoft Teams\"."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["results","total","backend"],"properties":{"results":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["sessionId","segment","startMs","endMs","text","highlight"],"properties":{"sessionId":{"type":"string"},"segment":{"type":"integer"},"startMs":{"type":"integer"},"endMs":{"type":"integer"},"track":{"type":["null","string"]},"speakerId":{"type":["null","string"]},"speakerName":{"type":["null","string"]},"text":{"type":"string"},"highlight":{"type":"string","description":"The matching text with <mark> around the hits."},"sessionTitle":{"type":["null","string"]},"app":{"type":["null","string"]},"sessionStartedAt":{"type":["null","string"]}}}},"total":{"type":"integer"},"backend":{"type":"string","enum":["postgres","elasticsearch"]}}}}}}}}},"/admin/search/rebuild":{"post":{"tags":["admin"],"description":"Re-derives the whole search index from Postgres — the recovery story and the upgrade story in one. Safe to run at any time; the index is derived state.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["accounts","sessions","backend"],"properties":{"accounts":{"type":"integer"},"sessions":{"type":"integer"},"backend":{"type":"string"}}}}}}}}},"/calendar/window":{"put":{"tags":["calendar"],"description":"Snapshot-replace of the relay's rolling window: the server deletes its relay-sourced events starting inside [from, to] and stores the payload — moved and cancelled meetings propagate with no delta protocol. Every pushed event must start inside the window, or the next snapshot could not retire it.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["from","to","events"],"properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"events":{"type":"array","maxItems":2000,"items":{"type":"object","additionalProperties":false,"required":["uid","occurrenceStart","subject","start","end"],"properties":{"uid":{"type":"string","minLength":1,"description":"Outlook GlobalAppointmentID or ICS UID — the event's identity."},"occurrenceStart":{"type":"string","format":"date-time","description":"Distinguishes instances of a recurring series."},"subject":{"type":"string"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"organizer":{"type":["null","string"]},"attendees":{"type":["null","array"],"items":{"type":"string"}},"isAllDay":{"type":"boolean","default":false},"isCancelled":{"type":"boolean","default":false},"showsAsFree":{"type":"boolean","default":false}}}}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["stored"],"properties":{"stored":{"type":"integer"}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}}},"/calendar/events":{"get":{"tags":["calendar"],"description":"The shared calendar copy, merged from the relay and the ICS subscription — what every device (and the web app) names meetings from. Accepts either auth.","parameters":[{"schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":true},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["events"],"properties":{"events":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["uid","occurrenceStart","subject","start","end"],"properties":{"uid":{"type":"string","minLength":1,"description":"Outlook GlobalAppointmentID or ICS UID — the event's identity."},"occurrenceStart":{"type":"string","format":"date-time","description":"Distinguishes instances of a recurring series."},"subject":{"type":"string"},"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"organizer":{"type":["null","string"]},"attendees":{"type":["null","array"],"items":{"type":"string"}},"isAllDay":{"type":"boolean","default":false},"isCancelled":{"type":"boolean","default":false},"showsAsFree":{"type":"boolean","default":false},"source":{"type":"string","enum":["relay","ics"]}}}}}}}}}}}},"/calendar/ics":{"post":{"tags":["calendar"],"description":"Subscribes the account to a published-ICS calendar. The URL is a credential (anyone holding it can read the calendar) and is stored encrypted at rest; the server fetches it every 15 minutes and immediately once on subscribe, reporting the outcome.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","minLength":1,"maxLength":2048}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["ok","result"],"properties":{"ok":{"type":"boolean"},"result":{"type":"string"}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["calendar"],"description":"Removes the ICS subscription and every event it fetched.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}}}}},"/audio/{sessionId}":{"put":{"tags":["audio"],"description":"Uploads the session's Opus audio (Content-Type audio/ogg, body streamed). `x-content-sha256` must be the hex SHA-256 of the body; the server hashes what it stores and refuses a mismatch, so retrying the same PUT is the resume story. Audio is immutable: re-uploading identical content is an idempotent success, different content is a 409.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"},"in":"header","name":"x-content-sha256","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["ok","size","sha256"],"properties":{"ok":{"type":"boolean"},"size":{"type":"integer"},"sha256":{"type":"string"}}}}}}}},"get":{"tags":["audio"],"description":"Streams the session's audio to the browser, honouring single-range Range requests with 206 — what <audio> seeking needs. The store is proxied; the bucket is never exposed.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"sessionId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/account/audio-retention":{"get":{"tags":["audio"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["days"],"properties":{"days":{"type":"integer","minimum":0,"maximum":3650,"description":"Delete uploaded audio after this many days; 0 keeps it until the session is deleted."}}}}}}}},"put":{"tags":["audio"],"description":"The account-level retention policy the plan requires so a cloud copy cannot outlive the local pruning rule. Enforced by an hourly purge job.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["days"],"properties":{"days":{"type":"integer","minimum":0,"maximum":3650,"description":"Delete uploaded audio after this many days; 0 keeps it until the session is deleted."}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["days"],"properties":{"days":{"type":"integer","minimum":0,"maximum":3650,"description":"Delete uploaded audio after this many days; 0 keeps it until the session is deleted."}}}}}}}}},"/web/sessions":{"get":{"tags":["web"],"description":"Every synced session, newest first, with which artifacts the server holds.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["sessions"],"properties":{"sessions":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["id","revision","hasAudio"],"properties":{"id":{"type":"string"},"displayName":{"type":["null","string"]},"appDisplayName":{"type":"string"},"startedAt":{"type":"string"},"endedAt":{"type":["null","string"]},"status":{"type":"string"},"owningDeviceId":{"type":["null","string"]},"leaseExpiresAt":{"type":["null","string"]},"revision":{"type":"integer"},"updatedAt":{"type":"string"},"hasAudio":{"type":"boolean"},"hasTranscript":{"type":"boolean"},"hasNotes":{"type":"boolean"}}}}}}}}}}}},"/web/sessions/{id}":{"get":{"tags":["web"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"required":["id","revision","hasAudio"],"properties":{"id":{"type":"string"},"displayName":{"type":["null","string"]},"appDisplayName":{"type":"string"},"startedAt":{"type":"string"},"endedAt":{"type":["null","string"]},"status":{"type":"string"},"owningDeviceId":{"type":["null","string"]},"leaseExpiresAt":{"type":["null","string"]},"revision":{"type":"integer"},"updatedAt":{"type":"string"},"hasAudio":{"type":"boolean"},"hasTranscript":{"type":"boolean"},"hasNotes":{"type":"boolean"}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}},"patch":{"tags":["web"],"description":"Renames a meeting. Field-level last-writer-wins written through the change log — the same write a device rename makes, so every device pulls it. Null clears the typed name (the calendar may re-name the session on its next manifest push).","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["displayName"],"properties":{"displayName":{"type":["string","null"],"minLength":1,"maxLength":500}}}}},"required":true},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"required":["id","revision","hasAudio"],"properties":{"id":{"type":"string"},"displayName":{"type":["null","string"]},"appDisplayName":{"type":"string"},"startedAt":{"type":"string"},"endedAt":{"type":["null","string"]},"status":{"type":"string"},"owningDeviceId":{"type":["null","string"]},"leaseExpiresAt":{"type":["null","string"]},"revision":{"type":"integer"},"updatedAt":{"type":"string"},"hasAudio":{"type":"boolean"},"hasTranscript":{"type":"boolean"},"hasNotes":{"type":"boolean"}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}},"delete":{"tags":["web"],"description":"Deletes a meeting everywhere: the same tombstone cascade a device push performs — manifest, transcript, notes, sidecar, audio blob, search documents — pulled by every device as deletions.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}}},"/web/sessions/{id}/transcript":{"get":{"tags":["web"],"description":"The stored transcript JSON, verbatim — the same document the desktop renders.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}}},"/web/sessions/{id}/notes":{"get":{"tags":["web"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["markdown"],"properties":{"markdown":{"type":"string"}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}}},"/web/speakers":{"get":{"tags":["web"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["speakers"],"properties":{"speakers":{"type":"array","items":{"type":"object","additionalProperties":true,"required":["id","name","revision"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"aliasIds":{"type":"array","items":{"type":"string"}},"notes":{"type":["null","string"]},"revision":{"type":"integer"}}}}}}}}}}}},"/web/speakers/{id}/rename":{"post":{"tags":["web"],"description":"Renames a speaker everywhere. Runs through the same server-side directory machinery as a device push: renaming onto another entry's name merges the two (aliases and voiceprints folded, losing id kept resolvable) unless the voiceprint guard refuses — then both entries stay, reported as merge-refused.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200}}}}},"required":true},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["status","speaker"],"properties":{"status":{"type":"string","enum":["ok","merged","merge-refused"]},"speaker":{"type":"object","additionalProperties":true,"required":["id","name","revision"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"aliasIds":{"type":"array","items":{"type":"string"}},"notes":{"type":["null","string"]},"revision":{"type":"integer"}}},"mergedInto":{"type":"string"},"conflictingSpeakerId":{"type":"string"}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}}},"/web/speakers/{id}/merge":{"post":{"tags":["web"],"description":"Merges speaker {id} into {targetId}: the target keeps its name, gains the source name and aliases as aliases, voiceprints fold by the weighted-mean rule, and the losing id stays resolvable through aliasIds. Refused with 409 when both hold same-model voiceprints that clearly disagree (the guard) — two people who share a name stay two people.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["targetId"],"properties":{"targetId":{"type":"string","minLength":1}}}}},"required":true},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["status","mergedInto","speaker"],"properties":{"status":{"type":"string","enum":["merged"]},"mergedInto":{"type":"string"},"speaker":{"type":"object","additionalProperties":true,"required":["id","name","revision"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"aliasIds":{"type":"array","items":{"type":"string"}},"notes":{"type":["null","string"]},"revision":{"type":"integer"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error","conflictingSpeakerId"],"properties":{"error":{"type":"string"},"conflictingSpeakerId":{"type":"string"}}}}}}}}},"/web/speakers/{id}":{"delete":{"tags":["web"],"description":"Deletes a directory entry with the same tombstone a device push writes; every device drops it.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"string"}}}}}}}}}},"servers":[{"url":"https://sync.ccfolder.com"}],"tags":[{"name":"auth","description":"Sign-in, device link, sessions"},{"name":"admin","description":"Allowlist administration (session cookie, admin only)"},{"name":"devices","description":"Device registry (session cookie)"},{"name":"sync","description":"The sync protocol (device bearer token)"},{"name":"search","description":"Full-text search over transcripts, titles and notes (either auth)"},{"name":"calendar","description":"The calendar relay (device token) and ICS subscription (session cookie)"},{"name":"audio","description":"Audio upload (device token), playback and retention (session cookie)"},{"name":"web","description":"The web app API (session cookie)"}]}