Files

683 lines
25 KiB
YAML

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
parameters:
- {name: If-None-Match, in: header, schema: {type: string}}
responses:
"200":
description: Public application configuration
content:
application/json:
schema: {$ref: "#/components/schemas/PublicAppConfigResponse"}
"304": {description: Cached configuration is still current}
/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:
PublicAppConfigResponse:
type: object
additionalProperties: false
required: [auth, operator, messages, consents, attachments, notification, ux, mobile_update]
properties:
auth: {$ref: "#/components/schemas/PublicAuthConfig"}
operator: {$ref: "#/components/schemas/PublicOperatorConfig"}
messages: {$ref: "#/components/schemas/PublicMessagesConfig"}
consents: {$ref: "#/components/schemas/PublicConsentsConfig"}
attachments: {$ref: "#/components/schemas/PublicAttachmentsConfig"}
notification: {$ref: "#/components/schemas/PublicNotificationConfig"}
ux: {$ref: "#/components/schemas/PublicUxConfig"}
mobile_update: {$ref: "#/components/schemas/MobileUpdatePolicy"}
PublicAuthConfig:
type: object
additionalProperties: false
required: [phone_enabled, password_enabled]
properties:
phone_enabled: {type: boolean}
password_enabled: {type: boolean}
PublicOperatorConfig:
type: object
additionalProperties: false
required: [call_phone]
properties:
call_phone: {type: string, minLength: 1, maxLength: 32}
PublicMessagesConfig:
type: object
additionalProperties: false
required: [max_text_length]
properties:
max_text_length: {type: integer, minimum: 1, maximum: 10000}
PublicConsentConfig:
type: object
additionalProperties: false
required: [required, document_url, version]
properties:
required: {type: boolean}
document_url: {type: string, format: uri, minLength: 1}
version: {type: string, minLength: 1, maxLength: 64}
PublicPersonalDataConsentConfig:
allOf:
- {$ref: "#/components/schemas/PublicConsentConfig"}
- type: object
additionalProperties: false
required: [required, document_url, version, privacy_policy_document_url]
properties:
required: {type: boolean}
document_url: {type: string, format: uri, minLength: 1}
version: {type: string, minLength: 1, maxLength: 64}
privacy_policy_document_url: {type: string, format: uri, minLength: 1}
PublicConsentsConfig:
type: object
additionalProperties: false
required: [personal_data, user_agreement, marketing]
properties:
personal_data: {$ref: "#/components/schemas/PublicPersonalDataConsentConfig"}
user_agreement: {$ref: "#/components/schemas/PublicConsentConfig"}
marketing: {$ref: "#/components/schemas/PublicConsentConfig"}
PublicAttachmentsConfig:
type: object
additionalProperties: false
required: [allowed_extensions, allowed_mime_types, max_size_mb]
properties:
allowed_extensions: {type: array, items: {type: string}}
allowed_mime_types: {type: array, items: {type: string}}
max_size_mb: {type: integer, minimum: 1}
PublicNotificationConfig:
type: object
additionalProperties: false
required: [carousel_autoplay_enabled, carousel_autoplay_interval_ms]
properties:
carousel_autoplay_enabled: {type: boolean}
carousel_autoplay_interval_ms: {type: integer, minimum: 1}
PublicUxConfig:
type: object
additionalProperties: false
required: [idle_timeout_minutes]
properties:
idle_timeout_minutes: {type: integer, minimum: 1}
MobileStoreUpdatePolicy:
type: object
additionalProperties: false
required: [enabled, latest_build, minimum_build, latest_version, store_url, release_notes]
properties:
enabled: {type: boolean}
latest_build: {type: [integer, "null"], minimum: 1}
minimum_build: {type: [integer, "null"], minimum: 1}
latest_version: {type: [string, "null"], minLength: 1, maxLength: 64}
store_url: {type: [string, "null"], format: uri, minLength: 1}
release_notes: {type: [string, "null"], maxLength: 4000}
MobileUpdatePolicy:
type: object
additionalProperties: false
required: [google_play, rustore, app_store]
properties:
google_play: {$ref: "#/components/schemas/MobileStoreUpdatePolicy"}
rustore: {$ref: "#/components/schemas/MobileStoreUpdatePolicy"}
app_store: {$ref: "#/components/schemas/MobileStoreUpdatePolicy"}
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: 10000}
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"]}