# Oncenter – API de Integração Externa

Esta documentação descreve as rotas disponíveis para integrações externas com a plataforma **OncenterChat**. Todas as rotas exigem autenticação via **UUID da empresa** e estão agrupadas sob o prefixo `/api/external` e URL Base `https://api.oncenterchat.com`.

---

## Sumário

1. [Autenticação](#autenticação)
2. [Formato de Resposta](#formato-de-resposta)
3. [Paginação](#paginação)
4. [Rotas de Usuários](#rotas-de-usuários)
   - [Listar Usuários](#listar-usuários)
   - [Detalhar Usuário](#detalhar-usuário)
   - [Alterar Status do Usuário](#alterar-status-do-usuário)
5. [Rotas de Contatos](#rotas-de-contatos)
   - [Listar Contatos](#listar-contatos)
   - [Detalhar Contato](#detalhar-contato)
   - [Criar Contato](#criar-contato)
   - [Atualizar Contato](#atualizar-contato)
6. [Rotas de Departamentos](#rotas-de-departamentos)
   - [Listar Departamentos](#listar-departamentos)
   - [Detalhar Departamento](#detalhar-departamento)
7. [Motivos de Finalização](#motivos-de-finalização)
   - [Listar Motivos de Finalização](#listar-motivos-de-finalização)
8. [Rotas de Tickets](#rotas-de-tickets)
   - [Listar Tickets](#listar-tickets)
   - [Detalhar Ticket](#detalhar-ticket)
   - [Finalizar Ticket](#finalizar-ticket)
   - [Transferir Ticket](#transferir-ticket)
9. [Atualizações de Tickets em Tempo Real](#atualizações-de-tickets-em-tempo-real-websocket)
   - [Conectar com Socket.IO](#conectar-com-socketio)
10. [Tabelas de Referência](#tabelas-de-referência)
11. [Exemplos com cURL](#exemplos-com-curl)
12. [Códigos de Erro](#códigos-de-erro)

---

## Autenticação

Toda requisição deve enviar o **UUID da empresa** no cabeçalho `Authorization`:

```
Authorization: <UUID_DA_EMPRESA>
Base URL: https://api.oncenterchat.com/api/external
```

O UUID pode ser localizado no painel administrativo da Oncenter em **Configurações → Empresa → Identificador de Integração**.

> ⚠️ **Não** utilize o prefixo `Bearer`. Envie apenas o UUID puro.

**Exemplo de cabeçalho:**
```http
GET /api/external/users HTTP/1.1
Host: app.oncenter.com.br
Authorization: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
```

Se o UUID for inválido ou estiver ausente, a API retorna:

```json
{
  "code": 401,
  "message": "Unauthorized",
  "error": "Invalid auth key"
}
```

---

## Formato de Resposta

Todas as respostas seguem o padrão:

```json
{
  "success": true,
  "data": { ... }
}
```

Em caso de erro de validação (HTTP 422):

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "campo": ["Mensagem de erro"]
  }
}
```

---

## Paginação

Endpoints que listam múltiplos recursos são **paginados**. O objeto `data` retornado segue o padrão Laravel:

```json
{
  "success": true,
  "data": {
    "current_page": 1,
    "data": [ ... ],
    "first_page_url": "https://api.oncenterchat.com/api/external/tickets?page=1",
    "from": 1,
    "last_page": 4,
    "last_page_url": "https://api.oncenterchat.com/api/external/tickets?page=4",
    "next_page_url": "https://api.oncenterchat.com/api/external/tickets?page=2",
    "path": "https://api.oncenterchat.com/api/external/tickets",
    "per_page": 50,
    "prev_page_url": null,
    "to": 50,
    "total": 180
  }
}
```

| Parâmetro  | Descrição                                 | Padrão |
|------------|-------------------------------------------|--------|
| `page`     | Número da página                          | `1`    |
| `per_page` | Itens por página (máx. 200 para tickets)  | `50`   |

---

## Rotas de Usuários

### Listar Usuários

Retorna a lista paginada de usuários da empresa. Campos sensíveis como senha, extensão e timestamps são omitidos.

**Endpoint:**
```
GET /api/external/users
```

**Parâmetros de Query (todos opcionais):**

| Parâmetro  | Tipo    | Descrição                                                        |
|------------|---------|------------------------------------------------------------------|
| `name`     | string  | Filtro parcial por nome (ex.: `?name=João`)                      |
| `role`     | string  | Filtro por perfil: `admin`, `supervisor` ou `operador`           |
| `active`   | integer | `1` = apenas ativos (padrão), `0` = apenas inativos             |
| `per_page` | integer | Itens por página (padrão: `50`)                                  |
| `page`     | integer | Número da página (padrão: `1`)                                   |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": {
    "current_page": 1,
    "data": [
      {
        "id": 12,
        "name": "João Silva",
        "photo": "uploads/users/joao.jpg",
        "email": "joao@empresa.com",
        "role": "operador",
        "companyId": 3,
        "active": true,
        "full_photo": "https://s3.oncenterchat.com/uploads/users/joao.jpg",
        "status_with_name": "🟢 João Silva",
        "departments": [
          {
            "id": 1,
            "name": "Suporte",
            "color": "#3498db",
            "active": true
          }
        ]
      }
    ],
    "total": 10,
    "per_page": 50,
    "current_page": 1,
    "last_page": 1
  }
}
```

**Campos do objeto `User`:**

| Campo              | Tipo    | Descrição                                    |
|--------------------|---------|----------------------------------------------|
| `id`               | integer | Identificador único do usuário               |
| `name`             | string  | Nome completo                                |
| `photo`            | string  | Caminho relativo da foto no S3               |
| `email`            | string  | E-mail                                       |
| `role`             | string  | Perfil: `admin`, `supervisor` ou `operador`  |
| `companyId`        | integer | ID da empresa                                |
| `active`           | boolean | Se o usuário está ativo                      |
| `full_photo`       | string  | URL pública completa da foto                 |
| `status_with_name` | string  | Ícone de status + nome (ex.: `🟢 João`)     |
| `departments`      | array   | Departamentos vinculados ao usuário          |

---

### Detalhar Usuário

Retorna os dados de um único usuário com seus departamentos.

**Endpoint:**
```
GET /api/external/users/{id}
```

**Parâmetros de Rota:**

| Parâmetro | Tipo    | Descrição              |
|-----------|---------|------------------------|
| `id`      | integer | ID do usuário          |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": {
    "id": 12,
    "name": "João Silva",
    "photo": "uploads/users/joao.jpg",
    "email": "joao@empresa.com",
    "role": "operador",
    "companyId": 3,
    "active": true,
    "full_photo": "https://s3.oncenterchat.com/uploads/users/joao.jpg",
    "status_with_name": "🟢 João Silva",
    "departments": [ ... ]
  }
}
```

**Resposta quando não encontrado (HTTP 404):**

```json
{
  "success": false,
  "message": "Usuário não encontrado."
}
```

---

### Alterar Status do Usuário

Altera o status de chat de um usuário para `online` ou `offline`.

**Endpoint:**
```
PATCH /api/external/users/{id}/status
```

**Parâmetros de Rota:**

| Parâmetro | Tipo    | Descrição     |
|-----------|---------|---------------|
| `id`      | integer | ID do usuário |

**Body (JSON):**

```json
{
  "status": "online"
}
```

| Campo    | Tipo   | Obrigatório | Valores aceitos      |
|----------|--------|-------------|----------------------|
| `status` | string | ✅ Sim      | `online`, `offline`  |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "message": "Status atualizado com sucesso.",
  "data": {
    "id": 12,
    "name": "João Silva",
    "chat_status": "online"
  }
}
```

---

## Rotas de Contatos

### Listar Contatos

Retorna a lista paginada de contatos da empresa com filtros opcionais.

**Endpoint:**
```
GET /api/external/contacts
```

**Parâmetros de Query (todos opcionais):**

| Parâmetro       | Tipo    | Descrição                                                                      |
|-----------------|---------|--------------------------------------------------------------------------------|
| `term`          | string  | Termo de busca por nome ou número de telefone                                  |
| `platform`      | string  | Filtra pela plataforma: `whatsapp`, `instagram`, `facebook`, `telegram`        |
| `blocked`       | integer | `1` = apenas bloqueados, `0` = apenas não bloqueados                           |
| `category_id`   | integer | ID da categoria do contato                                                     |
| `per_page`      | integer | Itens por página (padrão: `50`)                                                |
| `page`          | integer | Número da página (padrão: `1`)                                                 |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": {
    "current_page": 1,
    "data": [
      {
        "id": 55,
        "name": "Maria Souza",
        "phone": "5511999999999",
        "email": "maria@cliente.com",
        "cnpj": "12.345.678/0001-99",
        "about": null,
        "active": 1,
        "blocked": 0,
        "companyId": 3,
        "category_id": null
      }
    ],
    "total": 1,
    "per_page": 50,
    "current_page": 1,
    "last_page": 1
  }
}
```

---

### Detalhar Contato

Retorna os detalhes de um único contato.

**Endpoint:**
```
GET /api/external/contacts/{id}
```

**Parâmetros de Rota:**

| Parâmetro | Tipo    | Descrição            |
|-----------|---------|----------------------|
| `id`      | integer | ID do contato        |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": {
    "id": 55,
    "name": "Maria Souza",
    "phone": "5511999999999",
    "email": "maria@cliente.com",
    "cnpj": "12.345.678/0001-99",
    "about": null,
    "active": 1,
    "blocked": 0,
    "companyId": 3,
    "category_id": null
  }
}
```

---

### Criar Contato

Cria um novo contato na plataforma. Retorna erro se o telefone já existir para a empresa.

**Endpoint:**
```
POST /api/external/contacts
```

**Body (JSON):**

```json
{
  "name": "Maria Souza",
  "phone": "5511999999999",
  "email": "maria@cliente.com"
}
```

| Campo         | Tipo    | Obrigatório | Descrição                             |
|---------------|---------|-------------|---------------------------------------|
| `name`        | string  | ✅ Sim      | Nome do contato                       |
| `phone`       | string  | ❌ Não      | Telefone (somente números)            |
| `email`       | string  | ❌ Não      | E-mail do contato                     |
| `about`       | string  | ❌ Não      | Descrição/Observação                  |
| `photo`       | string  | ❌ Não      | URL da foto                           |
| `instagram`   | string  | ❌ Não      | User do Instagram                     |
| `facebook`    | string  | ❌ Não      | User do Facebook                      |
| `company_name`| string  | ❌ Não      | Nome da empresa do cliente            |
| `job`         | string  | ❌ Não      | Cargo do cliente                      |
| `area`        | string  | ❌ Não      | Área de atuação                       |
| `site`        | string  | ❌ Não      | Site                                  |
| `cnpj`        | string  | ❌ Não      | CNPJ/CPF                              |
| `zip`         | string  | ❌ Não      | CEP                                   |
| `category_id` | integer | ❌ Não      | ID da categoria                       |

**Resposta de sucesso (HTTP 201):**

```json
{
  "success": true,
  "message": "Contato criado com sucesso.",
  "data": {
    "id": 55,
    "name": "Maria Souza",
    "phone": "5511999999999",
    "email": "maria@cliente.com",
    "cnpj": "12.345.678/0001-99",
    "active": 1,
    "companyId": 3
  }
}
```

---

### Atualizar Contato

Atualiza os dados de um contato existente. Retorna erro se o telefone atualizado já existir em outro contato da empresa.

**Endpoint:**
```
PUT /api/external/contacts/{id}
```

**Parâmetros de Rota:**

| Parâmetro | Tipo    | Descrição            |
|-----------|---------|----------------------|
| `id`      | integer | ID do contato        |

**Body (JSON):**

(Mesmos campos da criação, o campo `name` é obrigatório)

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "message": "Contato atualizado com sucesso.",
  "data": {
    "id": 55,
    "name": "Maria Souza Editada",
    "phone": "5511999999999",
    "email": "maria@cliente.com",
    "cnpj": "12.345.678/0001-99",
    "active": 1,
    "companyId": 3
  }
}
```

---

## Rotas de Departamentos

### Listar Departamentos

Retorna todos os departamentos ativos da empresa, incluindo subdepartamentos (`children`).

**Endpoint:**
```
GET /api/external/departments
```

**Parâmetros de Query (todos opcionais):**

| Parâmetro    | Tipo    | Descrição                                               |
|--------------|---------|---------------------------------------------------------|
| `with_users` | integer | `1` = inclui usuários do departamento (padrão: `0`)     |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "Suporte",
      "color": "#3498db",
      "message": "Bem-vindo ao suporte!",
      "route": null,
      "is_default": true,
      "active": true,
      "companyId": 3,
      "parent_id": null,
      "children": [
        {
          "id": 5,
          "name": "Suporte N2",
          "color": "#2ecc71",
          "active": true,
          "parent_id": 1
        }
      ],
      "users": [
        {
          "id": 12,
          "name": "João Silva",
          "photo": "uploads/users/joao.jpg",
          "role": "operador",
          "active": true,
          "chat_status": "online"
        }
      ]
    }
  ]
}
```

> `users` só aparece quando `?with_users=1`.

**Campos do objeto `Department`:**

| Campo        | Tipo    | Descrição                                   |
|--------------|---------|---------------------------------------------|
| `id`         | integer | Identificador único                         |
| `name`       | string  | Nome do departamento                        |
| `color`      | string  | Cor em hexadecimal (ex.: `#3498db`)         |
| `message`    | string  | Mensagem de entrada do atendimento          |
| `route`      | string  | Tipo de roteamento (`assigned`, etc.)       |
| `is_default` | boolean | Se é o departamento padrão                  |
| `active`     | boolean | Se está ativo                               |
| `parent_id`  | integer | ID do departamento pai (null = raiz)        |
| `children`   | array   | Subdepartamentos                            |
| `users`      | array   | Usuários vinculados (com `?with_users=1`)   |

---

### Detalhar Departamento

Retorna um único departamento com seus usuários e subdepartamentos.

**Endpoint:**
```
GET /api/external/departments/{id}
```

**Parâmetros de Rota:**

| Parâmetro | Tipo    | Descrição            |
|-----------|---------|----------------------|
| `id`      | integer | ID do departamento   |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": {
    "id": 1,
    "name": "Suporte",
    "color": "#3498db",
    "active": true,
    "children": [ ... ],
    "users": [ ... ]
  }
}
```

---

## Motivos de Finalização

### Listar Motivos de Finalização

Retorna todos os motivos de finalização ativos da empresa. Necessário para utilizar a rota de [Finalizar Ticket](#finalizar-ticket).

**Endpoint:**
```
GET /api/external/finish-motives
```

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": [
    { "id": 1, "name": "Dúvida resolvida" },
    { "id": 2, "name": "Problema resolvido" },
    { "id": 3, "name": "Cliente desistiu" }
  ]
}
```

---

## Rotas de Tickets

### Listar Tickets

Retorna a lista paginada de tickets da empresa com filtros opcionais.

**Endpoint:**
```
GET /api/external/tickets
```

**Parâmetros de Query (todos opcionais):**

| Parâmetro       | Tipo    | Descrição                                                                      |
|-----------------|---------|--------------------------------------------------------------------------------|
| `status`        | string  | Filtra por status: `active`, `pending`, `closed`, `bot`, `rating`, `ai`        |
| `user_id`       | integer | ID do usuário responsável pelo ticket                                          |
| `department_id` | integer | ID do departamento                                                             |
| `date_from`     | string  | Data inicial de criação no formato `YYYY-MM-DD` (ex.: `2026-01-01`)            |
| `date_to`       | string  | Data final de criação no formato `YYYY-MM-DD` (ex.: `2026-03-24`)              |
| `per_page`      | integer | Itens por página — máx. 200 (padrão: `50`)                                     |
| `page`          | integer | Número da página (padrão: `1`)                                                 |

**Exemplo de requisição com filtros:**
```
GET /api/external/tickets?status=closed&department_id=1&date_from=2026-01-01&date_to=2026-03-24&per_page=100
```

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": {
    "current_page": 1,
    "data": [
      {
        "id": 987,
        "protocol": "ATD-20260301-987",
        "status": "closed",
        "lastMessage": "Obrigado pelo atendimento!",
        "unreadMessages": 0,
        "createdAt": "2026-03-01T10:00:00.000000Z",
        "updatedAt": "2026-03-01T11:30:00.000000Z",
        "finishedAt": "2026-03-01T11:30:00.000000Z",
        "attendedAt": "2026-03-01T10:05:00.000000Z",
        "userId": 12,
        "contactId": 55,
        "departmentId": 1,
        "companyId": 3,
        "motive": null,
        "tag": null,
        "user": {
          "id": 12,
          "name": "João Silva",
          "photo": "uploads/users/joao.jpg",
          "email": "joao@empresa.com",
          "role": "operador"
        },
        "contact": {
          "id": 55,
          "name": "Maria Souza",
          "phone": "5511999999999",
          "email": "maria@cliente.com"
        },
        "department": {
          "id": 1,
          "name": "Suporte",
          "color": "#3498db"
        }
      }
    ],
    "total": 180,
    "per_page": 50,
    "current_page": 1,
    "last_page": 4
  }
}
```

**Campos do objeto `Ticket`:**

| Campo            | Tipo    | Descrição                                                |
|------------------|---------|----------------------------------------------------------|
| `id`             | integer | Identificador único do ticket                            |
| `protocol`       | string  | Número de protocolo do atendimento                       |
| `status`         | string  | Status atual (veja [Tabela de Status](#tabelas-de-referência)) |
| `lastMessage`    | string  | Preview da última mensagem                               |
| `unreadMessages` | integer | Quantidade de mensagens não lidas                        |
| `createdAt`      | string  | Data/hora de criação (ISO 8601)                          |
| `updatedAt`      | string  | Data/hora da última atualização (ISO 8601)               |
| `finishedAt`     | string  | Data/hora de encerramento (null se não encerrado)        |
| `attendedAt`     | string  | Data/hora em que o atendimento foi iniciado              |
| `userId`         | integer | ID do usuário responsável (null se não atribuído)        |
| `contactId`      | integer | ID do contato                                            |
| `departmentId`   | integer | ID do departamento (null se não atribuído)               |
| `companyId`      | integer | ID da empresa                                            |
| `user`           | object  | Dados do usuário responsável                             |
| `contact`        | object  | Dados do contato                                         |
| `department`     | object  | Dados do departamento                                    |

---

### Detalhar Ticket

Retorna os detalhes completos de um único ticket.

**Endpoint:**
```
GET /api/external/tickets/{id}
```

**Parâmetros de Rota:**

| Parâmetro | Tipo    | Descrição       |
|-----------|---------|-----------------|
| `id`      | integer | ID do ticket    |

**Resposta de sucesso (HTTP 200):**

```json
{
  "success": true,
  "data": {
    "id": 987,
    "protocol": "ATD-20260301-987",
    "status": "closed",
    "user": { ... },
    "contact": { ... },
    "department": { ... },
    "finish_motive": {
      "id": 2,
      "name": "Problema resolvido"
    },
    "chat_tag": {
      "id": 1,
      "name": "Urgente",
      "color": "#e74c3c"
    }
  }
}
```

---

## Tabelas de Referência

### Status de Tickets

| Valor     | Descrição                                              |
|-----------|--------------------------------------------------------|
| `active`  | Ticket em atendimento humano ativo                     |
| `pending` | Aguardando resposta (fila)                             |
| `closed`  | Encerrado                                              |
| `bot`     | Em atendimento pelo bot                                |
| `rating`  | Aguardando avaliação do cliente                        |
| `ai`      | Em atendimento pelo agente de IA                       |

### Perfis de Usuário (`role`)

| Valor        | Descrição                                       |
|--------------|-------------------------------------------------|
| `admin`      | Administrador — acesso total                    |
| `supervisor` | Supervisor — acesso a relatórios e configurações|
| `operador`   | Operador — acesso apenas ao atendimento         |

### Status de Chat do Usuário (`chat_status`)

| Valor     | Descrição                     |
|-----------|-------------------------------|
| `online`  | Usuário disponível para chat  |
| `offline` | Usuário indisponível          |

---

## Exemplos com cURL

### Listar motivos de finalização

```bash
curl -X GET "https://api.oncenterchat.com/api/external/finish-motives" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000"
```

### Finalizar ticket com motivo

```bash
curl -X POST "https://api.oncenterchat.com/api/external/tickets/987/close" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"motive_id": 2, "motive_description": "Resolvido pelo time externo"}'
```

### Transferir ticket para um departamento (sem usuário)

```bash
curl -X POST "https://api.oncenterchat.com/api/external/tickets/987/transfer" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"department_id": 2}'
```

### Transferir ticket para usuário específico dentro do departamento

```bash
curl -X POST "https://api.oncenterchat.com/api/external/tickets/987/transfer" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"department_id": 2, "user_id": 15, "note": "Requer atenção especializada"}'
```

### Criar Contato

```bash
curl -X POST "https://api.oncenterchat.com/api/external/contacts" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"name": "Novo Contato", "phone": "5511999999999", "email": "novo@cliente.com"}'
```

### Listar todos os usuários ativos

```bash
curl -X GET "https://api.oncenterchat.com/api/external/users" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json"
```

### Buscar usuário por nome

```bash
curl -X GET "https://api.oncenterchat.com/api/external/users?name=João" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000"
```

### Colocar usuário online

```bash
curl -X PATCH "https://api.oncenterchat.com/api/external/users/12/status" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"status": "online"}'
```

### Colocar usuário offline

```bash
curl -X PATCH "https://api.oncenterchat.com/api/external/users/12/status" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"status": "offline"}'
```

### Listar departamentos com usuários

```bash
curl -X GET "https://api.oncenterchat.com/api/external/departments?with_users=1" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000"
```

### Listar tickets fechados do mês de março/2026

```bash
curl -X GET "https://api.oncenterchat.com/api/external/tickets?status=closed&date_from=2026-03-01&date_to=2026-03-31" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000"
```

### Listar tickets de um usuário específico

```bash
curl -X GET "https://api.oncenterchat.com/api/external/tickets?user_id=12" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000"
```

### Listar tickets de um departamento com paginação

```bash
curl -X GET "https://api.oncenterchat.com/api/external/tickets?department_id=1&per_page=100&page=2" \
  -H "Authorization: 550e8400-e29b-41d4-a716-446655440000"
```

---

## Códigos de Erro

| Código HTTP | Situação                                                         |
|-------------|------------------------------------------------------------------|
| `200`       | Sucesso                                                          |
| `401`       | UUID ausente ou inválido                                         |
| `404`       | Recurso não encontrado (usuário, departamento ou ticket)         |
| `422`       | Parâmetros de validação inválidos (ex.: status inexistente)      |
| `500`       | Erro interno do servidor                                         |

---

> Para suporte técnico, entre em contato com a equipe Oncenter.

## Atualizações de Tickets em Tempo Real (WebSocket)

Além das rotas REST, integrações externas podem receber atualizações de tickets
em tempo real por Socket.IO.

O endpoint de conexão é wss://ws.oncenterchat.com. Após conectar, entre no
canal ticket-{companyId} e escute o evento update.

### Conectar com Socket.IO

Use o pacote socket.io-client. O companyId é o ID numérico da empresa.

~~~js
import { io } from 'socket.io-client';

const companyId = COMPANY_ID;

const socket = io('wss://ws.oncenterchat.com', {
  transports: ['websocket'],
  reconnection: true,
});

socket.on('connect', () => {
  socket.emit('join', 'ticket-' + companyId);
});

socket.on('update', (rawEvent) => {
  const event = typeof rawEvent === 'string' ? JSON.parse(rawEvent) : rawEvent;

  console.log('Atualização recebida:', event.action, event.ticket);
});

socket.on('connect_error', (error) => {
  console.error('Falha no WebSocket:', error);
});
~~~

O evento é emitido em atualizações de ticket já publicadas pela plataforma,
como mensagens, atribuições, transferências e finalizações.

O exemplo abaixo usa dados fictícios e mostra os campos públicos mais úteis
para atualizar uma lista ou detalhe de tickets.

~~~json
{
  "action": "notify",
  "ticket": {
    "id": 502,
    "protocol": "PRO-2026/502",
    "userId": 4,
    "contactId": 30513,
    "departmentId": null,
    "companyId": 1,
    "status": "active",
    "lastMessage": "Mensagem mais recente",
    "unreadMessages": 1,
    "createdAt": "2026-06-30T02:50:09.000000Z",
    "updatedAt": "2026-07-16T03:31:08.000000Z",
    "finishedAt": null,
    "motive": null,
    "motive_description": null,
    "tag": null,
    "attendedAt": "2026-06-30T02:50:49.000000Z",
    "contact": {
      "id": 30513,
      "name": "Nome do contato",
      "photo": null,
      "phone": "5511999999999",
      "email": null,
      "companyId": 1
    },
    "user": {
      "id": 4,
      "name": "Nome do atendente",
      "fullPhoto": "https://...",
      "statusWithName": "🟢 Nome do atendente"
    },
    "department": null,
    "chat_tag": null,
    "last_msg": {
      "id": "id-da-mensagem",
      "ticketId": 502,
      "msgType": "chat",
      "body": "Mensagem mais recente",
      "createdAt": "2026-07-16T03:30:46.000000Z",
      "ack": 0,
      "fromMe": false,
      "payloadJson": null
    },
    "shared_users": []
  }
}
~~~

Os objetos department e chat_tag podem ser null. Quando o ticket estiver
compartilhado, shared_users contém os usuários vinculados.

O WebSocket não é uma fila de eventos. Para tolerar uma queda de conexão,
reconecte e sincronize os tickets alterados pela API REST, usando updatedAt
como referência. O cliente Socket.IO tenta reconectar automaticamente.
