asyncapi: 2.6.0 info: title: EUSRR Realtime WebSocket API version: 1.4.0 description: | Отдельная AsyncAPI-спецификация для WebSocket-контура EUSRR. HTTP API продолжает документироваться через OpenAPI в /api/schema/. Этот документ описывает только двусторонний realtime-канал /ws/. Аутентификация: - browser: одноразовый ?ticket= из POST /api/auth/ws-ticket/ - non-browser: заголовок Authorization: Bearer defaultContentType: application/json servers: development: url: localhost:8000/ws/ protocol: ws description: Локальная разработка security: - websocketTicket: [] - bearerAuth: [] production: url: api.example.com/ws/ protocol: wss description: Продакшен через TLS security: - websocketTicket: [] - bearerAuth: [] channels: userRealtime: address: /ws/ description: | Универсальное пользовательское соединение для всех real-time сценариев: - сообщения и список чатов - уведомления и unread count - typing indicators - реакции и голосования - procurement updates - статусы commerce-интеграций и фоновых синхронизаций - подписки на диапазоны календаря и calendar snapshots publish: operationId: clientAction summary: Команды от клиента к серверу message: oneOf: - $ref: '#/components/messages/OpenChatAction' - $ref: '#/components/messages/CloseChatAction' - $ref: '#/components/messages/SendMessageAction' - $ref: '#/components/messages/EditMessageAction' - $ref: '#/components/messages/DeleteMessageAction' - $ref: '#/components/messages/AddReactionAction' - $ref: '#/components/messages/RemoveReactionAction' - $ref: '#/components/messages/TypingAction' - $ref: '#/components/messages/StopTypingAction' - $ref: '#/components/messages/VotePollAction' - $ref: '#/components/messages/MarkReadAction' - $ref: '#/components/messages/CalendarSubscribeAction' - $ref: '#/components/messages/CalendarUnsubscribeAction' - $ref: '#/components/messages/PongAction' subscribe: operationId: serverEvent summary: События от сервера клиенту message: oneOf: - $ref: '#/components/messages/PingEvent' - $ref: '#/components/messages/ErrorEvent' - $ref: '#/components/messages/ChatOpenedEvent' - $ref: '#/components/messages/ChatClosedEvent' - $ref: '#/components/messages/ChatAccessChangedEvent' - $ref: '#/components/messages/InitialMessagesEvent' - $ref: '#/components/messages/NewMessageEvent' - $ref: '#/components/messages/ListUpdateEvent' - $ref: '#/components/messages/MessageUpdatedEvent' - $ref: '#/components/messages/MessageDeletedEvent' - $ref: '#/components/messages/ReactionAddedEvent' - $ref: '#/components/messages/ReactionRemovedEvent' - $ref: '#/components/messages/TypingStartEvent' - $ref: '#/components/messages/TypingStopEvent' - $ref: '#/components/messages/PollUpdateEvent' - $ref: '#/components/messages/MarkedReadEvent' - $ref: '#/components/messages/NotificationEvent' - $ref: '#/components/messages/UnreadCountEvent' - $ref: '#/components/messages/NotificationReadEvent' - $ref: '#/components/messages/NotificationsReadAllEvent' - $ref: '#/components/messages/ProcurementUpdateEvent' - $ref: '#/components/messages/CommerceConnectionUpdatedEvent' - $ref: '#/components/messages/TaskBoardUpdateEvent' - $ref: '#/components/messages/CalendarUpdateEvent' - $ref: '#/components/messages/CalendarSnapshotEvent' - $ref: '#/components/messages/CalendarErrorEvent' components: securitySchemes: websocketTicket: type: apiKey in: query name: ticket description: Одноразовый билет со сроком жизни 30 секунд. bearerAuth: type: http scheme: bearer bearerFormat: JWT schemas: ChatId: type: integer minimum: 1 MessageId: type: integer minimum: 1 UserId: type: integer minimum: 1 Emoji: type: string minLength: 1 GenericMessagePayload: type: object description: | Сериализованное сообщение чата. Полный набор полей зависит от communications.serialization.serialize_message. properties: id: type: integer content: type: string nullable: true mentions: type: array items: $ref: '#/components/schemas/MessageMention' chat: type: integer author: type: integer created_at: type: string format: date-time required: - id additionalProperties: true MessageMention: type: object required: [user_id, name, start, end] properties: user_id: $ref: '#/components/schemas/UserId' name: type: string start: type: integer minimum: 0 end: type: integer minimum: 1 NotificationPayload: type: object description: Сериализованное уведомление пользователя. properties: id: type: integer verb: type: string description: type: string unread: type: boolean action_url: type: string nullable: true data: type: object additionalProperties: true required: - id - verb - unread additionalProperties: true CalendarEventPayload: type: object required: [id, title, start, end, calendar, is_recurring] properties: id: oneOf: - type: integer - type: string title: type: string description: type: string start: type: string format: date-time end: type: string format: date-time calendar: type: integer minimum: 1 event_id: type: integer minimum: 1 rule: type: integer minimum: 1 nullable: true color_event: type: string nullable: true can_edit: type: boolean can_delete: type: boolean is_recurring: type: boolean additionalProperties: true CommerceSyncRunPayload: type: object required: [id, connection, status, processed, created_products, matched_products, stock_snapshots, images, image_errors, sales] properties: id: type: string connection: type: string status: type: string task_id: type: string processed: type: integer created_products: type: integer matched_products: type: integer stock_snapshots: type: integer images: type: integer image_errors: type: integer sales: type: integer error: type: string started_at: type: string format: date-time nullable: true finished_at: type: string format: date-time nullable: true created_at: type: string format: date-time additionalProperties: false CommerceConnectionUpdatePayload: type: object required: [id, status, auto_sync, sync_interval_minutes, updated_at] properties: id: type: string status: type: string account_name: type: string last_error: type: string last_tested_at: type: string format: date-time nullable: true last_sync_started_at: type: string format: date-time nullable: true last_sync_finished_at: type: string format: date-time nullable: true last_stats: type: object additionalProperties: true task_id: type: string auto_sync: type: boolean sync_interval_minutes: type: integer next_sync_at: type: string format: date-time nullable: true updated_at: type: string format: date-time recent_run: oneOf: - $ref: '#/components/schemas/CommerceSyncRunPayload' - type: 'null' additionalProperties: false messages: OpenChatAction: name: OpenChatAction title: Открыть чат payload: type: object required: [action, chat_id] properties: action: type: string enum: [open_chat] chat_id: $ref: '#/components/schemas/ChatId' load_history: type: boolean default: false CloseChatAction: name: CloseChatAction title: Закрыть чат payload: type: object required: [action, chat_id] properties: action: type: string enum: [close_chat] chat_id: $ref: '#/components/schemas/ChatId' SendMessageAction: name: SendMessageAction title: Отправить сообщение payload: type: object required: [action] properties: action: type: string enum: [send_message] content: type: string text: type: string mentioned_user_ids: type: array maxItems: 50 uniqueItems: true items: $ref: '#/components/schemas/UserId' EditMessageAction: name: EditMessageAction title: Редактировать сообщение payload: type: object required: [action, message_id, content] properties: action: type: string enum: [edit_message] message_id: $ref: '#/components/schemas/MessageId' content: type: string mentioned_user_ids: type: array maxItems: 50 uniqueItems: true items: $ref: '#/components/schemas/UserId' DeleteMessageAction: name: DeleteMessageAction title: Удалить сообщение payload: type: object required: [action, message_id] properties: action: type: string enum: [delete_message] message_id: $ref: '#/components/schemas/MessageId' AddReactionAction: name: AddReactionAction title: Добавить реакцию payload: type: object required: [action, message_id, emoji] properties: action: type: string enum: [add_reaction] message_id: $ref: '#/components/schemas/MessageId' emoji: $ref: '#/components/schemas/Emoji' RemoveReactionAction: name: RemoveReactionAction title: Удалить реакцию payload: type: object required: [action, message_id, emoji] properties: action: type: string enum: [remove_reaction] message_id: $ref: '#/components/schemas/MessageId' emoji: $ref: '#/components/schemas/Emoji' TypingAction: name: TypingAction title: Пользователь печатает payload: type: object required: [action] properties: action: type: string enum: [typing] StopTypingAction: name: StopTypingAction title: Пользователь перестал печатать payload: type: object required: [action] properties: action: type: string enum: [stop_typing] VotePollAction: name: VotePollAction title: Голосование в опросе payload: type: object required: [action, poll_id, option_ids] properties: action: type: string enum: [vote_poll] poll_id: type: integer minimum: 1 option_ids: type: array items: type: integer minimum: 1 MarkReadAction: name: MarkReadAction title: Deprecated mark_read payload: type: object required: [action, chat_id] properties: action: type: string enum: [mark_read] chat_id: $ref: '#/components/schemas/ChatId' note: type: string example: Deprecated, используйте HTTP mark-read endpoint CalendarSubscribeAction: name: CalendarSubscribeAction title: Подписаться на диапазон календаря payload: type: object required: [action, subscription_id, start, end] properties: action: type: string enum: [calendar_subscribe] subscription_id: type: string minLength: 1 maxLength: 80 start: type: string format: date-time end: type: string format: date-time calendar_id: type: integer minimum: 1 nullable: true scope: type: string enum: [accessible, all] default: accessible CalendarUnsubscribeAction: name: CalendarUnsubscribeAction title: Отписаться от диапазона календаря payload: type: object required: [action, subscription_id] properties: action: type: string enum: [calendar_unsubscribe] subscription_id: type: string minLength: 1 maxLength: 80 PongAction: name: PongAction title: Ответ на heartbeat payload: type: object required: [action] properties: action: type: string enum: [pong] timestamp: type: string format: date-time PingEvent: name: PingEvent payload: type: object required: [type, timestamp] properties: type: type: string enum: [ping] timestamp: type: string format: date-time ErrorEvent: name: ErrorEvent payload: type: object required: [type, error] properties: type: type: string enum: [error] error: type: string chat_id: $ref: '#/components/schemas/ChatId' ChatOpenedEvent: name: ChatOpenedEvent payload: type: object required: [type, chat_id] properties: type: type: string enum: [chat_opened] chat_id: $ref: '#/components/schemas/ChatId' ChatClosedEvent: name: ChatClosedEvent payload: type: object required: [type, chat_id] properties: type: type: string enum: [chat_closed] chat_id: $ref: '#/components/schemas/ChatId' ChatAccessChangedEvent: name: ChatAccessChangedEvent payload: type: object required: [type, chat_id, can_access] properties: type: type: string enum: [chat_access_changed] chat_id: $ref: '#/components/schemas/ChatId' can_access: type: boolean InitialMessagesEvent: name: InitialMessagesEvent payload: type: object required: [type, chat_id, messages] properties: type: type: string enum: [initial_messages] chat_id: $ref: '#/components/schemas/ChatId' messages: type: array items: $ref: '#/components/schemas/GenericMessagePayload' NewMessageEvent: name: NewMessageEvent payload: type: object required: [type, chat_id, message] properties: type: type: string enum: [new_message] chat_id: $ref: '#/components/schemas/ChatId' message: $ref: '#/components/schemas/GenericMessagePayload' ListUpdateEvent: name: ListUpdateEvent payload: type: object required: [type, chat_id, message] properties: type: type: string enum: [list_update] chat_id: $ref: '#/components/schemas/ChatId' message: $ref: '#/components/schemas/GenericMessagePayload' MessageUpdatedEvent: name: MessageUpdatedEvent payload: type: object required: [type, chat_id, message] properties: type: type: string enum: [message_updated] chat_id: $ref: '#/components/schemas/ChatId' message: $ref: '#/components/schemas/GenericMessagePayload' MessageDeletedEvent: name: MessageDeletedEvent payload: type: object required: [type, chat_id, message_id] properties: type: type: string enum: [message_deleted] chat_id: $ref: '#/components/schemas/ChatId' message_id: $ref: '#/components/schemas/MessageId' ReactionAddedEvent: name: ReactionAddedEvent payload: type: object required: [type, chat_id, message_id, emoji, user_id] properties: type: type: string enum: [reaction_added] chat_id: $ref: '#/components/schemas/ChatId' message_id: $ref: '#/components/schemas/MessageId' emoji: $ref: '#/components/schemas/Emoji' user_id: $ref: '#/components/schemas/UserId' user_name: type: string reactions_summary: type: object additionalProperties: true ReactionRemovedEvent: name: ReactionRemovedEvent payload: type: object required: [type, chat_id, message_id, emoji, user_id] properties: type: type: string enum: [reaction_removed] chat_id: $ref: '#/components/schemas/ChatId' message_id: $ref: '#/components/schemas/MessageId' emoji: $ref: '#/components/schemas/Emoji' user_id: $ref: '#/components/schemas/UserId' reactions_summary: type: object additionalProperties: true TypingStartEvent: name: TypingStartEvent payload: type: object required: [type, chat_id, user_id] properties: type: type: string enum: [typing_start] chat_id: $ref: '#/components/schemas/ChatId' user_id: $ref: '#/components/schemas/UserId' user_name: type: string TypingStopEvent: name: TypingStopEvent payload: type: object required: [type, chat_id, user_id] properties: type: type: string enum: [typing_stop] chat_id: $ref: '#/components/schemas/ChatId' user_id: $ref: '#/components/schemas/UserId' PollUpdateEvent: name: PollUpdateEvent payload: type: object required: [type, chat_id] properties: type: type: string enum: [poll_update] chat_id: $ref: '#/components/schemas/ChatId' poll_id: type: integer message_id: $ref: '#/components/schemas/MessageId' results: type: object additionalProperties: true MarkedReadEvent: name: MarkedReadEvent payload: type: object required: [type, chat_id, last_read_message_id] properties: type: type: string enum: [marked_read] chat_id: $ref: '#/components/schemas/ChatId' last_read_message_id: $ref: '#/components/schemas/MessageId' NotificationEvent: name: NotificationEvent payload: type: object required: [type, notification] properties: type: type: string enum: [notification] notification: $ref: '#/components/schemas/NotificationPayload' UnreadCountEvent: name: UnreadCountEvent payload: type: object required: [type, count] properties: type: type: string enum: [unread_count] count: type: integer minimum: 0 NotificationReadEvent: name: NotificationReadEvent payload: type: object required: [type, notification_id, unread_count] properties: type: type: string enum: [notification_read] notification_id: type: integer unread_count: type: integer minimum: 0 NotificationsReadAllEvent: name: NotificationsReadAllEvent payload: type: object required: [type, notification_ids, unread_count] properties: type: type: string enum: [notifications_read_all] notification_ids: type: array items: type: integer category: type: string nullable: true unread_count: type: integer minimum: 0 ProcurementUpdateEvent: name: ProcurementUpdateEvent payload: type: object required: [type, event, data] properties: type: type: string enum: [procurement_update] event: type: string data: type: object additionalProperties: true CommerceConnectionUpdatedEvent: name: CommerceConnectionUpdatedEvent title: Изменение состояния commerce-интеграции payload: type: object required: [type, connection] properties: type: type: string enum: [commerce_connection_updated] connection: $ref: '#/components/schemas/CommerceConnectionUpdatePayload' additionalProperties: false TaskBoardUpdateEvent: name: TaskBoardUpdateEvent payload: type: object required: [type, event, data] properties: type: type: string enum: [task_board_update] event: type: string data: type: object additionalProperties: true CalendarUpdateEvent: name: CalendarUpdateEvent payload: type: object required: [type, event, data] properties: type: type: string enum: [calendar_update] event: type: string enum: - event_created - event_updated - event_deleted - event_membership_changed - calendar_created - calendar_updated - calendar_deleted - calendar_membership_changed data: type: object required: [action, entity, calendar_id] properties: action: type: string entity: type: string enum: [calendar, event] calendar_id: type: integer minimum: 1 event_id: type: integer minimum: 1 nullable: true changed_fields: type: array items: type: string start: type: string format: date-time nullable: true end: type: string format: date-time nullable: true is_recurring: type: boolean CalendarSnapshotEvent: name: CalendarSnapshotEvent payload: type: object required: [type, reason, data] properties: type: type: string enum: [calendar_snapshot] reason: type: string enum: [subscribed, updated] data: type: object required: [subscription_id, start, end, scope, events] properties: subscription_id: type: string start: type: string format: date-time end: type: string format: date-time calendar_id: type: integer minimum: 1 nullable: true scope: type: string enum: [accessible, all] events: type: array items: $ref: '#/components/schemas/CalendarEventPayload' CalendarErrorEvent: name: CalendarErrorEvent payload: type: object required: [type, code, detail] properties: type: type: string enum: [calendar_error] subscription_id: type: string nullable: true code: type: string enum: [invalid_subscription, subscription_limit, snapshot_failed] detail: type: string