{
  "openapi": "3.1.0",
  "info": {
    "title": "Support Communication — outbound client webhooks",
    "version": "v1",
    "description": "Контракт запросов, которые Support Communication отправляет на сервер интегратора. Имена в webhooks — идентификаторы контрактов, а не маршруты Support Communication. Адрес приёма задаётся отдельно: outboundUrl канала Open Channel или url активной подписки Event Webhooks. В документе нет API управления настройками.\n\nЭто два разных неподписанных контура. Они не отправляют Authorization, HMAC-подпись, timestamp-заголовок или delivery-id/idempotency-заголовок. Входящий токен Open Channel не добавляется автоматически к outboundUrl. Используйте HTTPS, ограниченный секретный URL приёмника и идемпотентную обработку; URL и персональные данные не следует записывать в открытые логи.\n\nОтдельный механизм подписанных endpoint deliveries и External Bot не входят в этот публичный контракт. Наличие подписи в другом механизме не означает, что подписаны приведённые здесь события."
  },
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "paths": {},
  "security": [],
  "tags": [
    { "name": "open-channel", "description": "Ответы в кастомный канал на настроенный outboundUrl." },
    { "name": "event-webhooks", "description": "Шесть событий диалогов и клиентов на URL отдельной подписки." }
  ],
  "externalDocs": {
    "description": "Руководство для разработчиков",
    "url": "https://supportcom.ru/docs/#webhooks"
  },
  "webhooks": {
    "openChannelMessage": {
      "post": {
        "operationId": "receiveOpenChannelMessage",
        "summary": "Принять исходящее сообщение Open Channel",
        "description": "POST на outboundUrl активного канала. Обычный ответ оператора имеет message.type=text; исходящие сообщения с клавиатурой имеют type=keyboard. Исходящие сообщения также поддерживают photo, sticker, video, audio, document и location; для загруженных файлов выдаётся подписанный URL скачивания. sender отсутствует, если имя отправителя неизвестно. Content-Type: application/json; charset=utf-8.\n\nУспех определяется только HTTP 2xx; тело ответа не используется. HTTP 408, 425 и 429 повторяются в пределах общего бюджета; остальные 4xx завершают доставку без повторов. HTTP 3xx не перенаправляются и, как 5xx/ошибка сети/таймаут, расходуют бюджет повторов. Максимум 3 попытки всего, а не 3 повтора. Повторно передаётся сохранённое тело; для идемпотентности используйте message.id в контексте канала и recipient.id. Exactly-once и порядок доставки не гарантируются. Бюджет 3 попытки относится к штатно обработанным неуспехам; восстановление незавершённой попытки после сбоя может привести к дополнительной повторной отправке.\n\nПо умолчанию таймаут попытки 10 секунд, следующие попытки назначаются через 3 и 6 секунд от начала предыдущей попытки. Это срок доступности в очереди, не точное время отправки: очередь опрашивается раз в 3 секунды, обычные ответы операторов поступают через событийный цикл раз в 2 секунды. Если предыдущая попытка длилась дольше backoff, повтор может стать доступен сразу после её завершения.",
        "tags": ["open-channel"],
        "security": [],
        "x-delivery": {
          "destination": "configured channel outboundUrl",
          "contentType": "application/json; charset=utf-8",
          "applicationHeaders": ["content-type"],
          "signed": false,
          "timeoutMs": 10000,
          "maxAttempts": 3,
          "retryBackoffMs": 3000,
          "retryDelayFromAttemptStartMs": [3000, 6000],
          "queueIntervalMs": 3000,
          "redirect": "manual",
          "permanentHttpStatusRanges": [[400, 407], [409, 424], [426, 428], [430, 499]],
          "retryableHttpStatusCodes": [408, 425, 429],
          "deduplication": "channel + recipient.id + message.id"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/OpenChannelMessage" },
              "examples": {
                "operatorText": { "$ref": "#/components/examples/OpenChannelText" },
                "keyboard": { "$ref": "#/components/examples/OpenChannelKeyboard" }
              }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/Acknowledged" },
          "204": { "description": "Принято без тела ответа; доставка завершена." },
          "2XX": { "description": "Любой другой HTTP 2xx также завершает доставку; тело игнорируется." },
          "3XX": { "$ref": "#/components/responses/RedirectNotFollowed" },
          "408": { "$ref": "#/components/responses/TransientFailure" },
          "425": { "$ref": "#/components/responses/TransientFailure" },
          "429": { "$ref": "#/components/responses/TransientFailure" },
          "4XX": { "$ref": "#/components/responses/PermanentFailure" },
          "5XX": { "$ref": "#/components/responses/TransientFailure" }
        }
      }
    },
    "eventWebhook": {
      "post": {
        "operationId": "receiveEventWebhook",
        "summary": "Принять событие диалога или клиента",
        "description": "POST на url активной Event Webhooks подписки с соответствующим event_name. URL подписки и набор событий настраиваются отдельно; это не outboundUrl канала и не запись в другом разделе Webhook endpoints. Подписка должна быть подготовлена администратором рабочего пространства: отдельный клиентский UI её создания в текущей версии не представлен. Content-Type: application/json; charset=utf-8.\n\nPayload различается по event_name: client_attribute_updated не содержит общего снимка диалога, а chat_finished не содержит agent и вместо него передаёт agents. Это снимок данных на момент формирования доставки, а не журнал изменений с уникальным event id. Поле user_token — не механизм аутентификации: это произвольное значение setUserToken.\n\nЛюбой HTTP 2xx подтверждает приём, в том числе пустой 204. Только для chat_accepted/chat_updated JSON с result=ok (регистр не важен) может дополнить карточку; некорректный JSON или другой result не отменяет HTTP-подтверждение. HTTP 408, 425 и 429 допускают повтор; остальные 4xx постоянные. 3xx не перенаправляются; 3xx/5xx/сеть/таймаут повторяются в пределах 3 попыток всего. Таймаут 10 секунд; повторы назначаются через 5 и 10 секунд от начала предыдущей попытки, с учётом очереди (по умолчанию 3 секунды). Для chat_accepted/chat_finished событийный цикл по умолчанию 2 секунды; остальные события ставятся в очередь соответствующим запросом SDK. Это не SLA по времени доставки.\n\nНет delivery-id, nonce, подписи или timestamp-заголовка. Повторы могут содержать одинаковое тело; универсального ключа дедупликации нет. Для offline_message используйте offline_message_id в контексте подписки. Для остальных событий делайте операции идемпотентными по бизнес-состоянию; fingerprint тела с ограниченным TTL — только эвристика, которая способна объединить два законных одинаковых события. Exactly-once, гарантированный порядок и доставка после исчерпания попыток не обещаются. Бюджет 3 попытки относится к штатно обработанным неуспехам; восстановление незавершённой попытки после сбоя может привести к дополнительной повторной отправке.",
        "tags": ["event-webhooks"],
        "security": [],
        "x-delivery": {
          "destination": "configured event subscription url",
          "contentType": "application/json; charset=utf-8",
          "applicationHeaders": ["content-type"],
          "signed": false,
          "timeoutMs": 10000,
          "maxAttempts": 3,
          "retryBackoffMs": 5000,
          "retryDelayFromAttemptStartMs": [5000, 10000],
          "queueIntervalMs": 3000,
          "redirect": "manual",
          "permanentHttpStatusRanges": [[400, 407], [409, 424], [426, 428], [430, 499]],
          "retryableHttpStatusCodes": [408, 425, 429],
          "deduplication": "no universal delivery identifier",
          "responseEnrichmentEvents": ["chat_accepted", "chat_updated"]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/EventWebhook" },
              "examples": {
                "chatAccepted": { "$ref": "#/components/examples/ChatAccepted" },
                "chatUpdated": { "$ref": "#/components/examples/ChatUpdated" },
                "chatFinished": { "$ref": "#/components/examples/ChatFinished" },
                "clientUpdated": { "$ref": "#/components/examples/ClientUpdated" },
                "clientAttributeUpdated": { "$ref": "#/components/examples/ClientAttributeUpdated" },
                "offlineMessage": { "$ref": "#/components/examples/OfflineMessage" }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "HTTP-подтверждение приёма. Рекомендуемое JSON-тело — {result: ok}; только chat_accepted/chat_updated применяют дополнительные поля обогащения. Тело необязательно для транспортного успеха: другой JSON, текст или пустое тело тоже подтверждают приём.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EventWebhookAcknowledgement" },
                "examples": {
                  "acknowledge": { "$ref": "#/components/examples/Acknowledgement" },
                  "enrichAcceptedOrUpdated": { "$ref": "#/components/examples/Enrichment" }
                }
              }
            }
          },
          "204": { "description": "Принято без тела; обогащение карточки не выполняется." },
          "2XX": { "description": "Любой другой HTTP 2xx подтверждает доставку. Правило обогащения для chat_accepted/chat_updated то же, что у 200." },
          "3XX": { "$ref": "#/components/responses/RedirectNotFollowed" },
          "408": { "$ref": "#/components/responses/TransientFailure" },
          "425": { "$ref": "#/components/responses/TransientFailure" },
          "429": { "$ref": "#/components/responses/TransientFailure" },
          "4XX": { "$ref": "#/components/responses/PermanentFailure" },
          "5XX": { "$ref": "#/components/responses/TransientFailure" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "OpenChannelMessage": {
        "type": "object",
        "required": ["recipient", "message"],
        "properties": {
          "sender": {
            "type": "object",
            "description": "Может отсутствовать, если отправитель неизвестен. ID автора ответа; agent — резервное значение для старых сообщений.",
            "required": ["id", "name"],
            "properties": { "id": { "type": "string" }, "name": { "type": "string" } }
          },
          "recipient": {
            "type": "object",
            "required": ["id"],
            "properties": { "id": { "type": "string", "description": "Внешний идентификатор клиента в этом канале." } }
          },
          "message": {
            "oneOf": [
              { "$ref": "#/components/schemas/OpenChannelTextMessage" },
              { "$ref": "#/components/schemas/OpenChannelKeyboardMessage" },
              { "$ref": "#/components/schemas/OpenChannelMediaMessage" },
              { "$ref": "#/components/schemas/OpenChannelLocationMessage" }
            ],
            "discriminator": {
              "propertyName": "type",
              "mapping": {
                "text": "#/components/schemas/OpenChannelTextMessage",
                "keyboard": "#/components/schemas/OpenChannelKeyboardMessage"
              }
            }
          }
        }
      },
      "OpenChannelMessageFields": {
        "type": "object",
        "required": ["date", "id", "type"],
        "properties": {
          "date": { "type": "integer", "description": "Unix timestamp в секундах." },
          "id": { "type": "string", "description": "ID сообщения, сохраняемый при повторе той же доставки." },
          "text": { "type": "string", "description": "Текст ответа или текстовый fallback клавиатуры." },
          "type": { "type": "string" },
          "multiple": { "type": "boolean", "description": "Передаётся только если задано источником клавиатуры." }
        }
      },
      "OpenChannelTextMessage": {
        "allOf": [
          { "$ref": "#/components/schemas/OpenChannelMessageFields" },
          { "type": "object", "required": ["text"], "properties": { "type": { "const": "text" } } }
        ]
      },
      "OpenChannelKeyboardMessage": {
        "allOf": [
          { "$ref": "#/components/schemas/OpenChannelMessageFields" },
          {
            "type": "object",
            "properties": {
              "type": { "const": "keyboard" },
              "keyboard": {
                "type": "array",
                "maxItems": 20,
                "description": "Исходящая реализация передаёт до 20 кнопок. Массив может отсутствовать — покажите text как fallback. Это не входящий лимит 7 кнопок.",
                "items": {
                  "type": "object",
                  "required": ["id", "text"],
                  "properties": {
                    "id": { "type": "string", "maxLength": 2000, "description": "ID кнопки; при его отсутствии источник может использовать URL или текст." },
                    "text": { "type": "string", "maxLength": 128 },
                    "url": { "type": "string", "maxLength": 2000 }
                  }
                }
              }
            }
          }
        ]
      },
      "OpenChannelMediaMessage": {
        "allOf": [
          { "$ref": "#/components/schemas/OpenChannelMessageFields" },
          { "type": "object", "required": ["file"], "properties": {
            "type": { "enum": ["photo", "sticker", "video", "audio", "document"] },
            "file": { "type": "string" }, "file_name": { "type": "string" }, "file_size": { "type": "number" },
            "mime_type": { "type": "string" }, "thumb": { "type": "string" }, "width": { "type": "number" }, "height": { "type": "number" }
          } }
        ]
      },
      "OpenChannelLocationMessage": {
        "allOf": [
          { "$ref": "#/components/schemas/OpenChannelMessageFields" },
          { "type": "object", "required": ["latitude", "longitude"], "properties": {
            "type": { "const": "location" }, "latitude": { "type": "number", "minimum": -90, "maximum": 90 }, "longitude": { "type": "number", "minimum": -180, "maximum": 180 }
          } }
        ]
      },
      "EventWebhook": {
        "oneOf": [
          { "$ref": "#/components/schemas/ChatAcceptedEvent" },
          { "$ref": "#/components/schemas/ChatUpdatedEvent" },
          { "$ref": "#/components/schemas/ChatFinishedEvent" },
          { "$ref": "#/components/schemas/ClientUpdatedEvent" },
          { "$ref": "#/components/schemas/ClientAttributeUpdatedEvent" },
          { "$ref": "#/components/schemas/OfflineMessageEvent" }
        ],
        "discriminator": {
          "propertyName": "event_name",
          "mapping": {
            "chat_accepted": "#/components/schemas/ChatAcceptedEvent",
            "chat_updated": "#/components/schemas/ChatUpdatedEvent",
            "chat_finished": "#/components/schemas/ChatFinishedEvent",
            "client_updated": "#/components/schemas/ClientUpdatedEvent",
            "client_attribute_updated": "#/components/schemas/ClientAttributeUpdatedEvent",
            "offline_message": "#/components/schemas/OfflineMessageEvent"
          }
        }
      },
      "NumericCompatibilityId": {
        "type": "integer",
        "minimum": 1,
        "maximum": 9007199254740991,
        "description": "Постоянный положительный идентификатор из реестра БД, безопасный для JSON Number. Посетители разделены по организации и подключению канала. Не является ID события доставки."
      },
      "EmptyObject": { "type": "object", "maxProperties": 0, "additionalProperties": false },
      "EventAgent": {
        "type": "object",
        "required": ["id", "name", "email"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "email": { "type": "string", "description": "Адрес из профиля оператора, если он доступен." }
        }
      },
      "EventVisitor": {
        "type": "object",
        "required": ["chats_count", "description", "name", "number", "social"],
        "properties": {
          "chats_count": { "type": "integer", "minimum": 1 },
          "description": { "type": "string" },
          "email": { "type": "string" },
          "name": { "type": "string" },
          "number": { "type": "string", "pattern": "^[0-9]+$", "description": "Строковое представление стабильного числового ID клиента; не его исходный externalId." },
          "phone": { "type": "string" },
          "social": { "type": "object" }
        }
      },
      "EventPage": {
        "type": "object",
        "required": ["url"],
        "properties": { "url": { "type": "string" }, "title": { "type": "string" } },
        "description": "Адрес и известное название страницы клиента."
      },
      "EventSession": {
        "type": "object",
        "required": ["geoip", "ip_addr", "user_agent", "utm", "utm_json"],
        "properties": {
          "geoip": {
            "type": "object",
            "description": "Может быть пустым; доступные значения берутся из геоданных клиента.",
            "properties": {
              "city": { "type": "string" }, "country": { "type": "string" },
              "country_code": { "type": "string" }, "latitude": { "type": "number" },
              "longitude": { "type": "number" }, "organization": { "type": "string" },
              "region": { "type": "string" }, "region_code": { "type": "string" }
            }
          },
          "ip_addr": { "type": "string" },
          "user_agent": { "type": "string" },
          "utm": { "type": "string" },
          "utm_json": { "type": "object", "additionalProperties": { "type": "string" } }
        }
      },
      "ConversationEventFields": {
        "type": "object",
        "required": ["assigned_agent", "chat_id", "event_name", "organization", "page", "session", "status", "tags", "user_token", "visitor", "widget_id"],
        "properties": {
          "assigned_agent": { "type": ["object", "null"] },
          "chat_id": { "$ref": "#/components/schemas/NumericCompatibilityId" },
          "event_name": { "type": "string" },
          "organization": { "type": ["object", "null"] },
          "page": { "oneOf": [{ "$ref": "#/components/schemas/EventPage" }, { "type": "null" }] },
          "session": { "$ref": "#/components/schemas/EventSession" },
          "status": { "type": ["object", "null"] },
          "topic": { "type": "object" },
          "department": { "type": "object" },
          "tags": {
            "type": "array",
            "description": "Только пользовательские теги без двоеточия; id и title содержат один и тот же тег.",
            "items": { "type": "object", "required": ["id", "title"], "properties": { "id": { "type": "string" }, "title": { "type": "string" } } }
          },
          "user_token": { "type": ["string", "null"], "description": "Значение setUserToken или null. Не подтверждает подлинность события." },
          "visitor": { "$ref": "#/components/schemas/EventVisitor" },
          "widget_id": { "type": "string", "description": "ID подключения, ID Open Channel либо строковое имя канала при отсутствии ID." }
        }
      },
      "ChatAcceptedEvent": {
        "description": "Назначение или перевод диалога оператору (в том числе повторное назначение).",
        "allOf": [
          { "$ref": "#/components/schemas/ConversationEventFields" },
          { "type": "object", "required": ["agent", "analytics"], "properties": { "event_name": { "const": "chat_accepted" }, "agent": { "oneOf": [{ "$ref": "#/components/schemas/EventAgent" }, { "type": "null" }] }, "analytics": { "type": "object" } } }
        ]
      },
      "ChatUpdatedEvent": {
        "description": "Изменение контактов, страницы или customData через SDK, а также телефона и тегов в рабочем месте оператора.",
        "allOf": [
          { "$ref": "#/components/schemas/ConversationEventFields" },
          { "type": "object", "required": ["agent", "analytics"], "properties": { "event_name": { "const": "chat_updated" }, "agent": { "oneOf": [{ "$ref": "#/components/schemas/EventAgent" }, { "type": "null" }] }, "analytics": { "type": "object" } } }
        ]
      },
      "ChatFinishedEvent": {
        "description": "Закрытие диалога. Внутренние заметки и события исключены; agents содержит известных авторов ответов и назначенного оператора.",
        "allOf": [
          { "$ref": "#/components/schemas/ConversationEventFields" },
          {
            "type": "object",
            "required": ["agents", "analytics", "chat", "html_messages", "plain_messages"],
            "properties": {
              "event_name": { "const": "chat_finished" },
              "agents": { "type": "array", "items": { "$ref": "#/components/schemas/EventAgent" } },
              "analytics": { "type": ["object", "null"] },
              "chat": {
                "type": "object",
                "required": ["blacklisted", "messages", "rate"],
                "properties": {
                  "blacklisted": { "type": "boolean" },
                  "messages": { "type": "array", "items": { "$ref": "#/components/schemas/TranscriptMessage" } },
                  "rate": { "type": ["number", "string", "object", "null"] }
                }
              },
              "html_messages": { "type": "string", "description": "Экранированная HTML-версия публичной переписки." },
              "plain_messages": { "type": "string", "description": "Строки Agent: / Visitor:, разделённые переводом строки." }
            }
          }
        ]
      },
      "TranscriptMessage": {
        "type": "object",
        "required": ["message", "timestamp", "type"],
        "properties": {
          "message": { "type": "string" },
          "timestamp": { "type": "integer", "description": "Unix timestamp в секундах." },
          "type": { "type": "string", "enum": ["visitor", "agent"] },
          "agent_id": { "type": "string", "description": "Сохранённый автор конкретного ответа; отсутствует в старых сообщениях без этой информации." }
        }
      },
      "ClientUpdatedEvent": {
        "description": "Изменение контактных данных через SDK client-info; analytics не добавляется.",
        "allOf": [
          { "$ref": "#/components/schemas/ConversationEventFields" },
          { "type": "object", "required": ["agent", "client_id"], "properties": { "event_name": { "const": "client_updated" }, "agent": { "oneOf": [{ "$ref": "#/components/schemas/EventAgent" }, { "type": "null" }] }, "client_id": { "$ref": "#/components/schemas/NumericCompatibilityId" } } }
        ]
      },
      "ClientAttributeUpdatedEvent": {
        "type": "object",
        "description": "Отдельная компактная форма. Отправляется при передаче непустых нормализованных attributes, даже если значения совпали с прежними; visitor/chat_id/session/agent здесь не формируются.",
        "required": ["attributes", "client_id", "event_name", "user_token", "widget_id"],
        "properties": {
          "attributes": {
            "type": "object", "minProperties": 1, "maxProperties": 50,
            "propertyNames": { "type": "string", "minLength": 1, "maxLength": 100 },
            "additionalProperties": { "oneOf": [{ "type": "string", "maxLength": 500 }, { "type": "number" }] }
          },
          "client_id": { "$ref": "#/components/schemas/NumericCompatibilityId" },
          "event_name": { "type": "string", "const": "client_attribute_updated" },
          "user_token": { "type": ["string", "null"] },
          "widget_id": { "type": "string" }
        }
      },
      "OfflineMessageEvent": {
        "description": "Сообщение SDK с offlineMessage=true. offline_message_id связан с исходным сообщением и сохраняется при повторе доставки.",
        "allOf": [
          { "$ref": "#/components/schemas/ConversationEventFields" },
          { "type": "object", "required": ["agent", "analytics", "message", "offline_message_id"], "properties": { "event_name": { "const": "offline_message" }, "agent": { "oneOf": [{ "$ref": "#/components/schemas/EventAgent" }, { "type": "null" }] }, "analytics": { "type": "object" }, "message": { "type": "string" }, "offline_message_id": { "type": "string" } } }
        ]
      },
      "Acknowledgement": {
        "type": "object", "required": ["result"],
        "properties": { "result": { "type": "string", "const": "ok" } }
      },
      "EventWebhookAcknowledgement": {
        "type": "object",
        "description": "Рекомендуемый JSON-ответ. Дополнительные поля применяются только для chat_accepted/chat_updated; HTTP 2xx без такого тела всё равно подтверждает доставку.",
        "required": ["result"],
        "properties": {
          "result": { "type": "string", "pattern": "^[oO][kK]$" },
          "contact_info": {
            "type": "object",
            "description": "Непустые name, phone, email и description обновляют карточку. Пропущенные поля сохраняются.",
            "properties": { "name": { "type": "string" }, "phone": { "type": "string" }, "email": { "type": "string" } }
          },
          "custom_data": {
            "type": "array",
            "description": "Объекты сохраняются как custom data; title/key/content включаются в заметку для оператора.",
            "items": { "type": "object", "properties": { "title": { "type": "string" }, "key": { "type": "string" }, "content": { "type": "string" }, "link": { "type": "string" } } }
          },
          "crm_link": { "type": "string", "description": "Сохраняется в метаданных обогащения и заметке." },
          "enable_assign": { "type": "boolean", "description": "Сохраняется как метаданные; само поле не запускает назначение оператора." },
          "page": { "type": "object", "required": ["url"], "properties": { "url": { "type": "string" }, "title": { "type": "string" } }, "description": "Непустой url обновляет page-тег; title сохраняется в метаданных вместе с url." }
        }
      }
    },
    "responses": {
      "Acknowledged": {
        "description": "Доставка подтверждена. JSON {result: ok} — удобный пример, а не обязательное условие: любое тело HTTP 2xx или пустое тело принимается.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Acknowledgement" }, "examples": { "acknowledge": { "$ref": "#/components/examples/Acknowledgement" } } } }
      },
      "PermanentFailure": {
        "description": "Постоянная ошибка приёмника. HTTP 4xx, кроме 408/425/429, завершает доставку без повторов; Retry-After не используется. Тело не определяет решение о повторе.",
        "content": { "text/plain": { "schema": { "type": "string" }, "example": "invalid payload" } }
      },
      "TransientFailure": {
        "description": "Временная ошибка. Повторяется с backoff данного контура, пока не исчерпаны 3 попытки. Тело не определяет решение о повторе.",
        "content": { "text/plain": { "schema": { "type": "string" }, "example": "temporarily unavailable" } }
      },
      "RedirectNotFollowed": {
        "description": "Location не открывается. Ответ считается неуспехом и повторяется на исходный URL в пределах общего бюджета попыток; настройте конечный URL без редиректа."
      }
    },
    "examples": {
      "OpenChannelText": {
        "summary": "Ответ оператора",
        "value": {
          "sender": { "id": "agent", "name": "Мария" },
          "recipient": { "id": "customer_42" },
          "message": { "date": 1787788800, "id": "message_1024", "text": "Здравствуйте! Проверяю заказ.", "type": "text" }
        }
      },
      "OpenChannelKeyboard": {
        "summary": "Исходящий текст с клавиатурой",
        "value": {
          "sender": { "id": "agent", "name": "Support" },
          "recipient": { "id": "customer_42" },
          "message": {
            "date": 1787788800, "id": "message_1025", "text": "Какой вопрос вас интересует?", "type": "keyboard", "multiple": false,
            "keyboard": [{ "id": "delivery", "text": "Доставка" }, { "id": "order", "text": "Мой заказ", "url": "https://shop.example/orders/1024" }]
          }
        }
      },
      "ChatAccepted": {
        "summary": "Диалог назначен оператору",
        "value": {
          "agent": { "email": "", "id": "operator_7", "name": "Мария" },
          "assigned_agent": null, "chat_id": 537183224, "event_name": "chat_accepted", "organization": null,
          "page": { "url": "https://shop.example/orders/1024" },
          "session": { "geoip": {}, "ip_addr": "", "user_agent": "", "utm": "", "utm_json": {} },
          "status": null, "tags": [{ "id": "vip", "title": "vip" }], "user_token": "customer_42",
          "visitor": { "chats_count": 1, "description": "", "email": "customer@example.com", "name": "Анна", "number": "122557678", "phone": "+79990000000", "social": {} },
          "widget_id": "sdk_connection_1", "analytics": {}
        }
      },
      "ChatUpdated": {
        "summary": "Обновлены контакты или custom data",
        "value": {
          "agent": { "email": "", "id": "operator_7", "name": "Мария" },
          "assigned_agent": null, "chat_id": 537183224, "event_name": "chat_updated", "organization": null,
          "page": { "url": "https://shop.example/orders/1024" },
          "session": { "geoip": { "city": "Москва", "country": "Россия", "country_code": "RU" }, "ip_addr": "", "user_agent": "", "utm": "", "utm_json": {} },
          "status": null, "tags": [{ "id": "vip", "title": "vip" }], "user_token": "customer_42",
          "visitor": { "chats_count": 1, "description": "", "email": "customer@example.com", "name": "Анна", "number": "122557678", "phone": "+79990000000", "social": {} },
          "widget_id": "sdk_connection_1", "analytics": {}
        }
      },
      "ChatFinished": {
        "summary": "Закрытый диалог с текстовой перепиской",
        "value": {
          "assigned_agent": null, "chat_id": 537183224, "event_name": "chat_finished", "organization": null,
          "page": { "url": "https://shop.example/orders/1024" },
          "session": { "geoip": {}, "ip_addr": "", "user_agent": "", "utm": "", "utm_json": {} },
          "status": null, "tags": [{ "id": "vip", "title": "vip" }], "user_token": "customer_42",
          "visitor": { "chats_count": 1, "description": "", "email": "customer@example.com", "name": "Анна", "number": "122557678", "phone": "+79990000000", "social": {} },
          "widget_id": "sdk_connection_1", "agents": [{ "email": "", "id": "operator_7", "name": "Мария" }], "analytics": null,
          "chat": { "blacklisted": false, "messages": [{ "message": "Где мой заказ?", "timestamp": 1787788790, "type": "visitor" }, { "message": "Заказ передан в доставку.", "timestamp": 1787788800, "type": "agent", "agent_id": "operator_7" }], "rate": null },
          "html_messages": "", "plain_messages": "Visitor: Где мой заказ?\nAgent: Заказ передан в доставку."
        }
      },
      "ClientUpdated": {
        "summary": "Изменились контакты клиента",
        "value": {
          "agent": null, "assigned_agent": null, "chat_id": 537183224, "client_id": 122557678, "event_name": "client_updated", "organization": null,
          "page": null, "session": { "geoip": {}, "ip_addr": "", "user_agent": "", "utm": "", "utm_json": {} },
          "status": null, "tags": [], "user_token": null,
          "visitor": { "chats_count": 1, "description": "", "email": "customer@example.com", "name": "Анна", "number": "122557678", "phone": "+79990000000", "social": {} },
          "widget_id": "sdk_connection_1"
        }
      },
      "ClientAttributeUpdated": {
        "summary": "Компактное событие атрибутов, без снимка диалога",
        "value": {
          "attributes": { "plan": "business", "orders": 12 }, "client_id": 122557678,
          "event_name": "client_attribute_updated", "user_token": "customer_42", "widget_id": "sdk_connection_1"
        }
      },
      "OfflineMessage": {
        "summary": "SDK отправил offlineMessage=true",
        "value": {
          "agent": null, "assigned_agent": null, "chat_id": 537183224, "event_name": "offline_message", "organization": null,
          "page": { "url": "https://shop.example/orders/1024" },
          "session": { "geoip": {}, "ip_addr": "", "user_agent": "", "utm": "", "utm_json": {} },
          "status": null, "tags": [], "user_token": "customer_42",
          "visitor": { "chats_count": 1, "description": "", "email": "customer@example.com", "name": "Анна", "number": "122557678", "social": {} },
          "widget_id": "sdk_connection_1", "analytics": {}, "message": "Напишите мне, когда сможете проверить заказ.", "offline_message_id": "message_offline_1024"
        }
      },
      "Acknowledgement": { "summary": "Необязательное JSON-подтверждение", "value": { "result": "ok" } },
      "Enrichment": {
        "summary": "Обогащение только для chat_accepted/chat_updated",
        "value": {
          "result": "ok", "contact_info": { "name": "Анна", "phone": "+79990000000", "email": "customer@example.com" },
          "custom_data": [{ "title": "Заказ", "content": "1024", "link": "https://shop.example/orders/1024" }],
          "crm_link": "https://crm.example/customers/42", "enable_assign": true,
          "page": { "url": "https://shop.example/orders/1024", "title": "Заказ 1024" }
        }
      }
    }
  }
}
