openapi: 3.1.0 info: title: HAN Chat API version: 1.0.0 servers: - url: / paths: /health/live: get: operationId: healthLive responses: "200": {description: Process is live} /health/ready: get: operationId: healthReady responses: "200": {description: Ready or partially degraded} "503": {$ref: "#/components/responses/DependencyUnavailable"} /api/v1/public/app-config: get: operationId: getPublicAppConfig responses: "200": {description: Public application configuration} /api/v1/public/content: get: operationId: getPublicContent parameters: - {name: locale, in: query, schema: {type: string, default: ru}} responses: "200": {description: Active UI content} /api/v1/auth/bootstrap: post: operationId: bootstrap security: [{bearerAuth: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/BootstrapRequest"} responses: "200": {description: Local user resolved} "401": {$ref: "#/components/responses/Unauthorized"} /api/v1/consents: post: operationId: recordConsents security: [{bearerAuth: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/ConsentsRequest"} responses: "201": {description: Immutable consent records saved} /api/v1/analytics/session-start: post: operationId: startUxSession security: [{bearerAuth: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/SessionStartRequest"} responses: "201": {description: UX session created} /api/v1/me: get: operationId: getCurrentProfile security: [{bearerAuth: []}] responses: "200": {description: Block profile} /api/v1/me/documents: get: operationId: listDocuments security: [{bearerAuth: []}] responses: "200": {description: Cursor-paginated documents} /api/v1/documents/{document_id}: parameters: - {$ref: "#/components/parameters/DocumentId"} get: operationId: getDocument security: [{bearerAuth: []}] responses: "200": {description: Document metadata} "404": {$ref: "#/components/responses/NotFound"} /api/v1/documents/{document_id}/download-url: parameters: - {$ref: "#/components/parameters/DocumentId"} get: operationId: getDocumentDownloadUrl security: [{bearerAuth: []}] responses: "200": {description: Short-lived presigned GET} /api/v1/dialogs: post: operationId: createDialog security: [{bearerAuth: []}] parameters: - {$ref: "#/components/parameters/IdempotencyKey"} responses: "200": {description: Existing active dialog} "201": {description: New active dialog} get: operationId: listDialogs security: [{bearerAuth: []}] responses: "200": {description: Cursor-paginated dialogs} /api/v1/dialogs/{dialog_id}: parameters: - {$ref: "#/components/parameters/DialogId"} get: operationId: getDialog security: [{bearerAuth: []}] responses: "200": {description: Dialog summary} "404": {$ref: "#/components/responses/NotFound"} /api/v1/dialogs/{dialog_id}/messages: parameters: - {$ref: "#/components/parameters/DialogId"} get: operationId: listMessages security: [{bearerAuth: []}] parameters: - {name: after, in: query, schema: {type: string}} - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 50}} responses: "200": {description: Message history or polling delta} post: operationId: sendMessage security: [{bearerAuth: []}] parameters: - {$ref: "#/components/parameters/IdempotencyKey"} requestBody: required: true content: application/json: schema: oneOf: - {$ref: "#/components/schemas/TextMessageRequest"} - {$ref: "#/components/schemas/FileMessageRequest"} discriminator: {propertyName: content_kind} responses: "201": {description: Safety-allowed and delivered message} "422": {description: Message blocked, content redacted} "503": {$ref: "#/components/responses/DependencyUnavailable"} /api/v1/dialogs/{dialog_id}/attachments/init: parameters: - {$ref: "#/components/parameters/DialogId"} post: operationId: initAttachment security: [{bearerAuth: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/AttachmentInitRequest"} responses: "201": {description: Upload metadata and presigned PUT} /api/v1/dialogs/{dialog_id}/attachments/{attachment_id}/complete: parameters: - {$ref: "#/components/parameters/DialogId"} - {$ref: "#/components/parameters/AttachmentId"} post: operationId: completeAttachment security: [{bearerAuth: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/ChecksumRequest"} responses: "200": {description: Upload metadata verified} /api/v1/dialogs/{dialog_id}/attachments/{attachment_id}/download-url: parameters: - {$ref: "#/components/parameters/DialogId"} - {$ref: "#/components/parameters/AttachmentId"} get: operationId: getAttachmentDownloadUrl security: [{bearerAuth: []}] responses: "200": {description: Audited short-lived presigned GET} /api/v1/public/notifications: get: operationId: listGuestNotifications responses: "200": description: Server-sorted active guest campaigns content: application/json: schema: {$ref: "#/components/schemas/NotificationList"} /api/v1/public/notification-types: get: operationId: getNotificationCatalog parameters: - {name: If-None-Match, in: header, schema: {type: string}} responses: "200": {description: Public catalog with ETag} "304": {description: Catalog is unchanged} /api/v1/notifications: get: operationId: listNotifications security: [{bearerAuth: []}] parameters: - {name: place, in: query, required: true, schema: {type: string, enum: [home, center]}} responses: "200": description: Server-sorted personal notifications content: application/json: schema: {$ref: "#/components/schemas/NotificationList"} /api/v1/notifications/counter: get: operationId: getNotificationCounter security: [{bearerAuth: []}] responses: "200": description: Unread count in the center window content: application/json: schema: {$ref: "#/components/schemas/NotificationCounter"} /api/v1/notifications/{notification_id}: parameters: - {$ref: "#/components/parameters/NotificationId"} get: operationId: getNotification security: [{bearerAuth: []}] responses: "200": {description: Active notification detail} "404": {$ref: "#/components/responses/NotFound"} /api/v1/notifications/{notification_id}/read: parameters: - {$ref: "#/components/parameters/NotificationId"} post: operationId: readNotification security: [{bearerAuth: []}] responses: "200": {description: Current notification state} "409": {$ref: "#/components/responses/Conflict"} /api/v1/notifications/{notification_id}/hide: parameters: - {$ref: "#/components/parameters/NotificationId"} post: operationId: hideNotification security: [{bearerAuth: []}] responses: "200": {description: Current notification state} "409": {$ref: "#/components/responses/Conflict"} /api/v1/notifications/{notification_id}/buttons/{button_code}: parameters: - {$ref: "#/components/parameters/NotificationId"} - {name: button_code, in: path, required: true, schema: {type: string}} post: operationId: pressNotificationButton security: [{bearerAuth: []}] responses: "200": {description: Current notification state} "409": {$ref: "#/components/responses/Conflict"} "422": {$ref: "#/components/responses/Unprocessable"} /api/v1/notifications/{notification_id}/cta: parameters: - {$ref: "#/components/parameters/NotificationId"} post: operationId: invokeNotificationCta security: [{bearerAuth: []}] responses: "200": {description: CTA result and current state} "409": {$ref: "#/components/responses/Conflict"} /api/v1/notifications/{notification_id}/documents/{document_id}/download-url: parameters: - {$ref: "#/components/parameters/NotificationId"} - {$ref: "#/components/parameters/DocumentId"} get: operationId: getNotificationDocumentDownloadUrl security: [{bearerAuth: []}] responses: "200": {description: Audited short-lived presigned GET} "404": {$ref: "#/components/responses/NotFound"} /api/v1/uploads/init: post: operationId: initClientUpload security: [{bearerAuth: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/UploadInitRequest"} responses: "201": {description: Upload draft and presigned PUT} /api/v1/uploads/{draft_id}/complete: parameters: - {$ref: "#/components/parameters/DraftId"} post: operationId: completeClientUpload security: [{bearerAuth: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/ChecksumRequest"} responses: "200": {description: Current scan state} /api/v1/uploads: get: operationId: listClientUploads security: [{bearerAuth: []}] parameters: - {name: context_type, in: query, required: true, schema: {type: string, enum: [notification]}} - {name: context_id, in: query, required: true, schema: {type: string, format: uuid}} responses: "200": {description: Upload drafts for context} /api/v1/uploads/{draft_id}: parameters: - {$ref: "#/components/parameters/DraftId"} delete: operationId: deleteClientUpload security: [{bearerAuth: []}] responses: "204": {description: Draft discarded} /internal/notifications/v1/notifications: post: operationId: createNotification security: [{serviceBearer: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/NotificationCreateRequest"} responses: "200": {description: Idempotent duplicate} "201": {description: Notification created} "409": {$ref: "#/components/responses/Conflict"} /internal/notifications/v1/notifications/cancel: post: operationId: cancelNotification security: [{serviceBearer: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/NotificationCancelRequest"} responses: "200": {description: Notification closed or already closed} "404": {$ref: "#/components/responses/NotFound"} /internal/openlines/v1/inbox: post: operationId: applyOpenLinesInbox security: [{serviceBearer: []}] requestBody: required: true content: application/json: schema: {$ref: "#/components/schemas/OpenLinesInbox"} responses: "200": {description: Duplicate acknowledged} "201": {description: Event applied} /internal/settings/v1/otp: get: operationId: getOtpSettings security: [{serviceBearer: []}] responses: "200": description: Product OTP limits and cache metadata content: application/json: schema: {$ref: "#/components/schemas/OtpSettingsResponse"} "304": {description: Cached settings are still current} "503": {$ref: "#/components/responses/DependencyUnavailable"} components: securitySchemes: bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT} serviceBearer: {type: http, scheme: bearer} parameters: DialogId: {name: dialog_id, in: path, required: true, schema: {type: string, format: uuid}} AttachmentId: {name: attachment_id, in: path, required: true, schema: {type: string, format: uuid}} DocumentId: {name: document_id, in: path, required: true, schema: {type: string, format: uuid}} NotificationId: {name: notification_id, in: path, required: true, schema: {type: string, format: uuid}} DraftId: {name: draft_id, in: path, required: true, schema: {type: string, format: uuid}} IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, minLength: 1, maxLength: 128}} responses: Unauthorized: description: JWT is absent, invalid or expired content: {application/json: {schema: {$ref: "#/components/schemas/ErrorEnvelope"}}} NotFound: description: Resource absent or owned by another user content: {application/json: {schema: {$ref: "#/components/schemas/ErrorEnvelope"}}} DependencyUnavailable: description: Required dependency is unavailable content: {application/json: {schema: {$ref: "#/components/schemas/ErrorEnvelope"}}} Conflict: description: Notification state or idempotency conflict content: {application/json: {schema: {$ref: "#/components/schemas/ErrorEnvelope"}}} Unprocessable: description: Catalog action is not allowed content: {application/json: {schema: {$ref: "#/components/schemas/ErrorEnvelope"}}} schemas: OtpSettingsResponse: type: object additionalProperties: false required: - max_send_attempts_per_24h - min_seconds_between_attempts - max_verify_attempts - code_length - ttl_seconds - sms_order_timeout_ms - version - cache_ttl_seconds properties: max_send_attempts_per_24h: {type: integer, minimum: 1} min_seconds_between_attempts: {type: integer, minimum: 0} max_verify_attempts: {type: integer, minimum: 1} code_length: {type: integer, minimum: 4, maximum: 10} ttl_seconds: {type: integer, minimum: 60, maximum: 900, multipleOf: 60} sms_order_timeout_ms: {type: integer, minimum: 1} version: {type: string, minLength: 1, maxLength: 64} cache_ttl_seconds: {type: integer, minimum: 1} ErrorEnvelope: type: object required: [error] properties: error: type: object required: [code, message, request_id, details] properties: code: {type: string} message: {type: string} request_id: {type: string} details: {type: object} NotificationCounter: type: object additionalProperties: false required: [unread_count] properties: unread_count: {type: integer, minimum: 0} NotificationList: type: object additionalProperties: false required: [items] properties: items: type: array items: {$ref: "#/components/schemas/Notification"} Notification: type: object required: [id, notification_type, notification_datetime, header] properties: id: {type: string, format: uuid} notification_type: {type: string} notification_datetime: {type: string, format: date-time} header: {type: string} text: {type: [string, "null"]} instruction_url: {type: [string, "null"], format: uri} instruction_open_mode: {type: [string, "null"], enum: [new_tab, null]} lifecycle_status: {type: string, enum: [active, closed]} visibility: {type: string, enum: [visible, hidden]} is_read: {type: boolean} details: {type: [object, "null"]} NotificationCreateRequest: type: object additionalProperties: false required: [user_id, notification_type, source, external_id, notification_datetime, header] properties: user_id: {type: string, format: uuid} notification_type: {type: string, maxLength: 32} source: {type: string, maxLength: 32} external_id: {type: string, maxLength: 128} notification_datetime: {type: string, format: date-time} header: {type: string, maxLength: 255} text: {type: [string, "null"], maxLength: 1024} priority_override: {type: [integer, "null"]} date_expired: {type: [string, "null"], format: date-time} price: {type: [number, "null"]} old_price: {type: [number, "null"]} payment_url: {type: [string, "null"], format: uri} chat_message_text: {type: [string, "null"], maxLength: 1024} details: {type: [object, "null"]} NotificationCancelRequest: type: object additionalProperties: false required: [source, external_id, close_reason] properties: source: {type: string} external_id: {type: string} close_reason: {type: string, enum: [cancelled, paid]} UploadInitRequest: type: object additionalProperties: false required: [context_type, context_id, file_name, mime_type, size_bytes] properties: context_type: {const: notification} context_id: {type: string, format: uuid} file_name: {type: string, maxLength: 255} mime_type: {type: string, maxLength: 128} size_bytes: {type: integer, minimum: 1} ConsentChoice: type: object additionalProperties: false required: [accepted, version] properties: accepted: {type: boolean} version: {type: string, maxLength: 64} ConsentSet: type: object additionalProperties: false required: [personal_data, user_agreement, marketing] properties: personal_data: {$ref: "#/components/schemas/ConsentChoice"} user_agreement: {$ref: "#/components/schemas/ConsentChoice"} marketing: {$ref: "#/components/schemas/ConsentChoice"} Device: type: object additionalProperties: false required: [platform, app_version] properties: platform: {type: string, enum: [ios, android, web]} app_version: {type: string, maxLength: 64} device_id: {type: [string, "null"], maxLength: 255} BootstrapRequest: type: object additionalProperties: false required: [consents, device] properties: consents: {$ref: "#/components/schemas/ConsentSet"} device: {$ref: "#/components/schemas/Device"} ConsentsRequest: type: object additionalProperties: false required: [consents] properties: {consents: {$ref: "#/components/schemas/ConsentSet"}} SessionStartRequest: type: object additionalProperties: false required: [start_reason, device] properties: start_reason: {type: string, enum: [first_launch, cold_start, idle_timeout]} device: {$ref: "#/components/schemas/Device"} TextMessageRequest: type: object additionalProperties: false required: [content_kind, text] properties: content_kind: {const: text} text: {type: string, minLength: 1, maxLength: 4000} FileMessageRequest: type: object additionalProperties: false required: [content_kind, attachment_id, checksum] properties: content_kind: {const: file} attachment_id: {type: string, format: uuid} checksum: {type: string, pattern: "^sha256:[0-9a-f]{64}$"} AttachmentInitRequest: type: object additionalProperties: false required: [file_name, mime_type, size_bytes] properties: file_name: {type: string, minLength: 1, maxLength: 255} mime_type: {type: string, minLength: 1, maxLength: 128} size_bytes: {type: integer, minimum: 1} ChecksumRequest: type: object additionalProperties: false required: [checksum] properties: checksum: {type: string, pattern: "^sha256:[0-9a-f]{64}$"} OpenLinesInbox: type: object additionalProperties: false required: [event_id, event_type, external_chat_id, occurred_at] properties: event_id: {type: string, minLength: 1, maxLength: 255} event_type: {type: string, enum: [message.new, dialog.closed]} external_chat_id: {type: string, format: uuid} bitrix_message_id: {type: [string, "null"], maxLength: 255} occurred_at: {type: string, format: date-time} message: {type: [object, "null"]}