openapi: 3.1.0 info: title: Chatbeep Public API version: v1 summary: Contrato público maduro do Chatbeep em /api/v1 description: > Contrato público do Chatbeep para health checks, widget público, verificação em conversa e operações mobile autenticadas. As rotas legacy em /api/widget/* e /api/mobile/* continuam disponíveis apenas por compatibilidade, mas não integram a surface pública suportada. servers: - url: https://api.chatbeep.app/api/v1 description: Produção pública. - url: https://sandbox.chatbeep.app/api/v1 description: Sandbox público para integração e troubleshooting. externalDocs: description: Developer Portal url: https://docs.chatbeep.app tags: - name: Health description: Estado operacional e diagnósticos mínimos da plataforma. - name: Widget description: Endpoints públicos consumidos pelo widget e pelo simulador do visitante. - name: Verification description: Fluxos de verificação segura dentro da conversa pública. - name: Mobile Auth description: Login e contexto autenticado do operador mobile. - name: Mobile Conversations description: Operações maduras de atendimento para operadores mobile. paths: /health: get: tags: [Health] summary: Obter o health report da plataforma operationId: getHealth responses: '200': $ref: '#/components/responses/HealthSuccess' '503': $ref: '#/components/responses/HealthDegraded' /widget/config/{token}: get: tags: [Widget] summary: Carregar a configuração pública do widget operationId: getWidgetConfig parameters: - $ref: '#/components/parameters/WidgetTokenPath' responses: '200': $ref: '#/components/responses/WidgetConfigSuccess' '404': $ref: '#/components/responses/ApiNotFound' '429': $ref: '#/components/responses/ApiRateLimited' /widget/installations/{token}/health: get: tags: [Widget] summary: Validar a saúde da instalação do widget operationId: getWidgetInstallationHealth parameters: - $ref: '#/components/parameters/WidgetTokenPath' - name: current_url in: query required: false schema: type: string maxLength: 2048 description: URL atual do site onde o widget está montado. responses: '200': $ref: '#/components/responses/InstallationHealthSuccess' '404': $ref: '#/components/responses/ApiNotFound' '429': $ref: '#/components/responses/ApiRateLimited' /widget/ping: post: tags: [Widget] summary: Registar heartbeat da instalação operationId: postWidgetPing requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WidgetPingRequest' examples: default: value: token: pub_demo_installation_token current_url: https://example.com/help installation_type: snippet version: 1.0.0 responses: '200': $ref: '#/components/responses/WidgetPingSuccess' '422': $ref: '#/components/responses/ApiValidationError' '429': $ref: '#/components/responses/ApiRateLimited' /widget/visitors: post: tags: [Widget] summary: Criar ou atualizar o visitante público operationId: postWidgetVisitor requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WidgetVisitorRequest' examples: default: value: token: pub_demo_installation_token uuid: 11111111-1111-4111-8111-111111111111 name: Maria Demo email: maria@example.com phone: '+351912000000' current_url: https://example.com/help responses: '201': $ref: '#/components/responses/WidgetVisitorSuccess' '422': $ref: '#/components/responses/ApiValidationError' '429': $ref: '#/components/responses/ApiRateLimited' /widget/conversations: post: tags: [Widget] summary: Iniciar uma conversa pública operationId: postWidgetConversation requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WidgetConversationCreateRequest' examples: default: value: token: pub_demo_installation_token visitor_uuid: 11111111-1111-4111-8111-111111111111 department_id: 12 subject: Pedido de suporte message: Preciso de ajuda com a instalação. privacy_accepted: true current_url: https://example.com/help responses: '201': $ref: '#/components/responses/WidgetConversationSuccess' '404': $ref: '#/components/responses/ApiNotFound' '422': $ref: '#/components/responses/ApiValidationError' '429': $ref: '#/components/responses/ApiRateLimited' /widget/conversations/{conversation}/messages: get: tags: [Widget] summary: Obter a timeline pública da conversa operationId: getWidgetConversationMessages parameters: - $ref: '#/components/parameters/WidgetConversationPath' - $ref: '#/components/parameters/WidgetTokenQuery' - name: after_id in: query required: false schema: type: integer minimum: 1 description: Devolve apenas mensagens com ID superior ao indicado. responses: '200': $ref: '#/components/responses/WidgetMessagesSuccess' '404': $ref: '#/components/responses/ApiNotFound' '429': $ref: '#/components/responses/ApiRateLimited' post: tags: [Widget] summary: Enviar mensagem pública do visitante operationId: postWidgetConversationMessage parameters: - $ref: '#/components/parameters/WidgetConversationPath' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WidgetMessageCreateJsonRequest' multipart/form-data: schema: $ref: '#/components/schemas/WidgetMessageCreateMultipartRequest' responses: '201': $ref: '#/components/responses/WidgetMessageMutationSuccess' '404': $ref: '#/components/responses/ApiNotFound' '422': $ref: '#/components/responses/ApiValidationError' '429': $ref: '#/components/responses/ApiRateLimited' /widget/conversations/{conversation}/verification/start: post: tags: [Verification] summary: Iniciar desafio de verificação segura operationId: postWidgetVerificationStart parameters: - $ref: '#/components/parameters/WidgetConversationPath' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WidgetVerificationStartRequest' examples: sms: value: token: pub_demo_installation_token method: sms target: '+351912000000' responses: '202': $ref: '#/components/responses/WidgetVerificationStartSuccess' '404': $ref: '#/components/responses/ApiNotFound' '422': $ref: '#/components/responses/ApiValidationError' '429': $ref: '#/components/responses/ApiRateLimited' /widget/conversations/{conversation}/verification/confirm: post: tags: [Verification] summary: Confirmar o desafio de verificação operationId: postWidgetVerificationConfirm parameters: - $ref: '#/components/parameters/WidgetConversationPath' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WidgetVerificationConfirmRequest' examples: default: value: token: pub_demo_installation_token challenge_id: 1024 code: '123456' responses: '200': $ref: '#/components/responses/WidgetVerificationConfirmSuccess' '404': $ref: '#/components/responses/ApiNotFound' '422': $ref: '#/components/responses/ApiValidationError' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/login: post: tags: [Mobile Auth] summary: Autenticar operador mobile operationId: postMobileLogin requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MobileLoginRequest' examples: default: value: email: operator@example.com password: CHANGE_ME device_name: postman-sandbox responses: '200': $ref: '#/components/responses/MobileLoginSuccess' '403': $ref: '#/components/responses/ApiForbidden' '422': $ref: '#/components/responses/ApiValidationError' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/me: get: tags: [Mobile Auth] summary: Obter o contexto do operador autenticado operationId: getMobileMe security: - bearerAuth: [] responses: '200': $ref: '#/components/responses/MobileMeSuccess' '401': $ref: '#/components/responses/ApiUnauthorized' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/conversations: get: tags: [Mobile Conversations] summary: Listar conversas visíveis para o operador operationId: getMobileConversations security: - bearerAuth: [] parameters: - name: page in: query required: false schema: type: integer minimum: 1 - name: per_page in: query required: false schema: type: integer minimum: 1 maximum: 100 - name: status in: query required: false schema: type: string - name: department_id in: query required: false schema: type: integer - name: assigned_user_id in: query required: false schema: type: integer responses: '200': $ref: '#/components/responses/MobileConversationListSuccess' '401': $ref: '#/components/responses/ApiUnauthorized' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/conversations/{conversation}: get: tags: [Mobile Conversations] summary: Obter o detalhe de uma conversa operationId: getMobileConversation security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/MobileConversationPath' responses: '200': $ref: '#/components/responses/MobileConversationDetailSuccess' '401': $ref: '#/components/responses/ApiUnauthorized' '403': $ref: '#/components/responses/ApiForbidden' '404': $ref: '#/components/responses/ApiNotFound' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/conversations/{conversation}/messages: post: tags: [Mobile Conversations] summary: Responder numa conversa como operador operationId: postMobileConversationMessage security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/MobileConversationPath' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MobileConversationMessageRequest' responses: '200': $ref: '#/components/responses/MobileMessageMutationSuccess' '401': $ref: '#/components/responses/ApiUnauthorized' '403': $ref: '#/components/responses/ApiForbidden' '404': $ref: '#/components/responses/ApiNotFound' '422': $ref: '#/components/responses/ApiValidationError' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/conversations/{conversation}/assign: post: tags: [Mobile Conversations] summary: Atribuir a conversa a um operador operationId: postMobileConversationAssign security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/MobileConversationPath' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/MobileConversationAssignRequest' responses: '200': $ref: '#/components/responses/MobileConversationMutationSuccess' '401': $ref: '#/components/responses/ApiUnauthorized' '403': $ref: '#/components/responses/ApiForbidden' '404': $ref: '#/components/responses/ApiNotFound' '422': $ref: '#/components/responses/ApiValidationError' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/conversations/{conversation}/transfer: post: tags: [Mobile Conversations] summary: Transferir a conversa para outro setor ou operador operationId: postMobileConversationTransfer security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/MobileConversationPath' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/MobileConversationTransferRequest' responses: '200': $ref: '#/components/responses/MobileConversationMutationSuccess' '401': $ref: '#/components/responses/ApiUnauthorized' '403': $ref: '#/components/responses/ApiForbidden' '404': $ref: '#/components/responses/ApiNotFound' '422': $ref: '#/components/responses/ApiValidationError' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' /mobile/conversations/{conversation}/close: post: tags: [Mobile Conversations] summary: Encerrar a conversa operationId: postMobileConversationClose security: - bearerAuth: [] parameters: - $ref: '#/components/parameters/MobileConversationPath' requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/MobileConversationCloseRequest' responses: '200': $ref: '#/components/responses/MobileConversationMutationSuccess' '401': $ref: '#/components/responses/ApiUnauthorized' '403': $ref: '#/components/responses/ApiForbidden' '404': $ref: '#/components/responses/ApiNotFound' '423': $ref: '#/components/responses/ApiLocked' '429': $ref: '#/components/responses/ApiRateLimited' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: Sanctum headers: XApiVersion: description: Versão estável da API pública. schema: type: string example: v1 XRequestId: description: Identificador único do request, propagado para logs e troubleshooting. schema: type: string example: req_01J0EXAMPLE12345 XTraceId: description: Identificador de trace associado ao request. schema: type: string example: trace_01J0EXAMPLE12345 parameters: WidgetTokenPath: name: token in: path required: true schema: type: string description: Token público da instalação/widget. WidgetTokenQuery: name: token in: query required: true schema: type: string description: Token público da instalação/widget. WidgetConversationPath: name: conversation in: path required: true schema: type: string description: ID público da conversa do widget. MobileConversationPath: name: conversation in: path required: true schema: type: integer description: ID interno da conversa visível ao operador mobile. responses: HealthSuccess: description: Plataforma operacional. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/HealthEnvelope' HealthDegraded: description: Plataforma degradada, mas com payload de diagnóstico. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/HealthEnvelope' WidgetConfigSuccess: description: Configuração pública do widget. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetConfigEnvelope' InstallationHealthSuccess: description: Saúde da instalação e readiness do widget. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/InstallationHealthEnvelope' WidgetPingSuccess: description: Heartbeat aceite. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetPingEnvelope' WidgetVisitorSuccess: description: Visitante criado ou atualizado. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetVisitorEnvelope' WidgetConversationSuccess: description: Conversa criada com a timeline inicial. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetConversationEnvelope' WidgetMessagesSuccess: description: Timeline pública atualizada. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetMessagesEnvelope' WidgetMessageMutationSuccess: description: Mensagem do visitante registada e timeline incremental devolvida. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetMessageMutationEnvelope' WidgetVerificationStartSuccess: description: Desafio de verificação iniciado. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetVerificationStartEnvelope' WidgetVerificationConfirmSuccess: description: Verificação concluída com sucesso. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/WidgetVerificationConfirmEnvelope' MobileLoginSuccess: description: Login mobile aceite e token emitido. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/MobileLoginEnvelope' MobileMeSuccess: description: Contexto do operador autenticado. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/MobileMeEnvelope' MobileConversationListSuccess: description: Lista paginada de conversas visíveis ao operador. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/MobileConversationListEnvelope' MobileConversationDetailSuccess: description: Detalhe completo da conversa. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/MobileConversationDetailEnvelope' MobileMessageMutationSuccess: description: Mensagem do operador registada. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/MobileMessageMutationEnvelope' MobileConversationMutationSuccess: description: Conversa atualizada com sucesso. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/MobileConversationMutationEnvelope' ApiUnauthorized: description: Bearer token em falta, inválido ou expirado. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' examples: default: value: success: false error: code: authentication_failed message: Authentication is required. meta: api_version: v1 request_id: req_01J0AUTH trace_id: trace_01J0AUTH timestamp: '2026-07-07T11:00:00+01:00' ApiForbidden: description: O operador autenticado não tem permissão para esta ação. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' ApiNotFound: description: O recurso pedido não existe ou não é visível neste contexto. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' ApiLocked: description: O recurso ou contexto atual encontra-se bloqueado. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' ApiValidationError: description: O request falhou nas regras de validação. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/ValidationErrorEnvelope' examples: invalidCredentials: value: success: false error: code: validation_error message: Validation failed. details: errors: email: - Invalid credentials. meta: api_version: v1 request_id: req_01J0VALIDATION trace_id: trace_01J0VALIDATION timestamp: '2026-07-07T11:00:00+01:00' ApiRateLimited: description: Limite de requests excedido. headers: Retry-After: schema: type: integer description: Número de segundos até poder repetir o request. X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/RateLimitedErrorEnvelope' ApiUnexpectedError: description: Erro inesperado da plataforma. headers: X-Api-Version: $ref: '#/components/headers/XApiVersion' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Trace-Id: $ref: '#/components/headers/XTraceId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' schemas: ResponseMeta: type: object properties: api_version: type: string example: v1 request_id: type: string trace_id: type: string timestamp: type: string format: date-time pagination: $ref: '#/components/schemas/PaginationMeta' links: $ref: '#/components/schemas/PaginationLinks' rate_limit: $ref: '#/components/schemas/RateLimitMeta' required: [api_version, request_id, trace_id, timestamp] PaginationMeta: type: object properties: current_page: type: integer last_page: type: integer per_page: type: integer total: type: integer from: type: integer nullable: true to: type: integer nullable: true required: [current_page, last_page, per_page, total] PaginationLinks: type: object properties: first: type: string nullable: true last: type: string nullable: true prev: type: string nullable: true next: type: string nullable: true RateLimitMeta: type: object properties: limit: type: integer remaining: type: integer retry_after: type: integer BaseSuccessEnvelope: type: object properties: success: type: boolean const: true meta: $ref: '#/components/schemas/ResponseMeta' required: [success, meta] ErrorEnvelope: type: object properties: success: type: boolean const: false error: type: object properties: code: type: string message: type: string details: type: object additionalProperties: true required: [code, message] meta: $ref: '#/components/schemas/ResponseMeta' required: [success, error, meta] ValidationErrorEnvelope: type: object properties: success: type: boolean const: false error: type: object properties: code: type: string const: validation_error message: type: string details: type: object properties: errors: type: object additionalProperties: type: array items: type: string required: [errors] required: [code, message, details] meta: $ref: '#/components/schemas/ResponseMeta' required: [success, error, meta] RateLimitedErrorEnvelope: allOf: - $ref: '#/components/schemas/ErrorEnvelope' - type: object properties: meta: allOf: - $ref: '#/components/schemas/ResponseMeta' - type: object properties: rate_limit: $ref: '#/components/schemas/RateLimitMeta' HealthReport: type: object properties: status: type: string checked_at: type: string format: date-time checks: type: object additionalProperties: type: object additionalProperties: true required: [status, checked_at, checks] HealthEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/HealthReport' required: [data] WidgetConfigData: type: object properties: tenant: type: object properties: name: { type: string } widget_color: { type: string, nullable: true } widget_color_secondary: { type: string, nullable: true } widget_position: { type: string, nullable: true } widget_sound_variant: { type: string, nullable: true } widget_operator_wait_limit_minutes: { type: integer, nullable: true } widget_client_wait_limit_minutes: { type: integer, nullable: true } widget_logo: type: object additionalProperties: true widget_greeting: { type: string, nullable: true } privacy_policy_url: { type: string, nullable: true } online: { type: boolean } chatbot: type: object additionalProperties: true departments: type: array items: $ref: '#/components/schemas/DepartmentSummary' required: [name, online, departments] site: type: object properties: id: { type: integer } name: { type: string } primary_domain: { type: string, nullable: true } timezone: { type: string, nullable: true } required: [id, name] installation: type: object properties: id: { type: integer } public_key: { type: string } installation_type: { type: string } status: { type: string } health: { type: string } version: { type: string, nullable: true } required: [id, public_key, installation_type, status, health] required: [tenant, site, installation] WidgetConfigEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetConfigData' required: [data] InstallationHealthData: type: object properties: tenant: type: object properties: name: { type: string } slug: { type: string } required: [name, slug] site: type: object properties: id: { type: integer } name: { type: string } primary_domain: { type: string, nullable: true } domain_match: { type: boolean, nullable: true } required: [id, name] installation: type: object properties: id: { type: integer } public_key: { type: string } installation_type: { type: string } status: { type: string } health: { type: string } version: { type: string, nullable: true } last_seen_at: { type: string, format: date-time, nullable: true } required: [id, public_key, installation_type, status, health] widget: type: object properties: enabled: { type: boolean } script_url: { type: string } api_base_url: { type: string } required: [enabled, script_url, api_base_url] warnings: type: array items: oneOf: - type: string - type: object additionalProperties: true readiness: type: object additionalProperties: true required: [tenant, site, installation, widget, warnings, readiness] InstallationHealthEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/InstallationHealthData' required: [data] WidgetPingRequest: type: object properties: token: { type: string } current_url: { type: string, maxLength: 2048, nullable: true } installation_type: { type: string, nullable: true } version: { type: string, maxLength: 120, nullable: true } required: [token] WidgetPingData: type: object properties: installation: type: object properties: id: { type: integer } public_key: { type: string } status: { type: string } health: { type: string } last_seen_at: { type: string, format: date-time, nullable: true } version: { type: string, nullable: true } required: [id, public_key, status, health] site: type: object properties: id: { type: integer } name: { type: string } primary_domain: { type: string, nullable: true } required: [id, name] required: [installation, site] WidgetPingEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetPingData' required: [data] WidgetVisitorRequest: type: object properties: token: { type: string } uuid: { type: string, format: uuid, nullable: true } name: { type: string, maxLength: 255, nullable: true } email: { type: string, format: email, maxLength: 255, nullable: true } phone: { type: string, maxLength: 50, nullable: true } current_url: { type: string, maxLength: 2048, nullable: true } referrer: { type: string, maxLength: 2048, nullable: true } required: [token] WidgetVisitorData: type: object properties: uuid: { type: string, format: uuid } name: { type: string, nullable: true } email: { type: string, nullable: true } phone: { type: string, nullable: true } required: [uuid] WidgetVisitorEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetVisitorData' required: [data] WidgetConversationCreateRequest: type: object properties: token: { type: string } visitor_uuid: { type: string, format: uuid } department_id: { type: integer, nullable: true } subject: { type: string, maxLength: 255, nullable: true } message: { type: string, maxLength: 5000 } privacy_accepted: { type: boolean } current_url: { type: string, maxLength: 2048, nullable: true } required: [token, visitor_uuid, message, privacy_accepted] DepartmentSummary: type: object properties: id: { type: integer } name: { type: string } color: { type: string, nullable: true } required: [id, name] AssignedUserSummary: type: object properties: id: { type: integer } name: { type: string } email: { type: string, nullable: true } required: [id, name] VisitorSummary: type: object properties: name: { type: string, nullable: true } email: { type: string, nullable: true } phone: { type: string, nullable: true } ConversationPresence: type: object properties: is_online: { type: boolean } label: { type: string } required: [is_online, label] ConversationVerificationState: type: object additionalProperties: true nullable: true AttachmentDescriptor: type: object properties: id: { type: integer } file_name: { type: string } mime_type: { type: string, nullable: true } size_bytes: { type: integer, nullable: true } visibility: { type: string, nullable: true } purpose: { type: string, nullable: true } required: [id, file_name] WidgetPublicConversation: type: object properties: id: { type: string } site_id: { type: integer, nullable: true } installation_id: { type: integer, nullable: true } status: { type: string } channel: { type: string } channel_label: { type: string } assigned_user_id: { type: integer, nullable: true } assigned_user_name: { type: string, nullable: true } last_message_at: { type: string, format: date-time, nullable: true } presence: $ref: '#/components/schemas/ConversationPresence' verification: $ref: '#/components/schemas/ConversationVerificationState' required: [id, status, channel, channel_label, presence] WidgetMessage: type: object properties: id: { type: integer } sender_type: { type: string } sender_name: { type: string } sender_avatar_url: { type: string, nullable: true } body: { type: string, nullable: true } message_type: { type: string } channel: { type: string } channel_label: { type: string } direction: { type: string } quick_replies: type: array items: type: object additionalProperties: true links: type: array items: type: object properties: label: { type: string } url: { type: string } required: [label, url] media: type: object additionalProperties: true nullable: true input_ui: type: object additionalProperties: true nullable: true attachments: type: array items: $ref: '#/components/schemas/AttachmentDescriptor' created_at: { type: string, format: date-time, nullable: true } created_at_human: { type: string, nullable: true } required: [id, sender_type, sender_name, message_type, channel, channel_label, direction, quick_replies, links, attachments] WidgetConversationBootstrapData: type: object properties: conversation: $ref: '#/components/schemas/WidgetPublicConversation' messages: type: array items: $ref: '#/components/schemas/WidgetMessage' required: [conversation, messages] WidgetConversationEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetConversationBootstrapData' required: [data] WidgetMessagesData: type: object properties: conversation: $ref: '#/components/schemas/WidgetPublicConversation' messages: type: array items: $ref: '#/components/schemas/WidgetMessage' required: [conversation, messages] WidgetMessagesEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetMessagesData' required: [data] WidgetMessageCreateJsonRequest: type: object properties: token: { type: string } body: { type: string, maxLength: 5000, nullable: true } current_url: { type: string, maxLength: 2048, nullable: true } required: [token] WidgetMessageCreateMultipartRequest: type: object properties: token: { type: string } body: { type: string, maxLength: 5000, nullable: true } current_url: { type: string, maxLength: 2048, nullable: true } attachment: type: string format: binary required: [token] WidgetMessageMutationData: type: object properties: message: $ref: '#/components/schemas/WidgetMessage' messages: type: array items: $ref: '#/components/schemas/WidgetMessage' conversation: $ref: '#/components/schemas/WidgetPublicConversation' required: [message, messages, conversation] WidgetMessageMutationEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetMessageMutationData' required: [data] WidgetVerificationStartRequest: type: object properties: token: { type: string } method: { type: string } target: { type: string, maxLength: 255, nullable: true } required: [token, method] WidgetVerificationPayload: type: object properties: already_verified: { type: boolean, nullable: true } is_verified: { type: boolean, nullable: true } challenge_id: { type: integer, nullable: true } method: { type: string, nullable: true } delivery_channel: { type: string, nullable: true } requires_code: { type: boolean, nullable: true } target_masked: { type: string, nullable: true } expires_at: { type: string, format: date-time, nullable: true } WidgetVerificationConversation: type: object properties: id: { type: string } site_id: { type: integer, nullable: true } installation_id: { type: integer, nullable: true } status: { type: string } assigned_user_id: { type: integer, nullable: true } assigned_user_name: { type: string, nullable: true } presence: $ref: '#/components/schemas/ConversationPresence' verification: $ref: '#/components/schemas/ConversationVerificationState' required: [id, status, presence] WidgetVerificationStartData: type: object properties: message: { type: string } verification: $ref: '#/components/schemas/WidgetVerificationPayload' conversation: $ref: '#/components/schemas/WidgetVerificationConversation' required: [message, verification, conversation] WidgetVerificationStartEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetVerificationStartData' required: [data] WidgetVerificationConfirmRequest: type: object properties: token: { type: string } challenge_id: { type: integer } code: { type: string, minLength: 4, maxLength: 20 } required: [token, challenge_id, code] WidgetVerificationConfirmData: type: object properties: message: { type: string } verification: $ref: '#/components/schemas/WidgetVerificationPayload' conversation: $ref: '#/components/schemas/WidgetVerificationConversation' required: [message, verification, conversation] WidgetVerificationConfirmEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/WidgetVerificationConfirmData' required: [data] MobileLoginRequest: type: object properties: email: { type: string, format: email, maxLength: 255 } password: { type: string } device_name: { type: string, maxLength: 255, nullable: true } required: [email, password] AvailabilitySnapshot: type: object additionalProperties: true MobileUser: type: object properties: id: { type: integer } name: { type: string } email: { type: string } role: { type: string } last_seen_at: { type: string, format: date-time, nullable: true } availability: $ref: '#/components/schemas/AvailabilitySnapshot' departments: type: array items: $ref: '#/components/schemas/DepartmentSummary' required: [id, name, email, role, departments] MobileTenant: type: object properties: id: { type: integer } name: { type: string } email: { type: string, nullable: true } website: { type: string, nullable: true } plan: type: object additionalProperties: true required: [id, name] MobileLoginData: type: object properties: token: { type: string } token_type: { type: string } expires_at: { type: string, format: date-time } user: $ref: '#/components/schemas/MobileUser' tenant: $ref: '#/components/schemas/MobileTenant' required: [token, token_type, expires_at, user, tenant] MobileLoginEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/MobileLoginData' required: [data] MobileMeData: type: object properties: user: $ref: '#/components/schemas/MobileUser' tenant: $ref: '#/components/schemas/MobileTenant' required: [user, tenant] MobileMeEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/MobileMeData' required: [data] MobileConversationSummary: type: object properties: id: { type: integer } public_id: { type: string } status: { type: string } priority: { type: string } subject: { type: string, nullable: true } source: { type: string, nullable: true } channel: { type: string } channel_label: { type: string } started_at: { type: string, format: date-time, nullable: true } last_message_at: { type: string, format: date-time, nullable: true } closed_at: { type: string, format: date-time, nullable: true } messages_count: { type: integer } visitor: $ref: '#/components/schemas/VisitorSummary' department: allOf: - $ref: '#/components/schemas/DepartmentSummary' nullable: true assigned_user: allOf: - $ref: '#/components/schemas/AssignedUserSummary' nullable: true required: [id, public_id, status, priority, channel, channel_label, messages_count, visitor] MobileMessage: type: object properties: id: { type: integer } sender_type: { type: string } sender_name: { type: string } sender_avatar_url: { type: string, nullable: true } body: { type: string, nullable: true } message_type: { type: string } channel: { type: string } channel_label: { type: string } direction: { type: string } attachments: type: array items: $ref: '#/components/schemas/AttachmentDescriptor' created_at: { type: string, format: date-time, nullable: true } created_at_human: { type: string, nullable: true } required: [id, sender_type, sender_name, message_type, channel, channel_label, direction, attachments] MobileConversationDetail: allOf: - $ref: '#/components/schemas/MobileConversationSummary' - type: object properties: first_response_at: { type: string, format: date-time, nullable: true } messages: type: array items: $ref: '#/components/schemas/MobileMessage' required: [messages] MobileConversationListData: type: object properties: conversations: type: array items: $ref: '#/components/schemas/MobileConversationSummary' required: [conversations] MobileConversationListEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/MobileConversationListData' meta: allOf: - $ref: '#/components/schemas/ResponseMeta' - type: object properties: pagination: $ref: '#/components/schemas/PaginationMeta' links: $ref: '#/components/schemas/PaginationLinks' required: [data, meta] MobileConversationDetailData: type: object properties: conversation: $ref: '#/components/schemas/MobileConversationDetail' required: [conversation] MobileConversationDetailEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/MobileConversationDetailData' required: [data] MobileConversationMessageRequest: type: object properties: body: { type: string, maxLength: 5000 } required: [body] MobileConversationAssignRequest: type: object properties: assigned_user_id: { type: integer, nullable: true } MobileConversationTransferRequest: type: object properties: department_id: { type: integer, nullable: true } assigned_user_id: { type: integer, nullable: true } reason: { type: string, maxLength: 1000, nullable: true } MobileConversationCloseRequest: type: object properties: reason: { type: string, nullable: true } MobileMessageMutationData: type: object properties: message: $ref: '#/components/schemas/MobileMessage' conversation: $ref: '#/components/schemas/MobileConversationSummary' required: [message, conversation] MobileMessageMutationEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/MobileMessageMutationData' required: [data] MobileConversationMutationData: type: object properties: conversation: $ref: '#/components/schemas/MobileConversationSummary' required: [conversation] MobileConversationMutationEnvelope: allOf: - $ref: '#/components/schemas/BaseSuccessEnvelope' - type: object properties: data: $ref: '#/components/schemas/MobileConversationMutationData' required: [data]