{"openapi":"3.0.3","info":{"title":"Bolrach Chat API","version":"1.5.13","description":"Tenant-keyed chat platform. Authenticate /v1 with Authorization: Bearer sk_live_... SSE stream also accepts ?api_key= for EventSource clients that cannot set headers. Public health: /health and /v1/health."},"servers":[{"url":"https://api.chat.bolrach.com"},{"url":"https://chat.bolrach.com"},{"url":"https://realtime.chat.bolrach.com"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"sk_live_*"}}},"security":[{"apiKey":[]}],"paths":{"/health":{"get":{"summary":"Health","security":[],"responses":{"200":{"description":"ok"}}}},"/v1/health":{"get":{"summary":"Health (v1 alias)","security":[],"responses":{"200":{"description":"ok"}}}},"/v1/me":{"get":{"summary":"Current tenant + usage stats; unread_total when user_ref=","responses":{"200":{"description":"tenant + stats"}}}},"/v1/read":{"post":{"summary":"Mark all memberships read for user_ref (v1.5.7)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_ref"],"properties":{"user_ref":{"type":"string"}}}}}},"responses":{"200":{"description":"updated count"},"400":{"description":"user_ref required"}}}},"/v1/search":{"get":{"summary":"Search messages across tenant channels (q required)","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":30,"maximum":100}}],"responses":{"200":{"description":"hits"},"400":{"description":"q required"}}}},"/v1/channels":{"get":{"summary":"List channels (optional q=, kind=, exclude_kind=, has_pins=, no_pins=, min_pins=, max_pins=, has_topic=, no_topic=, archived_only=, min_members=, max_members=, min_messages=, max_messages=, sort=, limit=, offset=; has_more)","responses":{"200":{"description":"channels"}}},"post":{"summary":"Create channel","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slug","name"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","default":"group"},"topic":{"type":"string"}}}}}},"responses":{"201":{"description":"created"},"409":{"description":"slug exists"}}}},"/v1/channels/{id}":{"get":{"summary":"Get channel","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"channel"},"404":{"description":"not found"}}},"patch":{"summary":"Update channel name/topic or set archived true/false","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"updated"},"404":{"description":"not found"}}},"delete":{"summary":"Hard-delete an archived channel (and its messages/members)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"deleted"},"404":{"description":"not found"},"409":{"description":"not archived"}}}},"/v1/channels/{id}/members/{user_ref}":{"patch":{"summary":"Update member role (member|moderator|admin)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string"}}}}}},"responses":{"200":{"description":"member"},"400":{"description":"bad role"},"404":{"description":"not found"}}},"delete":{"summary":"Remove member by user_ref","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"user_ref","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"removed"},"404":{"description":"not found"}}}},"/v1/channels/{id}/members":{"get":{"summary":"List members (optional q=, role=, exclude_role=, has_display_name=, no_display_name=, has_last_read=, no_last_read=, sort=created|name|role|user_ref|last_read|last_read_asc, limit=, offset=)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"members"}}},"post":{"summary":"Add member","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"added"}}}},"/v1/channels/{id}/messages":{"get":{"summary":"List messages (before_id/after_id, after=, before=, q=, user_ref=, exclude_user_ref=, display_name=, has_attachments, no_attachments, has_reactions, no_reactions, emoji=, has_replies, no_replies, deleted_only, edited_only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"before_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"after_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"q","in":"query","schema":{"type":"string"},"description":"Body search (ILIKE)"}],"responses":{"200":{"description":"messages"}}},"post":{"summary":"Post message (broadcast to SSE)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"created"}}}},"/v1/channels/{id}/messages/{messageId}":{"get":{"summary":"Get a single message","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"message"},"404":{"description":"not found"}}},"patch":{"summary":"Edit message body","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"updated"},"404":{"description":"not found"}}},"delete":{"summary":"Delete a message (moderation)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"deleted"},"404":{"description":"not found"}}}},"/v1/channels/{id}/messages/purge-deleted":{"post":{"summary":"Hard-purge all soft-deleted messages in channel (empty trash) (v1.5.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"purged count"},"404":{"description":"channel not found"}}}},"/v1/channels/{id}/messages/{messageId}/restore":{"post":{"summary":"Restore a soft-deleted message (v1.5.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"restored message"},"404":{"description":"not found or not deleted"}}}},"/v1/channels/{id}/read":{"post":{"summary":"Mark channel read for a user_ref (v1.4.37 unread)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_ref"],"properties":{"user_ref":{"type":"string"},"at":{"type":"string","format":"date-time","description":"optional ISO time; default now"}}}}}},"responses":{"200":{"description":"last_read_at"},"400":{"description":"user_ref required"},"404":{"description":"not a member / channel"}}}},"/v1/channels/{id}/messages/{messageId}/purge":{"post":{"summary":"Hard-delete a soft-deleted message (v1.5.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"purged"},"404":{"description":"not found"},"409":{"description":"not soft-deleted"}}}},"/v1/channels/{id}/stats":{"get":{"summary":"Channel counters: messages, members, pins, deleted, edited (v1.5.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"stats"},"404":{"description":"not found"}}}},"/v1/channels/{id}/pins":{"get":{"summary":"List pinned messages (v1.4.37 convenience)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"pinned messages"},"404":{"description":"not found"}}}},"/v1/channels/{id}/messages/{messageId}/pin":{"post":{"summary":"Pin message (v1.4.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"pinned"},"404":{"description":"not found"}}},"delete":{"summary":"Unpin message (v1.4.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"unpinned"},"404":{"description":"not found"}}}},"/v1/channels/{id}/messages/{messageId}/reactions":{"get":{"summary":"List reactions on a message (v1.4.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"reactions + counts"},"404":{"description":"not found"}}},"post":{"summary":"Add reaction (v1.4.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"already present"},"201":{"description":"created"},"404":{"description":"not found"}}},"delete":{"summary":"Remove reaction (v1.4.7)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"removed"},"404":{"description":"not found"}}}},"/v1/channels/{id}/stream":{"get":{"summary":"Realtime SSE stream (Bearer or ?api_key=)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"api_key","in":"query","schema":{"type":"string"},"description":"For EventSource clients"}],"responses":{"200":{"description":"text/event-stream"}}}},"/v1/admin/tenants":{"post":{"summary":"Provision tenant + first API key (x-admin-secret)","security":[],"responses":{"201":{"description":"tenant + api_key (shown once)"}}}}}}