589 lines
21 KiB
YAML
589 lines
21 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
|
|
responses:
|
|
"200":
|
|
description: Public application configuration
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [messages]
|
|
properties:
|
|
messages:
|
|
type: object
|
|
required: [max_text_length]
|
|
properties:
|
|
max_text_length: {type: integer, minimum: 1, maximum: 10000}
|
|
/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: 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"]}
|